How to Migrate GitLab to a New Server with Zero Downtime

20 May 2026 · DevOps · By AegisElligent

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.

Quick answer: To migrate GitLab to a new server, install the exact same GitLab version and edition on the new machine, run gitlab-backup create on the old one, copy the backup archive plus /etc/gitlab/gitlab-secrets.json across, then run gitlab-backup restore and verify. Re-register runners, freeze writes briefly, and cut over with DNS while the old server stays read-only as a rollback.

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, Back up on the old server, restore on the new one

This is the core of any move to a new server. GitLab’s built-in backup tool captures the database, repositories, uploads, CI artifacts, LFS objects and, when it is stored locally, the container registry. It does not include your configuration, so two files have to travel separately. The commands below are for a Linux package (Omnibus) install.

On the old server

sudo gitlab-backup create
# the archive lands in /var/opt/gitlab/backups as
# <timestamp>_gitlab_backup.tar
# configuration is NOT inside the backup; copy these separately:
/etc/gitlab/gitlab.rb
/etc/gitlab/gitlab-secrets.json

On the new server

Install the same GitLab version and edition (CE or EE) as the source. A restore onto a different version or edition fails, so match first and upgrade afterwards. Put gitlab-secrets.json and gitlab.rb in /etc/gitlab/ before you restore, then:

sudo cp <timestamp>_gitlab_backup.tar /var/opt/gitlab/backups/
sudo chown git:git /var/opt/gitlab/backups/<timestamp>_gitlab_backup.tar
sudo gitlab-ctl reconfigure
sudo gitlab-ctl start
sudo gitlab-ctl stop puma
sudo gitlab-ctl stop sidekiq
# BACKUP is the archive name without the _gitlab_backup.tar suffix
sudo gitlab-backup restore BACKUP=<timestamp_and_version>
sudo gitlab-ctl reconfigure
sudo gitlab-ctl restart
sudo gitlab-rake gitlab:check SANITIZE=true
sudo gitlab-rake gitlab:doctor:secrets

The restore overwrites the contents of the target GitLab database, so only ever run it on the new, empty instance. The last command, gitlab:doctor:secrets, confirms that encrypted values in the database actually decrypt. Skip the secrets file and users with two-factor authentication lose access, runners cannot sign in, and stored CI/CD variables become unreadable. That is the single most common migration failure we are called in to fix.

If GitLab runs in Docker

Run the same tool inside the container: docker exec -t <container name> gitlab-backup create. The archive is written to /var/opt/gitlab/backups, which should be on a volume mounted from the host. Your configuration, including gitlab-secrets.json, lives in the mounted config directory (typically $GITLAB_HOME/config) rather than in the backup, so copy that directory too. On the new host, start a container from the same image tag with the same volume layout, place the backup and config in them, and run the stop, restore, reconfigure and check commands above through docker exec -it <container name>.

What about the database?

For the PostgreSQL database that ships with GitLab you do not need a separate dump: it is part of gitlab-backup create and is restored by gitlab-backup restore. If your database is external (for example a managed PostgreSQL service), plan its migration alongside the GitLab move and keep the database settings in gitlab.rb consistent with it. Data kept in object storage (artifacts, LFS, registry) is not inside the archive either, so those buckets are copied or re-pointed separately.

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. Anything pushed after the backup was taken exists only on the old server, so just before the switch we freeze writes briefly, take a final backup and restore it, then point the record at the new instance. The old instance stays online and read-only for at least 72 hours as a rollback safety net. Pipelines and deployments carry on through the whole move; the only interruption is that short write freeze, and most teams barely notice it.

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.
  • Object storage left behind, a registry kept on local disk is included in the backup, but registry, artifact or LFS data stored in object storage is not and has to be copied or re-pointed separately.
  • 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. Moving pipelines from Jenkins rather than moving GitLab itself? Read our Jenkins to GitLab CI/CD migration guide, or see how we approach DevOps and CI/CD automation and wider cloud migration.

Frequently asked questions

Can I migrate GitLab to a new server with no downtime at all? Close to it. Backup and restore copies a point in time, so anything pushed afterwards stays on the old server. A short write freeze and a final backup just before cutover keeps the interruption to minutes, while the old instance remains available read-only as a fallback.

Do the old and new servers need the same GitLab version? Yes. The destination must run the same version and the same edition (CE or EE) as the backup. Restore first, then upgrade.

What happens if I forget gitlab-secrets.json? The backup does not store your configuration, and without this file the database’s encrypted values cannot be decrypted. Two-factor sign-ins and runner registrations break, and CI/CD variables become unreadable. Copy it before you restore, then confirm with gitlab-rake gitlab:doctor:secrets.

Does the backup include the container registry? Registry data stored locally is part of the standard backup unless you skip it. Anything kept in object storage is not in the archive and must be migrated separately.

Can I move from GitLab.com to my own server with this method? No. gitlab-backup is for self-managed instances. Moving from GitLab.com to self-hosted uses GitLab’s group and project import and transfer features instead.

Last updated: 10 October 2026 · Written by AegisElligent.

Ready to secure and scale your AWS or Azure environment?

Start with a free 20-minute AWS or Azure cloud security assessment. We will identify your highest-priority security gaps and DevOps bottlenecks. No pitch, no obligation.

  • Free 20-minute assessment, no obligation
  • Fixed-price quote, approved before we start
  • Reply within approximately 1 hour
  • NDA available on request