{"meta":{"title":"Migrating from CircleCI to GitHub Actions","intro":"GitHub Actions and CircleCI share several similarities in configuration, which makes migration 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-circleci","title":"Migrate from CircleCI"}],"documentType":"article"},"body":"# Migrating from CircleCI to GitHub Actions\n\nGitHub Actions and CircleCI share several similarities in configuration, which makes migration to GitHub Actions relatively straightforward.\n\n## Introduction\n\nCircleCI and GitHub Actions both allow you to create workflows that automatically build, test, publish, release, and deploy code. CircleCI and GitHub Actions share some similarities in workflow configuration:\n\n* Workflow configuration files are written in YAML and stored in the repository.\n* Workflows include one or more jobs.\n* Jobs include one or more steps or individual commands.\n* Steps or tasks can be reused and shared with the community.\n\nFor more information, see [Understanding GitHub Actions](/en/actions/get-started/understand-github-actions).\n\n## Key differences\n\nWhen migrating from CircleCI, consider the following differences:\n\n* CircleCI’s automatic test parallelism automatically groups tests according to user-specified rules or historical timing information. This functionality is not built into GitHub Actions.\n* Actions that execute in Docker containers are sensitive to permissions problems since containers have a different mapping of users. You can avoid many of these problems by not using the `USER` instruction in your *Dockerfile*. For more information about the Docker filesystem on GitHub-hosted runners, see [GitHub-hosted runners reference](/en/actions/reference/runners/github-hosted-runners#docker-container-filesystem).\n\n## Migrating workflows and jobs\n\nCircleCI defines `workflows` in the *config.yml* file, which allows you to configure more than one workflow. GitHub requires one workflow file per workflow, and as a consequence, does not require you to declare `workflows`. You'll need to create a new workflow file for each workflow configured in *config.yml*.\n\nBoth CircleCI and GitHub Actions configure `jobs` in the configuration file using similar syntax. If you configure any dependencies between jobs using `requires` in your CircleCI workflow, you can use the equivalent GitHub Actions `needs` syntax. For more information, see [Workflow syntax for GitHub Actions](/en/actions/reference/workflows-and-actions/workflow-syntax#jobsjob_idneeds).\n\n## Migrating orbs to actions\n\nBoth CircleCI and GitHub Actions provide a mechanism to reuse and share tasks in a workflow. CircleCI uses a concept called orbs, written in YAML, to provide tasks that people can reuse in a workflow. GitHub Actions has powerful and flexible reusable components called actions, which you build with either JavaScript files or Docker images. You can create actions by writing custom code that interacts with your repository in any way you'd like, including integrating with GitHub's APIs and any publicly available third-party API. For example, an action can publish npm modules, send SMS alerts when urgent issues are created, or deploy production-ready code. For more information, see [Reusing automations](/en/actions/how-tos/reuse-automations).\n\nCircleCI can reuse pieces of workflows with YAML anchors and aliases. GitHub Actions supports YAML anchors and aliases for reusability, and also provides matrices for running jobs with different configurations. For more information about matrices, see [Running variations of jobs in a workflow](/en/actions/how-tos/write-workflows/choose-what-workflows-do/run-job-variations).\n\n## Using Docker images\n\nBoth CircleCI and GitHub Actions support running steps inside of a Docker image.\n\nCircleCI provides a set of pre-built images with common dependencies. These images have the `USER` set to `circleci`, which causes permissions to conflict with GitHub Actions.\n\nWe recommend that you move away from CircleCI's pre-built images when you migrate to GitHub Actions. In many cases, you can use actions to install the additional dependencies you need.\n\nFor more information about the Docker filesystem, see [GitHub-hosted runners reference](/en/actions/reference/runners/github-hosted-runners#docker-container-filesystem).\n\nFor more information about the tools and packages available on GitHub-hosted runner images, see [GitHub-hosted runners](/en/actions/concepts/runners/github-hosted-runners#preinstalled-software-for-github-owned-images).\n\n## Using variables and secrets\n\nCircleCI and GitHub Actions support setting variables in the configuration file and creating secrets using the CircleCI or GitHub UI.\n\nFor more information, see [Variables reference](/en/actions/reference/workflows-and-actions/variables#default-environment-variables) and [Using secrets in GitHub Actions](/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets).\n\n## Caching\n\nCircleCI and GitHub Actions provide a method to manually cache files in the configuration file.\n\nBelow is an example of the syntax for each system.\n\n### CircleCI syntax for caching\n\n```yaml\n- restore_cache:\n    keys:\n      - v1-npm-deps-{{ checksum \"package-lock.json\" }}\n      - v1-npm-deps-\n```\n\n### GitHub Actions syntax for caching\n\n```yaml\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\nGitHub Actions does not have an equivalent of CircleCI’s Docker Layer Caching (or DLC).\n\n## Persisting data between jobs\n\nBoth CircleCI and GitHub Actions provide mechanisms to persist data between jobs.\n\nBelow is an example in CircleCI and GitHub Actions configuration syntax.\n\n### CircleCI syntax for persisting data between jobs\n\n```yaml\n- persist_to_workspace:\n    root: workspace\n    paths:\n      - math-homework.txt\n\n...\n\n- attach_workspace:\n    at: /tmp/workspace\n```\n\n### GitHub Actions syntax for persisting data between jobs\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...\n\n- name: Download math result for job 1\n  uses: actions/download-artifact@v5\n  with:\n    name: homework\n```\n\nFor more information, see [Store and share data with workflow artifacts](/en/actions/tutorials/store-and-share-data).\n\n## Using databases and service containers\n\nBoth systems enable you to include additional containers for databases, caching, or other dependencies.\n\nIn CircleCI, the first image listed in the *config.yaml* is the primary image used to run commands. GitHub Actions uses explicit sections: use `container` for the primary container, and list additional containers in `services`.\n\nBelow is an example in CircleCI and GitHub Actions configuration syntax.\n\n### CircleCI syntax for using databases and service containers\n\n```yaml\n---\nversion: 2.1\n\njobs:\n\n  ruby-26:\n    docker:\n      - image: circleci/ruby:2.6.3-node-browsers-legacy\n        environment:\n          PGHOST: localhost\n          PGUSER: administrate\n          RAILS_ENV: test\n      - image: postgres:10.1-alpine\n        environment:\n          POSTGRES_USER: administrate\n          POSTGRES_DB: ruby26\n          POSTGRES_PASSWORD: \"\"\n\n    working_directory: ~/administrate\n\n    steps:\n      - checkout\n\n      # Bundle install dependencies\n      - run: bundle install --path vendor/bundle\n\n      # Wait for DB\n      - run: dockerize -wait tcp://localhost:5432 -timeout 1m\n\n      # Setup the environment\n      - run: cp .sample.env .env\n\n      # Setup the database\n      - run: bundle exec rake db:setup\n\n      # Run the tests\n      - run: bundle exec rake\n\nworkflows:\n  version: 2\n  build:\n    jobs:\n      - ruby-26\n...\n\n- attach_workspace:\n    at: /tmp/workspace\n```\n\n### GitHub Actions syntax for using databases and service containers\n\n<!-- markdownlint-disable search-replace -->\n\n```yaml\nname: Containers\n\non: [push]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n    container: circleci/ruby:2.6.3-node-browsers-legacy\n\n    env:\n      PGHOST: postgres\n      PGUSER: administrate\n      RAILS_ENV: test\n\n    services:\n      postgres:\n        image: postgres:10.1-alpine\n        env:\n          POSTGRES_USER: administrate\n          POSTGRES_DB: ruby25\n          POSTGRES_PASSWORD: \"\"\n        ports:\n          - 5432:5432\n        # Add a health check\n        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5\n\n    steps:\n      # This Docker file changes sets USER to circleci instead of using the default user, so we need to update file permissions for this image to work on GH Actions.\n      # See https://docs.github.com/actions/using-github-hosted-runners/about-github-hosted-runners#docker-container-filesystem\n\n      - name: Setup file system permissions\n        run: sudo chmod -R 777 $GITHUB_WORKSPACE /github /__w/_temp\n      - uses: actions/checkout@v6\n      - name: Install dependencies\n        run: bundle install --path vendor/bundle\n      - name: Setup environment configuration\n        run: cp .sample.env .env\n      - name: Setup database\n        run: bundle exec rake db:setup\n      - name: Run tests\n        run: bundle exec rake\n```\n\n<!-- markdownlint-enable search-replace -->\n\nFor more information, see [Communicating with Docker service containers](/en/actions/tutorials/use-containerized-services/use-docker-service-containers).\n\n## Complete Example\n\nBelow is a real-world example. The left shows the actual CircleCI *config.yml* for the [thoughtbot/administrator](https://github.com/thoughtbot/administrate) repository. The right shows the GitHub Actions equivalent.\n\n### Complete example for CircleCI\n\n```yaml\n---\nversion: 2.1\n\ncommands:\n  shared_steps:\n    steps:\n      - checkout\n\n      # Restore Cached Dependencies\n      - restore_cache:\n          name: Restore bundle cache\n          key: administrate-{{ checksum \"Gemfile.lock\" }}\n\n      # Bundle install dependencies\n      - run: bundle install --path vendor/bundle\n\n      # Cache Dependencies\n      - save_cache:\n          name: Store bundle cache\n          key: administrate-{{ checksum \"Gemfile.lock\" }}\n          paths:\n            - vendor/bundle\n\n      # Wait for DB\n      - run: dockerize -wait tcp://localhost:5432 -timeout 1m\n\n      # Setup the environment\n      - run: cp .sample.env .env\n\n      # Setup the database\n      - run: bundle exec rake db:setup\n\n      # Run the tests\n      - run: bundle exec rake\n\ndefault_job: &default_job\n  working_directory: ~/administrate\n  steps:\n    - shared_steps\n    # Run the tests against multiple versions of Rails\n    - run: bundle exec appraisal install\n    - run: bundle exec appraisal rake\n\njobs:\n  ruby-25:\n    <<: *default_job\n    docker:\n      - image: circleci/ruby:2.5.0-node-browsers\n        environment:\n          PGHOST: localhost\n          PGUSER: administrate\n          RAILS_ENV: test\n      - image: postgres:10.1-alpine\n        environment:\n          POSTGRES_USER: administrate\n          POSTGRES_DB: ruby25\n          POSTGRES_PASSWORD: \"\"\n\n  ruby-26:\n    <<: *default_job\n    docker:\n      - image: circleci/ruby:2.6.3-node-browsers-legacy\n        environment:\n          PGHOST: localhost\n          PGUSER: administrate\n          RAILS_ENV: test\n      - image: postgres:10.1-alpine\n        environment:\n          POSTGRES_USER: administrate\n          POSTGRES_DB: ruby26\n          POSTGRES_PASSWORD: \"\"\n\nworkflows:\n  version: 2\n  multiple-rubies:\n    jobs:\n      - ruby-26\n      - ruby-25\n```\n\n### Complete example for GitHub Actions\n\n```yaml\n# This workflow uses actions that are not certified by GitHub.\n# They are provided by a third-party and are governed by\n# separate terms of service, privacy policy, and support\n# documentation.\n\n# GitHub recommends pinning actions to a commit SHA.\n# To get a newer version, you will need to update the SHA.\n# You can also reference a tag or branch, but the action may change without warning.\n\nname: Containers\n\non: [push]\n\njobs:\n  build:\n\n    strategy:\n      matrix:\n        ruby: ['2.5', '2.6.3']\n\n    runs-on: ubuntu-latest\n\n    env:\n      PGHOST: localhost\n      PGUSER: administrate\n      RAILS_ENV: test\n\n    services:\n      postgres:\n        image: postgres:10.1-alpine\n        env:\n          POSTGRES_USER: administrate\n          POSTGRES_DB: ruby25\n          POSTGRES_PASSWORD: \"\"\n        ports:\n          - 5432:5432\n        # Add a health check\n        options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5\n\n    steps:\n      - uses: actions/checkout@v6\n      - name: Setup Ruby\n        uses: eregon/use-ruby-action@ec02537da5712d66d4d50a0f33b7eb52773b5ed1\n        with:\n          ruby-version: ${{ matrix.ruby }}\n      - name: Cache dependencies\n        uses: actions/cache@v4\n        with:\n          path: vendor/bundle\n          key: administrate-${{ matrix.image }}-${{ hashFiles('Gemfile.lock') }}\n      - name: Install postgres headers\n        run: |\n          sudo apt-get update\n          sudo apt-get install libpq-dev\n      - name: Install dependencies\n        run: bundle install --path vendor/bundle\n      - name: Setup environment configuration\n        run: cp .sample.env .env\n      - name: Setup database\n        run: bundle exec rake db:setup\n      - name: Run tests\n        run: bundle exec rake\n      - name: Install appraisal\n        run: bundle exec appraisal install\n      - name: Run appraisal\n        run: bundle exec appraisal rake\n```"}