How much work is migrating from GitHub Actions to GitLab CI/CD
Migrating from GitHub Actions to GitLab CI/CD means rewriting each workflow as a .gitlab-ci.yml pipeline, rebuilding the runner fleet that executes the jobs, and recreating every secret and variable the workflows depend on. The size of that work follows three counts: how many workflows and jobs you run, how many of them call reusable workflows or composite actions, and how many secrets, environments and deployment credentials they consume. A team with a handful of build workflows and no shared logic has a small rewrite. A team with dozens of repositories sharing deployment workflows and environment-scoped secrets has a program of work. Before quoting, inventory workflows by trigger, runner label, marketplace action and secret, and mark the ones that deploy to production, since those need the most review.
Repository import and workflow migration are separate tasks, and they should be planned as separate tracks. The GitHub importer moves repositories, issues and pull requests, but it does not convert Actions workflows into GitLab pipelines, so the pipeline rewrite starts after the code is in place. GitLab's own migration guide for moving from GitHub Actions frames the conversion, and it is the natural starting reference for mapping workflow concepts to jobs, stages and rules. Confirm with your GitLab administrators which runner executors and tags will replace your GitHub-hosted and self-hosted runners, because pipelines cannot be validated until a runner exists to execute them.
Ask any partner or internal team to break the estimate into a workflow inventory, a conversion plan for reusable logic (includes, components or templates instead of reusable workflows), a secrets and environment migration plan, runner build and hardening work, and a parallel-run period where both systems execute the same pipelines. Those items are one-time costs. Runner operation, pipeline maintenance and CI minutes on self-managed infrastructure recur, and they should be costed separately so the first year is not confused with steady state.
Which GitHub Actions concepts map to GitLab CI/CD and which need redesign
This table compares the main GitHub Actions concepts with their GitLab CI/CD counterparts and marks each as a direct mapping, a structural change or a decision the team must make. Read the middle column for the closest equivalent and the last column for the work that remains. Confirm each keyword against the current GitLab CI/CD documentation before rewriting pipelines, because feature names and syntax change between releases.
| Concept | GitLab CI/CD equivalent and mapping | Effort and decision |
|---|---|---|
| Workflow file | A single .gitlab-ci.yml at the project root replaces the .github/workflows directory. Multiple workflows usually become one pipeline, with includes pulling in shared files. | Structural change. Decide how many pipeline files you need and which ones live in shared projects. |
| Jobs and steps | Jobs map to GitLab jobs. Steps usually become script lines inside a job, or separate jobs when you need independent retries, artifacts or runners. | Structural change. Steps that depend on each other often need to be split into jobs or stages. |
| Triggers | Events such as push and pull request map to rules or workflow:rules. Confirm how merge request and branch pipelines are selected for your project settings. | Redesign. Trigger logic must be rewritten, and duplicate pipelines need to be checked. |
| Matrix builds | GitHub strategy.matrix maps to the parallel:matrix keyword in GitLab. Confirm the supported syntax for your target GitLab version. | Direct for many cases. Complex matrices with excludes may need manual expansion. |
| Environments and deployments | The environment keyword in GitLab records deployments and links to environment pages. Protected environments and approvals are configured in project settings. | Decision required. Define who may deploy to each environment and which approvals apply. |
| Artifacts and caching | artifacts passes files between jobs and cache reuses dependencies across runs. Cache keys and artifact expiry behave differently, so confirm them in the GitLab docs. | Redesign. Cache storage depends on your runner and object storage setup. |
| Reusable workflows | GitHub reusable workflows have no one-to-one match. GitLab uses include for shared YAML and CI/CD components for versioned, reusable building blocks. | Structural change. Choose between includes and components based on how you version shared logic. |
| Secrets and variables | Repository secrets map to CI/CD variables, ideally masked and protected. Confirm how external secrets managers are connected in your instance. | Decision required. Decide where secrets are stored, who can read them and how rotation works. |
| Runners | GitHub-hosted runners have no direct GitLab equivalent in self-managed instances. GitLab Runner executes jobs on machines or clusters you provision and register. | New decision. Choose executors, scaling model and isolation before migrating the first pipeline. |
Reusable workflows versus includes and components
GitHub reusable workflows and composite actions do not translate one to one into GitLab, so this section decides which shared logic moves and in what form. GitLab offers two main mechanisms for sharing pipeline configuration: include, which pulls YAML from other files or projects into a pipeline, and CI/CD components, which are versioned, reusable units that a pipeline references with a defined input interface. Neither is a direct equivalent of a GitHub reusable workflow called with workflow_call, and neither is a direct equivalent of a composite action. A workflow that calls another workflow as a job, or a composite action that bundles steps, usually needs to be redesigned rather than copied across. Confirm the current component syntax and catalog behavior in the GitLab CI/CD documentation for your release before you write any shared template, because the feature set has changed across versions.
The main trade-off is between flexibility and ownership. Includes are simple to adopt, but a change in a shared file lands in every pipeline that includes it, and pinning to a branch or moving reference makes builds drift without anyone noticing. Components are designed for versioned release, so a platform team can publish a fixed version, let product teams upgrade on their own schedule and roll back a bad release. The cost is the work to define inputs, document them and run a release process for the components. Ask who will own each shared piece, who approves a change that affects other teams, and how long old versions stay usable. For a self-managed instance, decide whether components live in the same instance or in a project with restricted write access, and check that the reference resolves from runners in your network, including in offline environments.
Documented import paths and what the GitLab importer does not carry over
The GitLab importer moves repository and project data from GitHub into self-managed GitLab, but it leaves Actions workflows behind. Each card below separates what the importer covers from what your team must rebuild, verify or plan before the cutover.
GitHub importer scope
The GitHub importer brings repositories, issues, pull requests and related project data into a GitLab project. Confirm the exact list of object types against the import documentation for your GitLab version, since coverage changes between releases. Check how pull requests are represented as merge requests and whether attachments, wiki content and releases arrive intact before you tell reviewers to switch.
Actions workflows are not converted
The importer does not translate .github/workflows files into .gitlab-ci.yml pipelines. Every workflow, trigger, matrix and job condition must be rewritten by your team or a partner. Inventory the workflows first, group them by purpose, and record which ones gate merges or deployments, because those carry the most risk if they are rebuilt incorrectly.
Secrets and variables stay outside the import
Repository and environment secrets configured in GitHub are not carried into GitLab CI/CD variables by the importer. Plan to recreate each secret in GitLab with the right protection and masking settings, and rotate any credential that a rewritten pipeline will now use. Confirm with your security team how values will be delivered, whether through GitLab variables or an external secrets manager.
Runners replace GitHub-hosted runners
GitHub-hosted runners do not exist in a self-managed GitLab instance, so jobs need a GitLab Runner registered to your instance. Decide which executor type fits each workload, and confirm that the runner version matches your GitLab version as the Runner documentation advises. Review how runner tags are assigned, since untagged jobs can land on the wrong machine.
Migration guide and alternative export paths
GitLab publishes a migration guide for teams moving from other CI systems, and it is the right starting point for planning the pipeline rewrite. Ask whether any API or export path is suitable for your inventory of repositories and workflows, and confirm the current capabilities against the documentation before relying on them. Treat any scripted export as a planning input that your team validates, not as a conversion of the workflows.
The sequence from inventory to decommissioning GitHub Actions
This sequence moves GitHub Actions workflows into GitLab CI/CD in controlled stages, so each pipeline is proven on one repository, run alongside the original, and switched over with deploy credentials checked before GitHub workflows are retired.
Inventory workflows and their dependencies
Your engineering or platform team lists every workflow file in every repository, including triggers, scheduled runs, environments, deployment targets and reusable workflows or composite actions each one calls. Record which third-party actions are pinned, which secrets and variables each job reads, and which self-hosted runners execute them. Confirm with the owners of each repository which pipelines are still in use. The output is a table with one row per workflow and its dependencies, signed off by the teams that own the deploy paths.
Map secrets and environments before rewriting
Decide where each GitHub secret and environment value will live in GitLab, whether as group or project CI/CD variables, protected and masked variables, or an external secrets manager your organization already runs. Document which values are scoped to production, which need protected branches or tags, and who can change them. Do not copy secret values into documents or chat. The check that closes this step is a mapping list where every secret has a named destination and an owner, reviewed by security before any pipeline runs.
Build the self-managed instance and runner fleet
Platform engineering installs the self-managed GitLab instance using the install method your standards allow, then registers GitLab Runner in the environments that need access to build or deploy targets. Choose executors that match the workload and keep Runner versions aligned with GitLab, as the Runner documentation advises. Set up identity, backups and the upgrade path before pilot work begins. The output is a working instance with a tested backup and at least one runner that can execute a sample job inside your network.
Pilot one low-risk repository
Pick a repository with simple build and test jobs and no production deploy. Rewrite its workflow as a
.gitlab-ci.ymlfile, reusing the GitLab migration guide for keyword mapping, and convert reusable workflow calls into includes or components. Run the pipeline on the new runner and compare artifacts, test results and job durations with the GitHub run. The pilot is complete when the team has a repeatable pattern for the workflow types in your inventory and has written down the differences it found.Run both systems in parallel
For each repository in the migration wave, keep the GitHub workflow active while the equivalent GitLab pipeline runs on the same commits or on a scheduled mirror. Compare results for build outcomes, test counts, artifacts and deployment dry runs, and log any mismatch with its root cause. Keep production deploy jobs disabled in GitLab until the cutover step. The output is a parallel run record per repository showing that the GitLab pipeline matches the GitHub result for the agreed checks.
Cut over triggers and deploy credentials
Switch the source of truth for each repository in a planned change window. Disable GitHub workflow triggers first, then enable the GitLab pipeline rules for branches, tags, schedules and manual deployments. Replace deploy credentials with the GitLab variables or secrets manager entries reviewed earlier, and rotate any credential that was shared between systems. Confirm that a production deploy runs once and that the expected approvals still apply. The closing check is a deploy from GitLab with evidence recorded in your change system.
Decommission GitHub workflows
After the cutover has run without rollback for a period your change policy sets, remove or disable the GitHub workflow files, revoke unused deploy keys and tokens, and retire self-hosted GitHub runners that are no longer needed. Archive the old workflow definitions and run history in your records system, since compliance teams may need them. Update repository documentation and access reviews. The sequence ends when no GitHub workflow can trigger a deployment and the archive is complete.
Rebuilding runners and secrets without leaking credentials
GitHub Actions secrets do not move into GitLab, and the jobs that used them need a runner they can trust. The cards below cover the runner fleet, the credentials you re-enter, the isolation of production deployments and the rotation that should follow cutover.
Choose runners and executors first
Each GitHub Actions runner label needs a GitLab Runner equivalent, and the executor decides how a job is isolated. The shell executor runs jobs directly on the host, while the Docker executor and autoscaling executors start a fresh environment per job. Inventory which workflows need GPUs, macOS or Windows builders, or private network access, then confirm which executor and runner hosts can serve them in your environment before rewriting runs-on as tags.
Keep Runner aligned with GitLab
The GitLab Runner documentation advises keeping the Runner's major and minor versions aligned with the GitLab instance. Plan the Runner upgrade into the same change window as the GitLab upgrade, and record which Runner version each fleet member runs. Mixed versions across runner hosts are a common source of confusing pipeline failures during a migration, so check the version matrix for your target GitLab release before cutover.
Isolate production deployment jobs
Workflows that deploy to production should not share a runner with pull request builds from contributors. Give deployment jobs a dedicated runner pool with restricted tags, protected branches and protected environments, so untrusted merge request pipelines cannot reach production credentials. Confirm the runner registration options and how your chosen executor separates job workspaces before you move the first deployment pipeline.
Re-enter secrets as CI/CD variables
GitHub does not export the values of repository and organization secrets, so each one must be recreated as a GitLab CI/CD variable by someone who holds the real value. Mark variables as masked and protected where the value allows it, and scope them to the groups or projects that need them. Build a register that maps each GitHub secret name to its GitLab variable, owner and consuming job, so no credential is copied from a developer's notes or a chat thread.
Confirm OIDC or external secrets
Long-lived cloud keys stored as variables are the riskiest part of a migrated pipeline. Ask your team whether your cloud provider and GitLab setup can issue short-lived identity tokens to jobs, or whether an external secrets manager should supply values at runtime instead. Confirm which option your GitLab version, runner executor and cloud accounts support, and document the trust conditions before any production job depends on them.
Rotate credentials during cutover
Treat every secret that was readable by GitHub Actions as exposed once the migration plan is known, and rotate it on a schedule agreed in writing. Rotate after the GitLab pipeline has been proven on a non-production target, and revoke the old values only after the last GitHub workflow that used them has been retired. Record the rotation date, the new owner and the verification run for each credential so audit evidence exists for the change.
What drives the effort and cost of the pipeline rewrite
This table lists the factors that change how much work a GitHub Actions to GitLab CI/CD rewrite takes, whether each cost is one-time or recurring, and what to request in a written quote. Read it before comparing offers, because the same workflow count can produce very different scopes depending on reusable workflows, secrets and runner needs.
| Driver | Why it changes effort or cost | One-time or recurring | What to ask for in a quote |
|---|---|---|---|
| Number of workflows and repositories | Each workflow needs translation, testing and review, and shared patterns across repositories can cut repeated work if they are designed once. | One-time | Inventory counts by repository, and whether shared templates will be built once and reused. |
| Reusable workflows and third-party actions | GitHub reusable workflows map to GitLab includes or components only after a design decision, and each marketplace action needs a script, a template or a vetted replacement. | One-time | A list of called workflows and external actions, with the proposed GitLab equivalent for each. |
| Matrix builds and environment complexity | Matrix strategies expand into parallel jobs in GitLab, and environment protection rules, approvals and deployment stages must be rebuilt explicitly. | One-time | Matrix dimensions, number of target environments and how approvals are expected to behave. |
| Secrets and variables | Each secret must be recreated at the right GitLab scope (instance, group or project), protected or masked as needed, and checked for unused or duplicated entries. | One-time, with ongoing rotation | Secret count by scope, the secrets manager in use, and who owns rotation after handover. |
| Deployment targets and credentials | Each target needs its own authentication path, such as OIDC trust or a stored credential, and each path requires review and a test deployment. | One-time, then recurring when targets change | Number of targets, the authentication method for each and the test plan for non-production deployments. |
| Runner infrastructure | Hosted GitHub runners do not carry over, so self-managed GitLab Runners must be installed, registered, sized and secured on infrastructure you control. | Setup is one-time, capacity and patching are recurring | Executor type, expected concurrent jobs, operating system images and who patches the fleet. |
| Parallel running during cutover | Running both systems side by side doubles pipeline maintenance and requires a rule for which system gates merges and releases. | One-time, usually weeks of overlap | The overlap period, the system of record during overlap and the exit criteria for turning off Actions. |
| Validation and evidence | Each rewritten pipeline needs comparison of outputs, artifacts and deployment results against the original, and the comparison record may be needed for change management. | One-time | Test cases per critical pipeline and where validation results will be stored. |
What usually goes wrong in a GitHub Actions to GitLab move
Most failed migrations of CI/CD are not failed builds. They are pipelines that look green, credentials that were never re-created, and runners that behave differently from what the old system did. The cards below cover the risks that most often surface after cutover, each with the check to run before the old workflows are switched off.
Pipelines that pass without testing
A translated job can succeed because it runs an empty script, a skipped rule or a missing test path. In GitHub Actions, a workflow may run tests through a matrix or a composite action that does not map one to one onto GitLab jobs. Compare the set of executed test cases and artifacts from both systems on the same commit, and fail the migration review if counts differ without an explanation.
Secrets left in the old system
Repository, environment and organization secrets in GitHub do not transfer to GitLab CI/CD variables. Teams often copy the workflow file and forget that the values must be re-entered as masked, protected variables scoped to the right groups and environments. Build an inventory of every secret referenced by a workflow, assign an owner for each, and rotate any value that was shared with more people than needed.
Runners sized without load data
GitHub-hosted runners hide capacity decisions that become your responsibility on self-managed GitLab. Without historical queue times, job durations and concurrency peaks, a fleet is either overbuilt or starved during release days. Export job timing from the existing system for at least one full release cycle, then size the runner pool and autoscaling limits from that data and confirm the plan with your platform team.
Untested deployment credentials
Deployment jobs usually fail only at the moment they need cloud or cluster access, which is often during cutover. Test every deploy credential against a non-production target before the first real release, including OIDC trust conditions, token lifetimes and the branch or tag restrictions that protect production environments. A credential that works in a dry run but not in the live pipeline should block cutover.
Triggers firing twice during parallel running
While both systems are live, a push to a shared repository can start a GitHub workflow and a GitLab pipeline at once. If both deploy, or both publish packages or send notifications, the result is duplicate releases and noisy alerts. Disable deployment triggers in one system during the parallel period, use manual gates on production jobs, and log which system ran each release so the team can see which pipeline is authoritative.
Runner version drift
GitLab Runner should stay aligned with the GitLab major and minor version, and a fleet built at different times can drift apart. Mismatched runners can fail on newer keywords or behave differently under the same configuration. Pin the runner version in your provisioning code, check the installed version in each pool during health checks, and upgrade runners in the same change window as the GitLab instance they serve.
Questions buyers ask before migrating GitHub Actions to GitLab
What does migrating GitHub Actions to GitLab CI/CD cost?
The cost is mostly the labor to rewrite and validate pipelines, plus the infrastructure to run GitLab Runner. Labor scales with the number of workflows, how many use reusable workflows, custom actions or matrix builds, and how many secrets and environments must be recreated and tested. Infrastructure is a recurring cost for runner hosts or cloud capacity, and for the GitLab instance itself. Software licensing is a separate line: GitLab Free costs 0 dollars, and the GitLab pricing page lists Premium at 29 US dollars per user per month billed annually, with Ultimate custom priced. Ask any provider to split the quote into one-time rewrite and validation work, recurring runner and instance costs, and any license changes.
How long does a GitHub Actions to GitLab pipeline migration take?
Duration depends on how many workflows you have, how much logic they share, and how many systems they call. A small set of single-file workflows moves faster than a fleet built on reusable workflows, composite actions and third-party marketplace actions, each of which needs an equivalent in GitLab, such as an include, a component or a script. Other drivers are the runner design you choose, the secrets and environment approvals you must recreate, the number of deployment targets, and how long you run both systems in parallel. Ask for the timeline as a written milestone plan tied to the scope inventory, not a single estimate.
What does our internal team need to provide during the migration?
Your team needs to supply the workflow inventory, owners for each repository and deployment target, and access to the GitHub and GitLab administrators who can create groups, projects, runners and variables. Security staff should confirm how secrets are stored and who approves production deployments. Platform engineers need to decide where runners run and what network paths they need to reach registries, clouds and internal services. Expect to provide test environments and a sign-off process for each pipeline that touches production. Agree these responsibilities in writing before the work starts so that unresolved owner questions do not stall the rewrite.
Can our team do the migration in-house using the GitLab migration guide?
Yes, a team with CI/CD and GitLab administration experience can follow the GitLab migration guide and rewrite its own pipelines. The questions to answer honestly are capacity and coverage: whether the people who know the workflows can pause feature work, whether someone can own runner security and upgrades after cutover, and whether anyone has built the reusable patterns you need. A partner adds value mainly when there are many workflows, strict change control, or compliance evidence to produce. Whichever path you choose, ask for a pilot on a few representative repositories before committing to the full inventory.
Can GitHub Actions and GitLab CI/CD run in parallel during cutover?
Yes, the two systems can run side by side, but you must decide which one is authoritative for each repository and deployment target at any time. Running both against the same production target risks duplicate deployments, so pipelines should be switched per repository or per environment, and secrets should be scoped so each system only reaches what it needs. Plan for a period where code is mirrored or migrated while workflows are still live in GitHub. Record the cutover order, the rollback step for each repository, and the date the GitHub workflow is disabled.
How does our GitLab tier choice change the migration scope?
The tier decides which CI/CD and security features are in scope. GitLab Free covers core pipelines. The GitLab pricing page lists advanced CI/CD and priority support for Premium, and application security testing, software supply chain security, vulnerability management and compliance features for Ultimate. If your GitHub workflows run security scanners, policy checks or compliance gates, confirm which of those exist at your chosen tier before rewriting them, because some will need replacement tooling or a tier change. Tier choice also affects licensing cost and who must approve the purchase. Ask for a feature-by-feature mapping of your current workflows to the target tier.
What breaks most often when moving workflows to GitLab runners?
The most common failures come from assumptions that do not carry over: runner images and installed tools, environment-specific secrets, job-level permissions for tokens, and marketplace actions with no direct equivalent. Runner executor choice matters too, since shell, Docker and autoscaling executors differ in isolation and in how they cache dependencies. Keep GitLab Runner major and minor versions aligned with the GitLab instance, as the GitLab Runner documentation advises. Test each pipeline on a runner that matches production constraints, and confirm the outbound network paths before cutover.
What the migration needs from your team
The first input is a complete workflow inventory. Export or list every workflow file under .github/workflows in each repository, and record which ones are reusable (called with workflow_call), which run on schedule or repository_dispatch, and which depend on marketplace actions. Add the composite actions your teams wrote and any workflow that publishes packages, images or deployments. Your team also needs to say which pipelines are production-critical, because those are the ones that get parallel runs and a rollback path. Ask us to confirm the inventory covers organization-level and repository-level workflows, since they are easy to miss when the scope is set by repository name.
The second input is access and ownership. We need read access to the GitHub organization, the repositories in scope and the settings that govern Actions, including environments, secrets and self-hosted runner groups. Name one owner for each deployment target (cluster, cloud account, server or registry) who can approve changes to its credentials and network rules. Secret ownership is the decision that most often stalls the work: for each secret, someone must confirm whether it is rotated during cutover, moved into GitLab CI/CD variables or a secrets manager, or retired. Values should be re-issued where possible rather than copied, so ask the security team to agree on that approach before the first pipeline is rewritten.
The third input is a signed-off cutover date and a decision on the runner fleet. Your change board should approve when each repository switches triggers, who can halt the switch, and how long GitHub Actions and GitLab pipelines run side by side. Confirm where the GitLab Runners will execute jobs and whether deployment targets accept connections from them, since a pipeline that cannot reach its target fails on the first deployment. Ask the platform team to confirm which GitLab edition and tier your organization has licensed, because some CI/CD features used in the rewrite depend on it. We will not set a cutover date for you; we will document the risks and dependencies that your approval needs to cover.
How DeployOpen works on this
DeployOpen plans, implements, customizes, tests, documents and supports self-hosted GitLab inside your own infrastructure, including its runners. For a GitHub Actions to GitLab CI/CD migration, we start from an inventory of your workflows and build a written scope before any pipeline is rewritten.
How we scope
- We inventory every GitHub Actions workflow, reusable workflow, composite action and secret your teams depend on.
- We map each workflow to GitLab jobs, includes, components, runner tags and CI/CD variables in a written scope document.
- Engagements are fixed-scope, so deliverables, timelines and exclusions are agreed in writing before work starts.
How we build
- We rewrite pipelines in a test project first, then compare job results and artifacts against the original workflow runs.
- We build and register the GitLab Runner fleet on infrastructure you control, with executor type and isolation chosen per workload.
- We move secrets into GitLab CI/CD variables or your secrets manager, scoped to protected branches and environments.
How we hand over
- We document the pipeline layout, runner configuration, variable ownership and the cutover sequence for your team to run.
- We run a planned cutover with a rollback path, then confirm that scheduled and branch-triggered pipelines behave as expected.
- We hand over the rewritten pipelines, runner setup notes and a list of open items your team owns after the migration.
How we support
- Ongoing support, upgrades and continuous testing are available as retainers after handover.
- Retainers can cover GitLab and Runner version alignment, since the Runner documentation advises keeping major and minor versions matched to GitLab.
- Confirm with us during scoping which support level, response expectations and upgrade stops your retainer will include.

