CircleCI → GitHub Actions
Move CircleCI workflows, jobs, orbs, contexts, caches, and service containers into GitHub Actions. Split one config.yml into one workflow file per workflow. Map requires to needs. Rebuild test parallelism, Docker Layer Caching, and contexts yourself.
Should you make this move?
Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.
CircleCI
- YAML pipelines, orbs, contexts, and SSH debugging make hosted CI approachable for growing engineering teams
- Repository-centered workflows keep code, review, and automation close together
- Usage credits, contexts, and orb-specific behavior create lock-in that does not map one-to-one onto other runners
- Advanced governance and automation can become configuration-heavy
GitHub Actions
- Automation lives beside the repository with an enormous reusable action ecosystem
- Repository-centered workflows keep code, review, and automation close together
- Large workflows can become slow, opaque, and expensive to debug
- Advanced governance and automation can become configuration-heavy
GitHub Actions: Automation lives beside the repository with an enormous reusable action ecosystem. This removes a major source-side concern: Usage credits, contexts, and orb-specific behavior create lock-in that does not map one-to-one onto other runners.
What you lose: YAML pipelines, orbs, contexts, and SSH debugging make hosted CI approachable for growing engineering teams. What you inherit: Large workflows can become slow, opaque, and expensive to debug.
Jump to a section
Know the shape of the move.
This timeline assumes
- Up to 80 repositories, 120 workflows, 40 contexts, and 20 orbs across one GitHub organization.
- You control CircleCI org settings, project API tokens, GitHub Actions, secrets, environments, and required checks.
- CircleCI stays connected until GitHub Actions has passed one representative release path on every critical repo.
- A pilot covers Docker jobs, service containers, caches, matrix builds, scheduled workflows, and a deploy job.
- We record evidence for every blocking verification check.
What survives the move.
“Partial” and “manual” are not footnotes. They are work that must be scheduled and verified.
| Item | Outcome | Impact | What happens | Mitigation |
|---|---|---|---|---|
| Workflows and job graphs | partial | critical | GitHub says CircleCI can declare several workflows in one config.yml. Actions needs one workflow file per workflow. CircleCI requires becomes Actions needs. | Inventory every workflow name and dependency, then create one YAML file under .github/workflows for each. |
| Automatic test parallelism | lost | critical | GitHub states CircleCI can group tests from rules or historical timing. That behavior is not built into Actions. | Split the suite with a matrix, a shard script, or a third-party action and compare wall-clock time on the same commit. |
| Orbs | manual | critical | Orbs are reusable YAML. Actions are JavaScript or Docker components. The importer leaves unknown orbs as manual work. | List every orb and version. Replace each with a pinned action, a composite action, or inline steps. |
| Contexts and project environment variables | manual | critical | GitHub's importer lists contexts and project-level environment variables as manual tasks. They do not become Actions secrets automatically. | Export context names and consumers. Recreate organization, repository, and environment secrets. Rotate every value. |
| CircleCI convenience images | partial | high | GitHub warns those images set USER to circleci and collide with Actions container permissions. It recommends leaving the convenience images. | Use GitHub-hosted runners or images that keep the default user. Drop the USER instruction from your own Dockerfiles. |
| Docker Layer Caching | lost | high | GitHub says Actions has no equivalent of CircleCI Docker Layer Caching. | Cache buildx layers or registry tags yourself and measure cold versus warm image builds. |
| Workspaces between jobs | partial | high | persist_to_workspace and attach_workspace become upload-artifact and download-artifact. Retention and size limits differ. | Name every artifact, pin actions/upload-artifact and actions/download-artifact, and fail the job if a required file is missing. |
| Service containers | partial | high | CircleCI treats the first docker image as the primary job image. Actions splits container and services and reaches the database by service hostname. | Move env vars onto the job, add a health check, and wait on the service name rather than localhost. |
| Caches | partial | medium | Both systems cache files from YAML. Key syntax changes from checksum to hashFiles. Cross-workflow automatic caches do not import. | Rewrite cache keys, restore-keys, and paths. Confirm a second run on the same lockfile is a hit. |
| SSH rerun debugging | lost | medium | CircleCI can open an SSH session on a failed job. Actions does not ship that as a first-class product feature. | Add tmate only on a labelled debug workflow, or reproduce the job on a self-hosted runner you control. |
| Build history, insights, and test timings | lost | medium | Historical timings that feed CircleCI parallelism and insights stay in CircleCI. | Export required reports before you cancel the plan and accept a new Actions baseline. |
| Scheduled workflows and approvals | manual | high | CircleCI schedules and approval jobs need Actions cron triggers, environments, and required reviewers. | Recreate each schedule in on.schedule and each approval as an environment protection rule. |
Where each thing goes.
| Source | Destination | Method | Notes |
|---|---|---|---|
| CircleCI config.yml workflow | .github/workflows/*.yml file | transform | One Actions file per CircleCI workflow. |
| requires | needs | transform | Keep the same job graph and fail-fast policy. |
| orb | action, composite action, or inline steps | manual | Unknown orbs stay manual after the importer. |
| context or project env var | Actions secret or variable | manual | Rotate and scope to org, repo, or environment. |
| restore_cache / save_cache | actions/cache | transform | Rewrite keys with hashFiles. |
| persist_to_workspace | actions/upload-artifact | transform | Set retention and required paths. |
| docker primary image plus secondary images | container plus services | transform | Use the service hostname and a health check. |
| test parallelism | matrix or shard script | manual | Actions does not group tests from historical timing. |
| approval job | environment protection rule | manual | Required reviewers replace type: approval. |
Make the move recoverable.
Create the source-of-truth backup
Keep every pipeline definition, secret consumer, orb pin, and required-check mapping before Actions becomes authoritative.
- Inventory repositories, workflows, jobs, orbs, contexts, env vars, schedules, approval gates, caches, and deploy targets.
- Export .circleci/config.yml from the production commit and record the CircleCI org and project slugs.
- List every context and project variable name, not the secret values, and note which jobs read them.
- Download recent test reports and timing data you still need for sharding.
- Record current required status checks on every protected branch.
Proof to capture: A dated manifest lists every repo, workflow, orb, context consumer, secret name, and required check.
Importer dry run
See what the official converter can emit before you trust a pull request.
- Run audit on the CircleCI organization if you have an org token.
- Dry-run one representative project.
- Treat contexts, project variables, and unknown orbs as a manual ledger.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
Workflow file split
Match Actions' one-file-per-workflow rule.
- Create one YAML file for each CircleCI workflow name.
- Translate requires to needs.
- Pin every third-party action to a commit SHA.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
The things most likely to hurt.
These are operating limits. Treat every “Stop if” condition as a blocked migration, not a suggestion.
Required checks still point at CircleCI
A protected branch accepts a merge without the new Actions workflow.
- Consequence
- Untested code reaches the default branch.
- Mitigation
- Add the Actions check, watch it go green, then remove the CircleCI check.
Stop if: Any production branch can merge without the approved Actions workflow.
Convenience images break permissions
A Docker job fails on checkout or cache because USER is circleci.
- Consequence
- The suite looks red for a permissions bug, not a product bug.
- Mitigation
- Leave CircleCI convenience images and fix USER in your own Dockerfiles.
Stop if: A critical job only passes after chmod 777 of the workspace.
Secrets leak across jobs
A forked-pull-request workflow can read an org secret, or one job can see another context.
- Consequence
- Credentials leave the intended environment.
- Mitigation
- Scope secrets per environment and disable secret access on untrusted forks.
Stop if: Any untrusted workflow can read a production secret.
Do the work in this order.
- Days 1–4 · inventory
Inventory and decisions
8–16 hours active2–4 days elapsedOwner review waiting- Inventory workflows, orbs, contexts, secrets, and required checks.
- Pick the importer or a manual rewrite per repo.
Depends on: CircleCI and GitHub admin access
Stop / go checkpointPilot?
Go when: Every critical workflow has an owner and a destination file plan.
Stop when: A production deploy job has no owner.
- Days 3–6 · backup
Export and pin
6–12 hours active2–3 days elapsedToken and report export waiting- Archive config.yml, orb pins, and secret-name ledgers.
- Export timing reports you still need.
Depends on: Approved inventory
Stop / go checkpointTransform?
Go when: The manifest matches CircleCI projects and protected branches.
Stop when: A required check or context consumer is missing.
- Days 5–16 · pilot
Translate and pilot
20–50 hours active6–12 days elapsedFirst Actions runs waiting- Dry-run the importer on one repo.
- Rewrite caches, services, and deploy jobs.
- Compare results with CircleCI on the same SHA.
Depends on: Verified backup
Stop / go checkpointScale?
Go when: Pilot jobs, secrets, and artifacts match the approved CircleCI result.
Stop when: A critical job is red only on Actions.
- Days 16–24 · cutover
Switch required checks
10–22 hours active3–7 days elapsedBranch-protection updates waiting- Enable Actions on remaining repos.
- Move required checks.
- Keep CircleCI running in parallel for one cycle.
Depends on: Passed pilot
Stop / go checkpointOpen production?
Go when: Actions is the only required check and deploys run once.
Stop when: CircleCI and Actions both deploy the same commit.
- Days 22–30 · observe
Observe and close
6–10 hours active5–8 days elapsedScheduled and release workflows waiting- Watch scheduled, fork, and release paths.
- Revoke CircleCI tokens only after the observation window.
Depends on: Verified cutover
Stop / go checkpointClose rollback?
Go when: No rollback trigger fires through one release cycle.
Stop when: A required path is missing or a secret is over-scoped.
Cut over with a way back.
Cutover
Make GitHub Actions the only required CI and the only deploy trigger.
- Freeze CircleCI config edits on the default branch.
- Merge the approved Actions workflows on the same commit you compared.
- Add the Actions check to branch protection, then remove the CircleCI check.
- Disable CircleCI deploys and scheduled workflows.
- Run the production release path once and keep CircleCI recoverable.
Proof to capture: Actions owns required checks and deploys. CircleCI cannot ship production. Counts of jobs and artifacts match the pilot.
Rollback
Return required checks and deploys to CircleCI without a double release.
- Stop Actions deploy jobs and environment approvals.
- Restore the CircleCI required check on protected branches.
- Re-enable CircleCI scheduled and deploy workflows.
- Disable the Actions deploy workflow or the environment.
- Run one CircleCI release path and confirm it is the only writer.
Proof to capture: CircleCI is the required check again and only one deploy ran.
- Actions required check missing
- Deploy ran twice
- Secret exposure
- Critical job red only on Actions
- Branch protection accepts an untested merge
Prove the migration worked.
Every blocking check must pass. Capture the evidence before cleanup begins.
| Pass | ID | Check | Method | Expected result | Evidence |
|---|---|---|---|---|---|
V-01Blocking | Workflow and job map | Compare every CircleCI workflow to an Actions file and every requires edge to needs. | Every production job has one destination. | Workflow map. | |
V-02Blocking | Same-commit parity | Run CircleCI and Actions on one frozen SHA for the pilot repo. | Tests, artifacts, and deploy dry-runs match. | Job logs and artifact hashes. | |
V-03Blocking | Context and environment scope | Inspect org, repo, and environment secrets and a fork pull request. | Least privilege. No production secret on an untrusted workflow. | Secret audit. | |
V-04Blocking | Images, services, and caches | Exercise Docker jobs, database services, and a warm cache run. | Services become healthy and the second run is a cache hit. | Service and cache logs. | |
V-05Blocking | Test sharding | Time the full suite and each shard. | Approved duration without silent test drops. | Timing report. | |
V-06Blocking | Required checks | Attempt a merge with Actions failing and with CircleCI removed. | Only the approved Actions check can satisfy the branch. | Branch-protection screenshot. | |
V-07Blocking | Deploy once | Run the production deploy path in a rehearsal. | One deploy, one set of notifications. | Deploy log. | |
V-08Blocking | Rollback | Restore the CircleCI required check in a dry run. | CircleCI can become authoritative again. | Rollback note. |
Remove the scaffolding safely.
Safe after: One full release cycle, one scheduled workflow, and seven stable days.
- Archive CircleCI configs and the importer reports.
- Revoke CircleCI personal API tokens used for the importer.
- Remove unused CircleCI project keys and deploy users.
- Keep the CircleCI org until billing and audit review finish.
- Update runbooks with the new required-check names.
- Schedule the next Actions pin and secret review.
When the plan met reality.
First-hand accounts are preferred. Vendor case studies are labeled, and every note below is an editorial paraphrase—follow the link for the full context.
Jonas Kemper describes his company moving every GitHub repository from CircleCI to GitHub Actions. He missed CircleCI SSH reruns so much that he kept developing jobs on CircleCI, then copied stable YAML to Actions. He found Actions easier for conditional Slack alerts, and found the Actions UI and config validator less intuitive than CircleCI's.
- Keep CircleCI available for SSH debugging until you have an Actions debug path you trust.
- Treat a green Actions Slack alert as a bonus, not proof that debugging and config tooling match CircleCI.
- The painful gap was interactive runner debugging, not the YAML job graph.
- A dual-running workaround can work while CircleCI credits are still allowed.
Evil Martians walked Ruby on Rails and Node.js projects from CircleCI to GitHub Actions and compared wall-clock time. They found the YAML models close enough to move with small edits. Dropping containers in favor of hosted-runner actions cut total workflow time below their CircleCI baseline. They warn that you lose container-level repeatability when you do that.
- Time the CircleCI workflow on a frozen commit before you rewrite it, then time the Actions version on the same commit.
- Look at each container step and replace it with a pinned action only when you accept runner-image drift.
- Rails and Node jobs can move with almost no logic change if you accept GitHub's one-file-per-workflow rule.
- Beating CircleCI duration came from leaving containers, not from a literal YAML translation.
WEAR's backend team at ZOZO moved Rails automated tests from CircleCI to GitHub Actions so they could store octocov coverage in Actions artifacts and trigger both push and pull_request events without calling CircleCI's API. They had to rebuild timing-based test splitting, failed-test reruns, and a TESTS-tab style failure list. After the move they still lacked CircleCI-style flaky-test insights and an easy pull-request coverage view.
- Rebuild timing-based shards and failed-test reruns before you delete CircleCI, because Actions does not ship those.
- If coverage diffs need both push and pull_request triggers, prefer Actions events over a CircleCI API bridge.
- Company-wide Actions adoption was a reason to move, not just a feature checklist.
- Test insights and flaky-test detection do not arrive automatically with the YAML rewrite.
Verify against the primary material.
Platform behavior changes. Check these sources and the review dates above before executing a production migration.
- GitHub: migrate from CircleCIAccessed 2026-10-29
- GitHub: migrate from CircleCI with Actions ImporterAccessed 2026-10-29
- CircleCI: configuration referenceAccessed 2026-10-29