{"meta":{"title":"Migrating from GitLab CI/CD to GitHub Actions","intro":"GitHub Actions and GitLab CI/CD share several configuration similarities, which makes migrating to GitHub Actions relatively straightforward.","product":"GitHub Actions","breadcrumbs":[{"href":"/en/actions","title":"GitHub Actions"},{"href":"/en/actions/tutorials","title":"Tutorials"},{"href":"/en/actions/tutorials/migrate-to-github-actions","title":"Migrate to GitHub Actions"},{"href":"/en/actions/tutorials/migrate-to-github-actions/manual-migrations","title":"Manual migrations"},{"href":"/en/actions/tutorials/migrate-to-github-actions/manual-migrations/migrate-from-gitlab-cicd","title":"Migrate from GitLab CI/CD"}],"documentType":"article"},"body":"# Migrating from GitLab CI/CD to GitHub Actions\n\nGitHub Actions and GitLab CI/CD share several configuration similarities, which makes migrating to GitHub Actions relatively straightforward.\n\n## Introduction\n\nGitLab CI/CD and GitHub Actions both allow you to create workflows that automatically build, test, publish, release, and deploy code. GitLab CI/CD and GitHub Actions share some similarities in workflow configuration:\n\n* Workflow configuration files are written in YAML and are stored in the code's repository.\n* Workflows include one or more jobs.\n* Jobs include one or more steps or individual commands.\n* Jobs can run on either managed or self-hosted machines.\n\nThere are a few differences, and this guide will show you the important differences so that you can migrate your workflow to GitHub Actions.\n\n## Jobs\n\nJobs in GitLab CI/CD are very similar to jobs in GitHub Actions. In both systems, jobs have the following characteristics:\n\n* Jobs contain a series of steps or scripts that run sequentially.\n* Jobs can run on separate machines or in separate containers.\n* Jobs run in parallel by default, but can be configured to run sequentially.\n\nYou can run a script or a shell command in a job. In GitLab CI/CD, script steps are specified using the `script` key. In GitHub Actions, all scripts are specified using the `run` key.\n\nBelow is an example of the syntax for each system.\n\n### GitLab CI/CD syntax for jobs\n\n```yaml\njob1:\n  variables:\n    GIT_CHECKOUT: \"true\"\n  script:\n    - echo \"Run your script here\"\n```\n\n### GitHub Actions syntax for jobs\n\n```yaml\njobs:\n  job1:\n    steps:\n      - uses: actions/checkout@v6\n      - run: echo \"Run your script here\"\n```\n\n## Runners\n\nRunners are machines on which the jobs run. Both GitLab CI/CD and GitHub Actions offer managed and self-hosted variants of runners. In GitLab CI/CD, `tags` are used to run jobs on different platforms, while in GitHub Actions it is done with the `runs-on` key.\n\nBelow is an example of the syntax for each system.\n\n### GitLab CI/CD syntax for runners\n\n```yaml\nwindows_job:\n  tags:\n    - windows\n  script:\n    - echo Hello, %USERNAME%!\n\nlinux_job:\n  tags:\n    - linux\n  script:\n    - echo \"Hello, $USER!\"\n```\n\n### GitHub Actions syntax for runners\n\n```yaml\nwindows_job:\n  runs-on: windows-latest\n  steps:\n    - run: echo Hello, %USERNAME%!\n\nlinux_job:\n  runs-on: ubuntu-latest\n  steps:\n    - run: echo \"Hello, $USER!\"\n```\n\nFor more information, see [Workflow syntax for GitHub Actions](/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on).\n\n## Docker images\n\nBoth GitLab CI/CD and GitHub Actions support running jobs in a Docker image. In GitLab CI/CD, Docker images are defined with an `image` key, while in GitHub Actions it is done with the `container` key.\n\nBelow is an example of the syntax for each system.\n\n### GitLab CI/CD syntax for Docker images\n\n```yaml\nmy_job:\n  image: node:20-bookworm-slim\n```\n\n### GitHub Actions syntax for Docker images\n\n```yaml\njobs:\n  my_job:\n    container: node:20-bookworm-slim\n```\n\nFor more information, see [Workflow syntax for GitHub Actions](/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idcontainer).\n\n## Condition and expression syntax\n\nGitLab CI/CD uses `rules` to determine if a job will run for a specific condition. GitHub Actions uses the `if` keyword to prevent a job from running unless a condition is met.\n\nBelow is an example of the syntax for each system.\n\n### GitLab CI/CD syntax for conditions and expressions\n\n```yaml\ndeploy_prod:\n  stage: deploy\n  script:\n    - echo \"Deploy to production server\"\n  rules:\n    - if: '$CI_COMMIT_BRANCH == \"master\"'\n```\n\n### GitHub Actions syntax for conditions and expressions\n\n```yaml\njobs:\n  deploy_prod:\n    if: contains( github.ref, 'master')\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo \"Deploy to production server\"\n```\n\nFor more information, see [Evaluate expressions in workflows and actions](/en/actions/learn-github-actions/expressions).\n\n## Dependencies between Jobs\n\nBoth GitLab CI/CD and GitHub Actions allow you to set dependencies for a job. In both systems, jobs run in parallel by default, but job dependencies in GitHub Actions can be specified explicitly with the `needs` key. GitLab CI/CD also has a concept of `stages`, where jobs in a stage run concurrently, but the next stage will start when all the jobs in the previous stage have completed. You can recreate this scenario in GitHub Actions with the `needs` key.\n\nBelow is an example of the syntax for each system. The workflows start with two jobs named `build_a` and `build_b` running in parallel, and when those jobs complete, another job called `test_ab` will run. Finally, when `test_ab` completes, the `deploy_ab` job will run.\n\n### GitLab CI/CD syntax for dependencies between jobs\n\n```yaml\nstages:\n  - build\n  - test\n  - deploy\n\nbuild_a:\n  stage: build\n  script:\n    - echo \"This job will run first.\"\n\nbuild_b:\n  stage: build\n  script:\n    - echo \"This job will run first, in parallel with build_a.\"\n\ntest_ab:\n  stage: test\n  script:\n    - echo \"This job will run after build_a and build_b have finished.\"\n\ndeploy_ab:\n  stage: deploy\n  script:\n    - echo \"This job will run after test_ab is complete\"\n```\n\n### GitHub Actions syntax for dependencies between jobs\n\n```yaml\njobs:\n  build_a:\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo \"This job will be run first.\"\n\n  build_b:\n    runs-on: ubuntu-latest\n    steps:\n      - run: echo \"This job will be run first, in parallel with build_a\"\n\n  test_ab:\n    runs-on: ubuntu-latest\n    needs: [build_a,build_b]\n    steps:\n      - run: echo \"This job will run after build_a and build_b have finished\"\n\n  deploy_ab:\n    runs-on: ubuntu-latest\n    needs: [test_ab]\n    steps:\n      - run: echo \"This job will run after test_ab is complete\"\n```\n\nFor more information, see [Workflow syntax for GitHub Actions](/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idneeds).\n\n## Scheduling workflows\n\nBoth GitLab CI/CD and GitHub Actions allow you to run workflows at a specific interval. In GitLab CI/CD, pipeline schedules are configured with the UI, while in GitHub Actions you can trigger a workflow on a scheduled interval with the \"on\" key.\n\nFor more information, see [Events that trigger workflows](/en/actions/using-workflows/events-that-trigger-workflows#scheduled-events).\n\n## Variables and secrets\n\nGitLab CI/CD and GitHub Actions support setting variables in the pipeline or workflow configuration file, and creating secrets using the GitLab or GitHub UI.\n\nFor more information, see [Store information in variables](/en/actions/learn-github-actions/variables) and [Secrets](/en/actions/security-for-github-actions/security-guides/about-secrets).\n\n## Caching\n\nGitLab CI/CD and GitHub Actions provide a method in the configuration file to manually cache workflow files.\n\nBelow is an example of the syntax for each system.\n\n### GitLab CI/CD syntax for caching\n\n```yaml\nimage: node:latest\n\ncache:\n  key: $CI_COMMIT_REF_SLUG\n  paths:\n    - .npm/\n\nbefore_script:\n  - npm ci --cache .npm --prefer-offline\n\ntest_async:\n  script:\n    - node ./specs/start.js ./specs/async.spec.js\n```\n\n### GitHub Actions syntax for caching\n\n```yaml\njobs:\n  test_async:\n    runs-on: ubuntu-latest\n    steps:\n    - name: Cache node modules\n      uses: actions/cache@v4\n      with:\n        path: ~/.npm\n        key: v1-npm-deps-${{ hashFiles('**/package-lock.json') }}\n        restore-keys: v1-npm-deps-\n```\n\n## Artifacts\n\nBoth GitLab CI/CD and GitHub Actions can upload files and directories created by a job as artifacts. In GitHub Actions, artifacts can be used to persist data across multiple jobs.\n\nBelow is an example of the syntax for each system.\n\n### GitLab CI/CD syntax for artifacts\n\n```yaml\nscript:\nartifacts:\n  paths:\n    - math-homework.txt\n```\n\n### GitHub Actions syntax for artifacts\n\n```yaml\n- name: Upload math result for job 1\n  uses: actions/upload-artifact@v4\n  with:\n    name: homework\n    path: math-homework.txt\n```\n\nFor more information, see [Store and share data with workflow artifacts](/en/actions/using-workflows/storing-workflow-data-as-artifacts).\n\n## Databases and service containers\n\nBoth systems enable you to include additional containers for databases, caching, or other dependencies.\n\nIn GitLab CI/CD, a container for the job is specified with the `image` key, while GitHub Actions uses the `container` key. In both systems, additional service containers are specified with the `services` key.\n\nBelow is an example of the syntax for each system.\n\n### GitLab CI/CD syntax for databases and service containers\n\n```yaml\ncontainer-job:\n  variables:\n    POSTGRES_PASSWORD: postgres\n    # The hostname used to communicate with the\n    # PostgreSQL service container\n    POSTGRES_HOST: postgres\n    # The default PostgreSQL port\n    POSTGRES_PORT: 5432\n  image: node:20-bookworm-slim\n  services:\n    - postgres\n  script:\n    # Performs a clean installation of all dependencies\n    # in the `package.json` file\n    - npm ci\n    # Runs a script that creates a PostgreSQL client,\n    # populates the client with data, and retrieves data\n    - node client.js\n  tags:\n    - docker\n```\n\n### GitHub Actions syntax for databases and service containers\n\n```yaml\njobs:\n  container-job:\n    runs-on: ubuntu-latest\n    container: node:20-bookworm-slim\n\n    services:\n      postgres:\n        image: postgres\n        env:\n          POSTGRES_PASSWORD: postgres\n\n    steps:\n      - name: Check out repository code\n        uses: actions/checkout@v6\n\n      # Performs a clean installation of all dependencies\n      # in the `package.json` file\n      - name: Install dependencies\n        run: npm ci\n\n      - name: Connect to PostgreSQL\n        # Runs a script that creates a PostgreSQL client,\n        # populates the client with data, and retrieves data\n        run: node client.js\n        env:\n          # The hostname used to communicate with the\n          # PostgreSQL service container\n          POSTGRES_HOST: postgres\n          # The default PostgreSQL port\n          POSTGRES_PORT: 5432\n```\n\nFor more information, see [Communicating with Docker service containers](/en/actions/using-containerized-services/about-service-containers)."}