Migrating GitLab to new infrastructure sounds risky, but with the right sequence, you can move every repository, runner, pipeline and container image without stopping a single deployment. Here is the exact playbook we use on client engagements.
Why teams migrate GitLab in the first place
Most GitLab migrations we handle fall into three buckets: moving from GitLab.com (SaaS) to a self-hosted instance for data-sovereignty reasons, upgrading from an ageing self-managed server to a modern cloud VM or Kubernetes cluster, or consolidating several GitLab instances after a merger. In every case the fear is the same, that a migration means a maintenance window, frozen pipelines and a team sitting idle. It does not have to.
Step 1, Inventory everything before you touch anything
Before any migration, we build a complete inventory: total repository count and size, number of CI/CD runners and their tags, active pipeline definitions, the size of the container registry, and all integrations (Slack, Jira, webhooks). GitLab stores most of this in its database and the Git data on disk or in object storage. Knowing the exact volume tells you whether a simple backup-and-restore will work or whether you need a phased, repository-by-repository approach.
Step 2, Stand up the target instance in parallel
Never migrate in place. We provision the new GitLab instance, whether on an EC2 instance, an Azure VM, or a Kubernetes cluster via the official Helm chart, completely in parallel with the old one. This target runs the same GitLab version as the source (version parity is critical; you upgrade after the migration, never during). We configure object storage for artifacts, LFS and the registry up front so nothing lands on local disk.
Step 3, Use the built-in backup and restore, then verify
GitLab ships a robust backup tool (gitlab-backup create) that captures repositories, the database, uploads, and CI artifacts. We run a full backup on the source, transfer it to the target over a private network link, and restore with gitlab-backup restore. The registry and secrets file (gitlab-secrets.json) are handled separately, miss the secrets file and every stored CI/CD variable becomes unreadable, which is the single most common migration failure we are called in to fix.
Step 4, Migrate runners without breaking active jobs
Runners are where "zero-downtime" is won or lost. Rather than repointing existing runners, we register a fresh set of runners against the new instance while the old runners keep serving the old instance. Once the new instance is verified, we update the DNS record. In-flight jobs on the old runners complete normally; new jobs route to the new runners. No pipeline is ever cancelled mid-run.
Step 5, Cut over with DNS, keep the old instance warm
The final cutover is a DNS change, not a server shutdown. We lower the DNS TTL to 60 seconds a day in advance, then switch the record to the new instance. The old instance stays online and read-only for at least 72 hours as a rollback safety net. Teams keep pushing and deploying throughout, most never notice the migration happened.
Common pitfalls we see
- Forgetting the secrets file, CI/CD variables become undecryptable. Always migrate
gitlab-secrets.json. - Version mismatch, restoring a backup onto a newer GitLab version fails. Match versions, migrate, then upgrade.
- Registry left behind, the container registry is separate from the Git backup and needs its own migration.
- No rollback plan, always keep the source instance warm and read-only until the new one is proven.
How long a GitLab migration actually takes
For a typical mid-size team, a few hundred repositories, dozens of pipelines, a moderate registry, the hands-on migration window is usually a single evening, with a few days of parallel-running on either side for verification and rollback safety. The bulk of the effort is in the preparation: inventorying everything, provisioning and hardening the target, and doing a dry-run restore into a staging instance before touching production. We always rehearse the full restore at least once against throwaway infrastructure so the production cutover holds no surprises.
Post-migration: what to verify before you celebrate
After cutover we run a verification checklist: every repository clones correctly, CI/CD pipelines trigger and pass on a test commit, the container registry pushes and pulls, all CI/CD variables decrypt (this is where the secrets file matters), webhooks and integrations fire, and user permissions map correctly. Only once every item passes do we schedule the decommission of the old instance, and even then we keep a final backup archived. Skipping this checklist is how teams discover three weeks later that a critical scheduled pipeline silently stopped running.
The bottom line
A GitLab migration is a sequencing problem, not a downtime problem. Inventory first, run in parallel, migrate secrets with the data, repoint runners cleanly, and cut over with DNS. On our last client engagement we migrated 200+ repositories and 70+ pipeline jobs with zero unplanned downtime. If you are planning a GitLab migration, book a free assessment and we will map the exact sequence for your environment.
Last updated: 1 July 2026 · Written by Momina Farooq, AWS Certified Solutions Architect, Azure AZ-500, Licensed Ethical Hacker.