# Migrating Self-Hosted GitLab to a New Server

> Migrating self-hosted GitLab to a new server: the gitlab-backup command, moving gitlab-secrets.json, restoring the archive, and migrating Container Registry.

## Ways to migrate self-hosted GitLab

Self-hosted GitLab can be migrated two ways: the official `gitlab-backup` mechanism, or a full copy of the Omnibus installation directories. The first method suits moving between GitLab versions, the second suits an as-is transfer to an identical version.

Before migrating, record the package version: the same version of `gitlab-ce` or `gitlab-ee` must be installed on both the old and the new server before restoring the backup.

## Creating a backup with gitlab-backup create

This command creates an archive with repositories, the database, and uploads, but it does not include configuration or secrets:

```
gitlab-backup create BACKUP=2026_09_29
ls /var/opt/gitlab/backups/
```

Copy the archive file, named something like `2026_09_29_16.5.0_gitlab_backup.tar`, to the new server with scp or rsync.

## Migrating secrets and gitlab.rb

Without the `/etc/gitlab/gitlab-secrets.json` file, the restored GitLab instance cannot decrypt CI/CD tokens, user 2FA settings, or environment variables. Transfer both configuration files along with the backup:

```
scp /etc/gitlab/gitlab-secrets.json root@newserver:/etc/gitlab/
scp /etc/gitlab/gitlab.rb root@newserver:/etc/gitlab/
```

On the new server, apply the configuration with `gitlab-ctl reconfigure` before restoring the backup.

## Restoring on the new server

Stop the data-writing services, leaving only the database and Redis running, then restore the archive:

```
gitlab-ctl stop puma
gitlab-ctl stop sidekiq
gitlab-backup restore BACKUP=2026_09_29
gitlab-ctl restart
```

Check the command output for permission restoration errors — a common issue when the `/var/opt/gitlab/backups` directory was copied without preserving the `git:git` owner.

## Migrating the Container Registry and CI/CD artifacts

Container Registry images are not included in the standard backup once local disk storage exceeds a certain size. Migrate the registry directory separately:

```
rsync -avz /var/opt/gitlab/gitlab-rails/shared/registry/ root@newserver:/var/opt/gitlab/gitlab-rails/shared/registry/
```

If GitLab runs in Docker containers, the procedure for migrating the containers and volumes themselves is covered in the article on [migrating Docker containers between servers](https://www.zevshost.net/en/kb/migratsia-docker-konteynerov/). For large registry directories, use the methods from the article on [syncing servers with rsync](https://www.zevshost.net/en/kb/rsync-sinhronizatsiya-serverov/) — this lets you finish copying data without a repeated full transfer.

## Verifying the migration

Run a service diagnostic and compare the number of projects and users:

```
gitlab-rake gitlab:check SANITIZE=true
gitlab-rails runner "puts Project.count"
```

Check that cloning a repository works over both SSH and HTTPS, that a CI pipeline runs, and that at least one user with 2FA enabled can log in — this is the most common source of problems when secrets were not migrated.

## Summary: GitLab migration checklist

- Make sure the GitLab version on the new server matches the original.
- Create a backup with `gitlab-backup create` and transfer the archive.
- Copy `gitlab-secrets.json` and `gitlab.rb` before restoring.
- Restore the backup and migrate the Container Registry directory separately.
- Check repository cloning, the CI pipeline, and login with 2FA.

After switching DNS to the new server, go through the general [post-migration checklist](https://www.zevshost.net/en/kb/migratsia-proverka-posle/).

---

Source: https://www.zevshost.net/en/kb/migratsia-gitlab-self-hosted/
