Migrating an existing GitLab
Already using GitLab and want to move to server.camp? That works – and in both cases your repositories, issues and merge requests come along. Which path applies depends on exactly one question: is your GitLab on gitlab.com or on a server of your own?
| Your situation | Path | Who does it | Downtime |
|---|---|---|---|
| gitlab.com (SaaS) | Direct transfer | you, as self-service | none |
| Self-hosted (Omnibus, Docker, Kubernetes) | Backup and restore | we do, in a maintenance window | yes, about 2 - 4 hours |
Before the move: create a subscriptionBoth paths need a GitLab subscription at server.camp first – you can try GitLab free for 30 days and rehearse the migration during the trial.
On gitlab.com, direct transfer is the path GitLab recommends. A backup restore as in path B is not possible here: GitLab SaaS gives you no access to backup files. The only alternative is a file-based export – but it carries fewer objects and is more cumbersome, which is why GitLab itself recommends direct transfer.
Direct transfer runs directly between the two GitLab instances over HTTPS. You do this yourself – all you need from us is the provisioned instance.
- Direct transfer has to be enabled on both instances. It is active on gitlab.com; in your instance at server.camp you have to switch it on once (see the next section).
- The Owner role on the source group you want to migrate
- A personal access token with the
apiscope on the source (i.e. on gitlab.com) – on the destination side, a role that lets you create subgroups is enough
On self-managed instances – and therefore on ours – direct transfer is disabled by default, and so are all import sources since GitLab 16.0. You enable both yourself as an admin (details in GitLab’s import and export settings):
- In the upper-right corner, select Admin
- In the left sidebar, select Settings → General
- Expand the Import and export settings section
- Select the checkbox for Allow migrating GitLab groups and projects by direct transfer
- In the same section, additionally enable GitLab.com under Import sources
Switch it off again after the moveBoth switches are import features you no longer need once the migration is done. Disabling them again afterwards is good practice.
Direct transfer migrates groups and projects – but no user accounts. User accounts are never created during a migration. How issues, merge requests and comments still end up with the right people changed in GitLab 18.4: there is no more matching by username or email address. Instead, placeholder users are created and you assign them to the real accounts afterwards (post-migration mapping).
- Import the group – via New group → Import group, entering the source URL (
https://gitlab.com) and your token. Projects come along with the group import; individual projects can also be imported on their own. - Placeholders appear automatically – GitLab creates a placeholder user (
Placeholder <name>) for everyone with imported contributions. These accounts cannot sign in, cannot trigger anything and do not count towards license limits. - Create the users – as an admin under Admin area → Overview → Users, or connect single sign-on right away. This may happen after the import: you have time to review the result first.
- Reassign the contributions – in your top-level group, go to Manage → Members → Placeholders → Awaiting reassignment, pick the real user per placeholder and select Reassign. For larger teams there is Reassign with CSV: download the template, enter the GitLab username or public email, upload it.
- The recipients confirm – everyone receives an email and has to accept the assignment in GitLab by selecting Approve reassignment. Only then are the contributions transferred. For later imports from the same source into the same group, the mapping happens automatically.
Always import into a group, never into a personal namespaceIf you import a project into a personal namespace, no contribution and membership mapping takes place – every contribution is attributed to the owner of that namespace and cannot be split up afterwards. So create a group first and import into it.
Registration is disabled by defaultOpen registration is off by default in your instance at server.camp. You create users as an admin under Admin area → Overview → Users – or you connect single sign-on right away and let accounts be created on first login.
GitLab lists which objects direct transfer covers under migrated items. In practice, you will have to redo the following after the import:
| Follow-up work | What to do |
|---|---|
| CI/CD variables | Re-enter them in the new instance (check both project and group level) |
| Runners | Register again – see Getting started |
| Container registry images | Rebuild and push, or pull from the old registry and push again |
| Package registry | Publish packages again |
| Deploy keys and deploy tokens | Create anew and swap them in the target systems |
| Webhooks | Recreate them, including their secrets |
| Job logs and job artifacts | Do not come along. The pipeline history does migrate as a record – the logs and artifacts of the jobs do not. Download them from gitlab.com beforehand if you need them. |
| Environments and feature flags | Recreate them |
| Merge request approval rules | Reconfigure them |
Running both instances means two versions of the truthAs long as work continues on gitlab.com, commits, issues and comments accumulate there that the import will not pick up afterwards. So set a cut-off date after which everyone pushes to the new instance only – and archive the old projects once you pass it.
If you have been running GitLab yourself (Omnibus package, Docker or Kubernetes), we take the complete route: you create a backup, we restore it on our side. The result is a 1:1 copy of your instance – including users, 2FA settings, CI/CD variables, tokens, registry and LFS objects.
The process and the files match GitLab’s official guide Migrate to a new server.
We agree on a time slot with you – ideally half a day – during which you can do without your GitLab and we can restore and verify the backup without rushing.
- You make your instance read-only so the backup is consistent and nothing is written afterwards.
- You create the backup and hand the files over to us – described step by step under how to create the backup.
- We restore the data into your new instance and verify it – among other things with
gitlab-rake gitlab:doctor:secretsandgitlab-rake gitlab:check. The secrets check is the real proof of success: if it reports errors, the key material does not match the database, and we will not go live but sort it out with you. - We release the instance, you sign it off.
- You switch DNS and your Git remotes. The old instance stays shut down so nobody accidentally pushes into two systems.
In essence, we need four files:
| File | Contents |
|---|---|
<timestamp>_gitlab_backup.tar |
All GitLab data: database, repositories, uploads, LFS, job artifacts, pages, registry. |
Redis backup (dump.rdb) |
Sessions and background jobs not yet processed. Optional – see the note below. |
/etc/gitlab/gitlab.rb |
Your instance configuration. We use it as a reference to carry over functional settings. |
/etc/gitlab/gitlab-secrets.json |
Your instance’s key material. Without this file, CI/CD variables, 2FA secrets and tokens become unusable. |
gitlab-secrets.json is the critical pieceA restore without the matchinggitlab-secrets.jsonlooks successful at first glance – but none of the encrypted values in the database can be decrypted any more. As a consequence, 2FA for every user, every CI/CD variable and every token would have to be set up again. At that point it is no longer a move, it is a rebuild. So secure that file first.
Why there are four filesThe backup archive deliberately contains no configuration and no key material – which is exactly whygitlab.rbandgitlab-secrets.jsoncome along separately from/etc/gitlab. Redis is not in the archive either, which is why the official guide covers the Redis dump as its own step (flush Redis to disk, then hand overdump.rdb). Without the Redis dump you lose active sessions – every user has to log in once more – and the background jobs still sitting in the queue. That is not a blocker, but it is cleaner with it.
What the archive does contain: the database, Git repositories and LFS objects, uploads and attachments, wikis, snippets, CI/CD artifacts and job logs, external merge request diffs, package registry and container registry images, secure files, Terraform states and GitLab Pages content. So path B brings across considerably more than direct transfer does.
A backup only fits the exact same version and editionGitLab is unambiguous about this: a backup can only be restored into exactly the same version and the same type (CE or EE) it was created on. We need to sort out both before the maintenance window, not during it.
- Find your version: open
/helpin your instance or rungitlab-rake gitlab:env:info. - Ask for our target version: we currently host GitLab 19.2, Community Edition. Because we update versions regularly, our support team will tell you the exact version at the time of your migration.
- Are you older? Then upgrade your instance to our version before taking the backup, following the official upgrade path. That is the preferred route: upgrades then happen on your system rather than under time pressure inside the maintenance window.
Enterprise Edition: please raise this earlyPlease let us know in advance if you use the Enterprise Edition. Depending on whether you use the Community Edition or the Enterprise Edition with us, that decides early on how the export and import will run. In principle, though, every route is possible.
The following steps match the official guide and apply to an instance installed from the Linux package (Omnibus). Docker and Kubernetes differ – see the note below. Run them at the start of the maintenance window, in this order.
Step 1 – Stop allowing new writes. On Premium or Ultimate, enable maintenance mode under Admin area → Settings → General.
Step 2 – Flush Redis to disk and stop the services. Only PostgreSQL and Gitaly need to keep running for the backup itself:
sudo /opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket save && \
sudo gitlab-ctl stop && \
sudo gitlab-ctl start postgresql && \
sudo gitlab-ctl start gitaly
Step 3 – Create the backup:
sudo gitlab-backup create
The archive ends up in /var/opt/gitlab/backups/ and is named after the pattern <backup-id>_gitlab_backup.tar, for example 1784030400_2026_07_26_19.2.0_gitlab_backup.tar. On large instances the run can take hours – plan for that in the maintenance window, and make sure there is enough free space under /var/opt/gitlab/backups/ for the complete archive.
Step 4 – Collect the four files:
/var/opt/gitlab/backups/<backup-id>_gitlab_backup.tar
/var/opt/gitlab/redis/dump.rdb
/etc/gitlab/gitlab.rb
/etc/gitlab/gitlab-secrets.json
Docker and KubernetesIf your GitLab runs in Docker, the backup command isdocker exec -t <container name> gitlab-backup create; the four files then live in the mounted volumes (config/forgitlab.rbandgitlab-secrets.json,data/backups/anddata/redis/for the archive and the Redis dump). On Kubernetes/Helm you create the backup withkubectl execrunning thebackup-utilityscript on the toolbox pod – see the charts backup documentation. On a self-compiled installation it issudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production. If you are unsure which case applies to you, just ask us beforehand.
Rehearse it once beforehandYou can safely runsudo gitlab-backup createbefore the maintenance window while the instance is live. Such a backup is not consistent enough for the actual move, but it tells you how long the run takes and how large the archive gets – we need both for planning.
The tar archive is typically several to many gigabytes in size. We provide an upload area for the handover (Nextcloud or Seafile), or you use scp/rsync into a directory we prepare for you.
gitlab-secrets.json and gitlab.rb contain secrets in plain text. Hand them over separately from the archive and encrypted – for example as an entry in a shared Vaultwarden vault, via Hemmelig or GPG-encrypted.
We configure your instance at server.camp on the platform side – reverse proxy, certificates, ports, backups, monitoring and performance parameters are derived from your plan. A gitlab.rb dropped in by hand would be overwritten on the next deployment.
So we read it, go through it with you where needed and decide setting by setting:
Setting in your gitlab.rb |
At server.camp |
|---|---|
external_url, nginx, ports, SSH port |
We set these on the platform side – see Your own domain |
| SMTP | Our mail delivery by default; your own mail server is configurable in the portal |
| Container registry | Your own registry domain via the customer portal |
| Performance (Puma, Sidekiq, PostgreSQL tuning) | Derived from the resource allocation of your plan |
| Backup, monitoring, logging | Handled automatically by the platform |
| LDAP, SAML, OIDC and other functional settings | We carry these over – as configuration of your subscription in the portal |
That is why you should send us the gitlab.rb early, not on moving day: anything to be carried over is a change to your subscription – and we plan those together before the maintenance window.
These points apply to both paths:
- Your own domain: if you want to keep using your previous domain, we will set it up together with you – see Your own domain.
- Update Git remotes: your repositories may live under a new domain. GitLab shows the correct clone URL in every project under Code → Clone. Your developers update their remotes with
git remote set-url origin <new-url>. - Git over SSH: we publish Git-over-SSH on a dedicated port per instance, not on port 22. The port is part of the SSH clone URL GitLab displays (
ssh://git@…:<port>/…); alternatively you set it once in~/.ssh/config. Cloning over HTTPS is unaffected. - SSH host keys (path B only): on request we carry over the host keys of your old instance. Your developers then see no “REMOTE HOST IDENTIFICATION HAS CHANGED” warning on the first push. Just tell us in advance whether you want that.
- Check runners: on path A runners always have to be registered again, on path B usually not. After the move, check under Settings → CI/CD → Runners whether your runners are online.
- Re-register hardware keys and passkeys (path B only): WebAuthn registrations are cryptographically bound to the old domain. If your instance runs under a new domain after the move, the registered devices are disabled and have to be registered once more. Time-based one-time codes (TOTP apps) are unaffected – they keep working as long as
gitlab-secrets.jsoncame along. - Registry references: with the new registry domain, Docker logins as well as the
image:entries in.gitlab-ci.ymland in your deployment manifests change. - Backups and monitoring run automatically from the first deployment onwards. There is nothing for you to set up.
- The old instance: leave it shut down – but not yet deleted – until you have signed off the migration and worked productively on the new instance for a few days.
Write to us via the customer portal if you want to move. The following details help us give you a useful first answer:
- Is your GitLab on gitlab.com or is it self-hosted?
- Which GitLab version are you running (
/helpin your instance)? - Are you running Community Edition or Enterprise Edition? (
/helpshows this too.) - Roughly how large is your data set (number of projects, backup size)?
- Do you have a preferred date for the maintenance window?
For a direct transfer from gitlab.com you can start right away without asking us – just get in touch if anything does not work as expected.