Skip to main content
Cloud DevOps Solutions
Get a quote Talk to an engineer
CI/CD 2025-10-08 12 min read

How to Migrate from Jenkins to GitHub Actions (Step-by-Step)

Saddique Khan

DevOps / SRE Engineer & CEO, Cloud DevOps Solutions

Jenkins has served teams well for 15 years, but its operational overhead, managing controllers, agents, plugins, and upgrades, is a constant drain. GitHub Actions eliminates the controller entirely and runs where your code already lives. Here is how to migrate with minimal disruption.

Concept mapping

JenkinsGitHub ActionsNotes
Jenkinsfile.github/workflows/*.ymlOne workflow per concern
PipelineWorkflowTriggered by events
StageJobCan run in parallel
StepStepShell command or Action
AgentRunnerGitHub-hosted or self-hosted
CredentialsSecretsOrg/repo/environment scoped
Shared LibraryReusable Workflow / ActionMore composable
ParameterInput / workflow_dispatchManual trigger inputs
Stash/UnstashArtifactsupload-artifact / download-artifact
when { branch 'main' }if: github.ref == 'refs/heads/main'Conditional jobs
Blue OceanGitHub Actions UIBuilt into GitHub

Simple Jenkinsfile conversion

Before (Jenkins):

pipeline {
  agent any
  stages {
    stage('Install') {
      steps { sh 'npm ci' }
    }
    stage('Test') {
      steps { sh 'npm test' }
      post { always { junit 'test-results/*.xml' } }
    }
    stage('Build') {
      steps { sh 'npm run build' }
    }
    stage('Deploy') {
      when { branch 'main' }
      steps { sh './deploy.sh' }
    }
  }
}

After (GitHub Actions):

name: CI
on:
  push:
    branches: [main]
  pull_request:

jobs:
  install:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, cache: npm }
      - run: npm ci

  test:
    runs-on: ubuntu-latest
    needs: install
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, cache: npm }
      - run: npm ci
      - run: npm test
      - uses: actions/upload-artifact@v4
        if: always()
        with:
          name: test-results
          path: test-results/*.xml

  build:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20, cache: npm }
      - run: npm ci && npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/

  deploy:
    runs-on: ubuntu-latest
    needs: build
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - run: ./deploy.sh

Handling parallel stages

Jenkins parallel:

stage('Test') {
  parallel {
    stage('Unit')        { steps { sh 'npm run test:unit' } }
    stage('Integration') { steps { sh 'npm run test:integration' } }
    stage('E2E')         { steps { sh 'npm run test:e2e' } }
  }
}

GitHub Actions (jobs run in parallel by default when they share the same needs):

test-unit:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - run: npm ci && npm run test:unit

test-integration:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - run: npm ci && npm run test:integration

test-e2e:
  runs-on: ubuntu-latest
  steps:
    - uses: actions/checkout@v4
    - run: npm ci && npm run test:e2e

deploy:
  needs: [test-unit, test-integration, test-e2e]  # waits for all three
  runs-on: ubuntu-latest
  steps:
    - run: ./deploy.sh

Migrating Jenkins credentials

Jenkins stores credentials in its own encrypted store. Map them to GitHub secrets:

Jenkins credential typeGitHub equivalent
Username/passwordTwo separate secrets (_USER and _PASS)
Secret textRepository or org secret
SSH private keySecret containing the key content
CertificateSecret containing PEM content
# Export from Jenkins (requires Jenkins CLI)
java -jar jenkins-cli.jar -s http://jenkins:8080 \
  -auth admin:token \
  list-credentials-as-xml system::system::jenkins _

# Add to GitHub via CLI
gh secret set AWS_ACCESS_KEY_ID --body "AKIA..."
gh secret set AWS_SECRET_ACCESS_KEY --body "..."
gh secret set KUBECONFIG --body "$(cat ~/.kube/config | base64)"

For environment-specific secrets (staging vs production), use GitHub Environments:

deploy-production:
  environment: production  # environment secrets override repo secrets
  runs-on: ubuntu-latest
  steps:
    - run: echo "Using production DB_HOST=${{ secrets.DB_HOST }}"

Self-hosted runners

If your Jenkins agents have special requirements (private network access, GPU, specific tools), replace them with self-hosted GitHub Actions runners.

# On the machine that used to be a Jenkins agent
mkdir actions-runner && cd actions-runner

# Download runner (get latest URL from GitHub Settings > Actions > Runners)
curl -O -L https://github.com/actions/runner/releases/download/v2.319.1/actions-runner-linux-x64-2.319.1.tar.gz
tar xzf ./actions-runner-linux-x64-2.319.1.tar.gz

# Configure
./config.sh \
  --url https://github.com/your-org \
  --token YOUR_REGISTRATION_TOKEN \
  --labels self-hosted,linux,x64,private-network

# Install as service
sudo ./svc.sh install && sudo ./svc.sh start

Use runner labels to route jobs:

jobs:
  deploy:
    runs-on: [self-hosted, private-network]

Matrix builds

Jenkins matrix (requires Matrix plugin):

matrix {
  axes {
    axis { name 'NODE'; values '18', '20', '22' }
  }
  stages {
    stage('Test') { steps { sh "nvm use $NODE && npm test" } }
  }
}

GitHub Actions matrix:

test:
  strategy:
    matrix:
      node-version: [18, 20, 22]
    fail-fast: false
  runs-on: ubuntu-latest
  steps:
    - uses: actions/setup-node@v4
      with:
        node-version: ${{ matrix.node-version }}
    - run: npm ci && npm test

Jenkins Shared Libraries → Reusable Workflows

Jenkins shared libraries are Groovy; GitHub Actions equivalent is reusable workflows.

# .github/workflows/reusable-deploy.yml
on:
  workflow_call:
    inputs:
      environment:
        required: true
        type: string
    secrets:
      KUBECONFIG:
        required: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
      - uses: actions/checkout@v4
      - run: helm upgrade --install my-app ./helm/my-app -n ${{ inputs.environment }}

Calling it:

# In another workflow
jobs:
  deploy:
    uses: ./.github/workflows/reusable-deploy.yml
    with:
      environment: staging
    secrets:
      KUBECONFIG: ${{ secrets.KUBECONFIG }}

Common gotchas

  1. npm ci needs a package-lock.json, if you used npm install in Jenkins, package-lock.json may be .gitignored. Add it to git.
  2. GitHub Actions has a 6-hour job timeout, long-running Jenkins jobs need to be split or optimised.
  3. No persistent workspace, each job starts clean. Use actions/cache or actions/upload-artifact to share state.
  4. Secret masking differs, GitHub Actions masks secrets in logs, but multiline secrets need base64 encoding.
  5. Plugin parity, Jenkins has 1,800+ plugins; most have Action equivalents but check before assuming.

Migration checklist

  • Inventory all Jenkinsfiles and shared libraries
  • Map credentials to GitHub secrets (org vs repo vs environment)
  • Identify self-hosted agent requirements
  • Create workflow files in a branch; test against PRs before merging
  • Run old and new pipelines in parallel for 2 weeks
  • Decommission Jenkins after validating all jobs pass

Key takeaways

  • Jobs in GitHub Actions run in parallel by default; use needs to sequence them.
  • Replace Jenkins credentials with GitHub Secrets; use Environments for per-environment overrides.
  • Self-hosted runners replace Jenkins agents with minimal configuration.
  • Reusable Workflows replace Shared Libraries with better composability and type safety.
  • The biggest migration risk is uncovered credentials and hard-coded paths, audit both before cutting over.

Planning the move

If you are still deciding whether to migrate, see how we approach a Jenkins migration. For a staged plan with a parallel run and rollback, read the Jenkins to GitHub Actions migration plan.

#jenkins #github-actions #migration #cicd
Found this useful? Share it with your team or link to it from your documentation.

Need help implementing this?

Our certified engineers can handle the setup, migration, or ongoing operations for you.