Skip to content

Migrate from Jenkins

Jenkins estates grow organically: freestyle jobs configured in the UI, scripted and declarative pipelines in Groovy, shared libraries, and dozens of plugins. Slipway is declarative YAML with a deployment model on top. Plan to simplify while you migrate, not transliterate.

Jenkins Slipway
Controller Control plane (server, scheduler, worker)
Agent / node / label Runner in a pool; runs_on labels
Cloud agents (Kubernetes plugin pod templates) kubernetes executor: a pod per job
Freestyle job A pipeline with one job
Jenkinsfile (declarative) .slipway/*.yaml
pipeline { agent ... } runs_on, container
stages { stage(...) } stages: with jobs:
parallel { ... } Several jobs in a stage, or a matrix
steps { sh '...' } steps: - run: ...
when { branch 'main' } if: git.branch == "main"
environment { X = '...' } env:
credentials('id') ${{ secrets.NAME }} in env
post { always / failure } Steps or jobs with if: always() / if: failure()
options { timeout, retry } timeout, retry
triggers { cron, pollSCM } on: schedule; SCM webhooks replace polling
parameters { string ... } on.manual.inputs
stash / unstash artifacts between jobs
archiveArtifacts artifacts:
junit Test reports are parsed from JUnit files automatically
Shared library template: / extends: / registry steps
Multibranch pipeline Automatic: pipelines run per branch and PR
Input step Approval on an environment
Promoted builds / deploy jobs Release, lifecycle, deployment
Folders and credentials domains Projects and secret scopes
Role Strategy plugin RBAC roles and bindings
Blue Ocean The Slipway UI
Jenkins plugin purpose In Slipway
Git, GitHub, GitLab, Bitbucket SCM integrations
Docker, Kubernetes Container executors; registry integrations
Credentials, Vault Secrets with external references
Slack, email notifications Notification integrations
JIRA Jira integration
SonarQube, security scanners Scanner steps and ingestion
Pipeline utility steps Shell and CEL functions

Jenkinsfile:

pipeline {
agent { label 'linux && docker' }
options { timeout(time: 30, unit: 'MINUTES') }
environment { REGISTRY = 'ghcr.io/acme' }
stages {
stage('Test') { steps { sh 'make test' } }
stage('Build') {
when { branch 'main' }
steps {
withCredentials([string(credentialsId: 'registry-token', variable: 'TOKEN')]) {
sh 'echo $TOKEN | docker login ghcr.io -u ci --password-stdin && make image push'
}
}
}
stage('Deploy') {
when { branch 'main' }
steps { input 'Deploy to production?'; sh './deploy.sh production' }
}
}
post { failure { slackSend channel: '#ci', message: "Build failed: ${env.BUILD_URL}" } }
}

Slipway:

version: 1
name: api
on:
push: { branches: [main] }
pull_request:
defaults: { runs_on: [linux, docker], timeout: 30m }
env: { REGISTRY: ghcr.io/acme }
jobs:
- name: test
steps:
- uses: slipway/checkout@v1
- run: make test
- name: build
needs: [test]
if: git.branch == "main"
outputs: { version: "${{ steps.v.outputs.version }}" }
steps:
- uses: slipway/checkout@v1
- run: echo "$TOKEN" | docker login ghcr.io -u ci --password-stdin && make image push
env: { TOKEN: "${{ secrets.REGISTRY_TOKEN }}" }
- id: v
run: echo "version=$(git describe --tags --always)" >> "$SLIPWAY_OUTPUT"
- uses: slipway/release-create@v1
- name: deploy
needs: [build]
if: git.branch == "main"
environment: { name: production, service: api } # approval is a protection rule, not a pipeline step
steps:
- uses: slipway/deploy@v1
with: { release: "${{ needs.build.outputs.version }}", strategy: rolling }

Notifications move out of the pipeline: create a Slack subscription for run.finished (filtered to failures) instead of a post block in every Jenkinsfile.

  1. Inventory jobs by value. Many Jenkins jobs are stale. Migrate the ones that ran in the last quarter.
  2. Install runners where your Jenkins agents live (same network, same tooling). Reuse labels as runner labels.
  3. Move secrets out of Jenkins credentials into your secret manager and reference them, or federate with OIDC.
  4. Convert the build first, then model environments and deployments properly with approvals and strategies, replacing input steps and deploy scripts.
  5. Replace polling with webhooks. Connect the SCM integration; delete pollSCM.
  6. Retire the controller once nothing depends on it.
  • Groovy logic (loops, functions, dynamic stages) has no direct equivalent. Move it into scripts the steps call, or express it with matrix and if.
  • Jenkins workspaces persist on agents; Slipway job workspaces are fresh per job. Use cache: and artifacts: explicitly.
  • Shared global state (environment variables set in the UI, credentials domains) must become explicit project or org variables and secrets.