GitLab → GitHub Actions
Move GitLab CI pipelines, jobs, runners, variables, schedules, artifacts, environments, and release operations into GitHub Actions without treating an importer pull request as a finished cutover.
Should you make this move?
Both platforms have a case. Compare what you gain with what you give up before scheduling the cutover.
GitLab
- A comprehensive DevSecOps platform offers deep CI/CD and self-hosting options
- Repository-centered workflows keep code, review, and automation close together
- Breadth, upgrades, and administration can be heavy for smaller teams
- 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: Breadth, upgrades, and administration can be heavy for smaller teams.
What you lose: A comprehensive DevSecOps platform offers deep CI/CD and self-hosting options. 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 100 active pipelines, 50 runners, and 30 shared templates
- Repository, infrastructure, security, and release owners approve equivalent pipeline behavior.
- Destination workflows remain disabled until source queues drain.
- Representative builds and releases can run safely in an isolated environment.
- The migration team records 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 |
|---|---|---|---|---|
| Pipeline YAML and job intent | partial | critical | GitHub documents a syntax map: script becomes run, tags become runs-on, image becomes container, rules become if, and stages become needs. The Actions Importer can convert many pipelines, then still leave manual steps. | Run audit, forecast, dry-run, and migrate per namespace, then review every workflow pull request. |
| Importer coverage and audit scope | partial | high | The audit command only works with a GitLab organization account. Dry-run and migrate work for organization or user accounts. Automatic caching between jobs in different workflows is not supported. | Treat unsupported caches as a redesign and keep a ledger of failed or partial conversions. |
| Masked variables and secrets | lost | critical | GitHub lists masked project or group variable values as a manual task. Secret values must never travel inside exported YAML. | Recreate and rotate credentials as repository or organization secrets with least privilege. |
| Artifact reports | manual | high | The importer does not migrate GitLab artifact reports. Artifacts that persist files still need upload-artifact and matching retention. | Rebuild test, coverage, and security reports as Actions artifacts or checks. |
| Runners, tags, and capacity | manual | critical | GitLab tags choose a runner. GitHub uses runs-on. Self-hosted labels from a converted pipeline are not created for you. | Build reproducible runners and load-test peak concurrency before cutover. |
| Triggers and pipeline schedules | partial | critical | GitLab pipeline schedules live in the UI. GitHub schedules use on.schedule in the workflow file. Push, merge-request, and manual rules need an if expression. | Test every trigger and prevent duplicate schedules during the dual-CI window. |
| Includes, templates, and reusable pieces | manual | critical | GitLab include and extends do not become Actions automatically. Teams that used templates usually rewrite reusable workflows or custom actions. | Inventory each template and pin a replacement action or reusable workflow. |
| Caches | partial | high | GitLab cache keys and paths map conceptually to actions/cache. Cross-workflow automatic caches do not convert. | Rewrite cache keys from lockfiles and compare hit rates on a rehearsal branch. |
| Environments, approvals, and protected releases | manual | critical | Deployment gates, reviewers, and environment protection need GitHub Environments. | Rebuild separation of duties and run a controlled staging release. |
| Build history and job logs | lost | medium | Historical GitLab pipelines stay in GitLab. The importer writes conversion files, not past job logs. | Archive required evidence before the source becomes read-only. |
| Notifications and external integrations | manual | high | Chat, incident, deployment, and reporting callbacks need new credentials and payloads. | Test success, failure, cancel, and timeout notifications. |
| Dual-CI mirror lag | partial | high | A GitLab push mirror can lag for minutes while both CIs run. Teams that compare the same commit can see GitHub start later than GitLab. | Measure mirror delay and avoid treating late GitHub starts as workflow defects. |
Where each thing goes.
| Source | Destination | Method | Notes |
|---|---|---|---|
| GitLab job script | GitHub Actions run step | transform | Keep checkout explicit with actions/checkout. |
| Runner tag | runs-on label | transform | Create missing self-hosted labels before enabling the workflow. |
| image | container | transform | Pin the image digest when the job is production-critical. |
| rules | if expression | transform | Test branch, merge-request, and schedule filters. |
| stage | needs dependency | transform | Preserve failure and ordering behavior. |
| CI/CD variable or masked variable | GitHub Actions secret or variable | manual | Rotate masked values. Do not paste them into YAML. |
| cache paths | actions/cache | transform | Cross-workflow caches need a new design. |
| artifacts paths or reports | actions/upload-artifact or a check | manual | Reports do not convert automatically. |
| include, extends, or template | Reusable workflow or custom action | manual | Pin versions and review permissions. |
Make the move recoverable.
Create the source-of-truth backup
Preserve GitLab data, configuration, and operating evidence before any destination write.
- Export GitLab CI YAML, includes, variables inventory, schedules, environments, runners, and audit evidence.
- Inventory every credential reference, network dependency, notification, and release target.
- Record recent duration, queue, failure, retry, and artifact baselines.
- Hash configuration exports and archive required historical logs.
Proof to capture: A signed manifest reconciles every scoped record class, runtime dependency, export file, count, and hash.
Pipeline capability inventory
Map syntax, templates, runners, credentials, triggers, and release controls.
- Run gh actions-importer audit gitlab for each organization namespace.
- List secrets, self-hosted labels, and unknown build steps from the summary.
- Reject unmapped critical jobs.
Proof to capture: Save the input, output, command or tool settings, warnings, and final item counts.
Converted workflow test harness
Prove artifact and deployment equivalence before writes move.
- Dry-run each active pipeline and review the YAML.
- Run the same revision on GitLab and GitHub.
- Reconcile accepted, rejected, and rewritten steps.
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.
Converted workflows pass but produce different artifacts
A green build hides dependency, environment, or packaging differences.
- Consequence
- Bad releases reach users.
- Mitigation
- Compare checksums, provenance, tests, and deploy side effects.
Stop if: A critical artifact or release invariant differs.
Both systems trigger deployments
The same commit starts GitLab and GitHub release workflows.
- Consequence
- Deployments race or duplicate changes.
- Mitigation
- Disable destination deploy jobs until source queues drain, then switch once.
Stop if: Any source deployment can still start after ownership moves.
Runner or secret access is over-broad
Untrusted code can reach credentials or protected networks.
- Consequence
- Build compromise becomes production compromise.
- Mitigation
- Use isolated least-privilege runners and short-lived identity.
Stop if: A test job can access an unauthorized secret or network.
Do the work in this order.
- Days 1–4 · inventory
Inventory and decisions
8–16 hours active3–4 days elapsedOwner review waiting- Inventory GitLab pipelines, templates, runners, variables, and integrations.
- Approve which jobs convert, rewrite, or retire.
Depends on: GitLab and GitHub Actions administrator access
Stop / go checkpointExport?
Go when: Every critical item has an owner and disposition.
Stop when: Consent, billing, access, or system ownership is unclear.
- Days 4–7 · backup
Backup and reconcile
5–10 hours active2–3 days elapsedExport processing waiting- Export pipeline YAML, variable inventories, and recent job evidence.
- Reconcile counts, totals, and hashes.
Depends on: Approved inventory
Stop / go checkpointTransform?
Go when: Source totals and export manifests agree.
Stop when: Any critical dataset or configuration is absent.
- Days 7–24 · pilot
Map and pilot
20–70 hours active8–16 days elapsedDual-CI comparison waiting- Convert a test repository, then lower-impact projects.
- Run GitLab and GitHub on the same revisions and compare artifacts.
Depends on: Verified backup
Stop / go checkpointScale?
Go when: Pilot mappings, behavior, access, and evidence pass.
Stop when: Any critical check fails or produces unexplained variance.
- Days 20–35 · cutover
Final delta and switch
6–24 hours active1–5 days elapsedQueue drain waiting- Disable GitLab writers and deploy jobs.
- Enable GitHub as the only automation platform and run blocking checks.
Depends on: Passed pilot and approved rollback
Stop / go checkpointOpen production?
Go when: Counts reconcile and one destination system owns production.
Stop when: A source writer remains active or a blocking check fails.
- Days 22–50 · observe
Observe and close
4–20 hours active4–12 days elapsedOperating-cycle evidence waiting- Monitor one complete operating cycle.
- Sign the verification report and close rollback only after stability.
Depends on: Verified cutover
Stop / go checkpointClose rollback?
Go when: No trigger occurs during the agreed observation period.
Stop when: Data, access, delivery, routing, or business results regress.
Cut over with a way back.
Cutover
Make GitHub Actions the only production automation system without losing the final GitLab delta.
- Drain GitLab deploy queues and disable production GitLab pipelines.
- Enable the approved GitHub workflows and protected environments.
- Switch remotes, deploy keys, and release integrations to GitHub.
- Run every blocking verification check and keep GitLab readable.
- Keep the source intact through the rollback window.
Proof to capture: GitHub Actions owns production automation, totals reconcile, and every blocking check has durable evidence.
Rollback
Return production automation to GitLab without losing destination-era changes.
- Stop new GitHub deploy workflows.
- Restore the prior GitLab pipeline schedules, deploy jobs, and remotes.
- Export GitHub post-cutover workflow and release evidence.
- Review and apply safe destination-era changes to GitLab.
- Run the same blocking checks against the restored source.
Proof to capture: GitLab again owns production automation with current configuration and no duplicate destination action.
- Unexplained critical count or value variance
- Missing or exposed critical data
- Duplicate production action
- Failed access, routing, delivery, or integration check
- A critical feature has no safe destination replacement
Prove the migration worked.
Every blocking check must pass. Capture the evidence before cleanup begins.
| Pass | ID | Check | Method | Expected result | Evidence |
|---|---|---|---|---|---|
V-01Blocking | Pipeline coverage | Reconcile active GitLab jobs to destination workflows or retirements. | Every job has one approved disposition. | Pipeline ledger. | |
V-02Blocking | Command and test parity | Run identical revisions and inputs. | Required tests and exit behavior match. | Run comparison. | |
V-03Blocking | Binary equivalence | Compare checksums, metadata, provenance, and retention. | Approved artifacts match. | Artifact report. | |
V-04Blocking | Event and schedule behavior | Exercise push, merge request, manual, and cron paths. | One correct run per approved event. | Trigger log. | |
V-05Blocking | Environment and capacity | Test tools, network, isolation, and peak concurrency. | Jobs run within approved security and time objectives. | Runner report. | |
V-06Blocking | Secrets and permissions | Run allowed and denied access tests. | Least privilege and no secret disclosure. | Security evidence. | |
V-07Blocking | Approvals and deployment | Run a controlled staging and production release. | Gates, concurrency, rollback, and notifications pass. | Release evidence. | |
V-08Blocking | Single automation platform | Inspect source queues, triggers, webhooks, and schedules. | GitHub Actions alone starts approved work. | Cutover checklist. |
Remove the scaffolding safely.
Safe after: One complete operating cycle, at least seven stable days, and owner sign-off on every blocking check.
- Create final GitLab exports and archive verification evidence.
- Revoke temporary credentials, API keys, webhooks, and elevated roles.
- Remove obsolete pipelines, schedules, integrations, and unused runners.
- Keep the source intact through the approved retention window.
- Cancel paid plans only after billing, legal, and recovery review.
- Schedule the next GitHub Actions backup, access, and migration-playbook 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.
Clear Street Engineering described moving self-hosted GitLab CI to GitHub Actions over two and a half quarters, with one quarter spent on the Fleet monorepo. They mirrored GitLab to GitHub, ran dual CI, and built Octomigrator so each GitLab merge request opened a GitHub pull request and posted the Actions result back. Lower-impact Terraform and Retool repos moved first. Cutover was disabling GitLab writes and switching remotes. They later measured p90 queue time around 80 seconds and reported mirror lag from a few minutes up to 15 minutes during the side-by-side phase.
- Mirror the repository and run both CIs on the same changes until you can fail the source pipeline when the destination workflow fails.
- Move a low-traffic repository first, then recruit a person from each team that owns a different pipeline.
- The hard work was proving parity on a monorepo, not flipping the git remote.
- GitLab’s push mirror added minutes of delay while both systems ran, which slowed the comparison loop.
Baserow CEO Bram Wiepjes wrote that the project left GitLab as its primary home in November 2025 and kept GitLab as a read-only mirror. The largest piece of work was rebuilding CI for GitHub Actions, which they said had no simple one-to-one path from .gitlab-ci.yml. Issues and open merge requests moved with a modified node-gitlab-2-github after a test on an empty repository. After the move they reported more than 1,000 GitHub stars in about three months, faster pipelines, and some GitLab review features they still missed.
- Budget a CI rewrite instead of a mechanical YAML conversion, and keep the old GitLab file as evidence.
- Rehearse issue and merge-request import on an empty repository before you touch the production project.
- CI rebuild time dominated the migration even for a team that already had a GitHub mirror.
- Community reach drove the platform change, while review workflow details were the main regret.
Verify against the primary material.
Platform behavior changes. Check these sources and the review dates above before executing a production migration.
- GitHub: migrate from GitLab CI/CDAccessed 2026-10-24
- GitHub: GitLab Actions ImporterAccessed 2026-10-24
- GitLab: CI/CD YAML syntaxAccessed 2026-10-24