About this article: AI-assisted writing, reviewed against the cited sources. This is source research, not a lab validation of the procedures. Check the stated versions and your own recovery requirements before making changes.
A self-managed GitLab server holds a business's source code, and also its issues, merge request discussions, CI/CD pipelines, package registry and the credentials those pipelines use. GitLab provides a backup command that gathers most of this into a single archive. Most, not all, and the parts left out are the ones that make the rest usable.
This guide follows GitLab's own documentation to explain what the archive contains, what must be kept beside it, and the rules a restore must meet.
Why it matters
Git itself makes it easy to believe the code is safe: developers may hold repository clones. But an ordinary project clone does not include the GitLab database, merge-request reviews, protected CI/CD variables, uploads or access settings. Wikis are separate Git repositories and need their own coverage; a project clone is not a complete GitLab backup. Those live in GitLab's database and storage.
The overview page lists the reasons for a recovery plan, among them protection against "hardware failures, software bugs, or accidental deletions". It also notes that these methods apply to self-managed installations: "you can't use these methods to export or back up your data from GitLab.com."
How it works
What the backup archive contains
The Back up GitLab page says that by default "the backup creates an archive in a single compressed tar file." It lists what that file includes:
- "Database data and configuration"
- "Account and group settings"
- "CI/CD artifacts and job logs"
- "Git repositories and LFS objects"
- "External merge request diffs"
- "Package registry data and container registry images"
- "Project and group wikis"
- "Project-level attachments and uploads"
- "Secure Files", "GitLab Pages content", "Terraform states" and "Snippets"
The database matters most. The page describes PostgreSQL as "the single source of truth for most user-generated content in the Web interface, such as issue and merge request content, comments, permissions, and credentials."
What the archive leaves out
The same page lists "Data not included in a backup":
- "Redis (and thus Sidekiq jobs)"
- "Object storage on Linux package (Omnibus) / Docker / Self-compiled installations"
- "Global server hooks" and "File hooks"
- "GitLab configuration files (/etc/gitlab)"
- "TLS- and SSH-related keys and certificates"
- "Other system files"
Two of these need particular attention.
Object storage. Many installations keep uploads, artifacts, LFS objects or registry images in an object store. On Linux package, Docker and self-compiled installations, that data is not in the archive and must be backed up separately. The page's "Simple backup procedure" makes this an explicit step: "Back up object storage, if applicable."
Redis and background jobs. The page says "The provided backup command does not back up Redis data. This means that in order to take a consistent backup with the backup command, there must be no pending or running background jobs."
It also notes that Elasticsearch, if used for advanced search, is not backed up, but its data "can be regenerated from PostgreSQL data after a restore."
The secrets file, and why it is kept apart
The configuration is left out on purpose. Under "Storing configuration files" the documentation explains: "your database contains items including encrypted information for two-factor authentication and the CI/CD secure variables. Storing encrypted information in the same location as its key defeats the purpose of using encryption in the first place."
It continues: "the secrets file contains your database encryption key. If you lose it, then the GitLab application will not be able to decrypt any encrypted values in the database. Additionally, the secrets file may change after upgrades."
For a Linux package installation, the minimum to back up is /etc/gitlab/gitlab-secrets.json and /etc/gitlab/gitlab.rb. Self-compiled, Docker and Helm installations keep the equivalent files in other places, which the page lists.
The troubleshooting page shows what losing it means. Without the key, "GitLab can't decrypt those columns, preventing access to" items including CI/CD variables, runner authentication, project mirroring, integrations, webhooks and deploy tokens. Symptoms include "Stuck jobs" and "500 errors". The recovery path resets those tokens, and the page warns that the steps "can potentially lead to data loss on the previously listed items."
Our reading: the archive without the secrets file restores the code and issues, but not the working system. Every pipeline credential and integration may need to be recreated by hand. Our recommendations:
- Back up the secrets file and configuration every time the archive is backed up, and again after every upgrade, because the file "may change after upgrades".
- Store them separately from the archive, as the documentation intends, but make sure both can be found together at restore time.
- Protect the secrets file like a master password: encrypted at rest, with access limited to the people who perform restores.
The same-version rule
The Restore GitLab page is unambiguous: "You can only restore a backup to exactly the same version and type (CE or EE) of GitLab on which it was created. For example, CE 15.1.4." If the versions differ, "you must downgrade or upgrade your GitLab installation before restoring the backup." The restore command checks this and stops with a version mismatch error.
Our reading: this turns version records into part of the backup. When the server is lost, you need to know which exact version and edition to install before anything can be restored. The backup file name in the documentation's example includes the version (..._10.6.4-ce_gitlab_backup.tar), but our recommendation is to record it separately as well, alongside the installation method. Keep the required installation packages or a verified retrieval route, so the recovery does not depend on finding an old release during an outage.
Configuration that must match
The restore page adds that "Certain configuration is coupled to data in PostgreSQL." Its example: if the original had three repository storages with particular names, "the target environment must also have at least those storage names defined in configuration."
It also warns that restoring a backup taken on local storage "restores to local storage even if the target environment uses object storage. Migrations to object storage must be done before or after restoration." Restoring into directories that are mount points requires them to be empty first.
What a restore does to existing data
The restore page explains how existing data on the target is treated:
- "PostgreSQL data is automatically erased during the restore process."
- Git repositories with the same name cause the restore to fail with a "repository already exists" error.
- "File system data is moved to a separate directory before restoration, if possible."
- "Object storage data is not automatically cleared."
Before the restore command, the documentation warns in bold that it "overwrites the contents of your GitLab database!" It also recommends: "For a reliable restore process, for example when automating production-to-staging restores, use a fresh GitLab installation at the same version as the backup."
Our recommendation: restore onto a fresh server of the exact version wherever you can, never onto the damaged production server as a first attempt. That keeps the original available for investigation and avoids the overwrite.
Space and timing
Before extracting, GitLab checks that the backup directory has "free space equal to at least three times the size of the archive." The page calls this "an estimate", so a restore can still run out of space.
For backup, the documentation's simple procedure is aimed at installations with "a PostgreSQL database smaller than 100 GB" where the backup command "remains practical". It notes that the backup database step uses pg_dump, and that as the database grows, the duration "becomes impractical at some point". Larger installations have a separate guide for "external databases, object storage, or distributed repository storage", which backs up each component with its own tools.
The default backup streams data into the archive. A copy strategy is available that copies data first, at the cost of "up to an additional 1X disk space."
Old archives are pruned according to backup_keep_time, but the page says this "manages only local files". GitLab "doesn't prune old files stored in a third-party object storage", so set retention on that storage yourself.
Installations with several servers
Where GitLab runs across several servers, the backup page says "you need to pick a node for running the backup command." That node must be "persistent, and not subject to auto-scaling", must have the GitLab Rails application installed, and must have "sufficient storage and memory to produce the backup file."
Our reading: in environments that add and remove servers automatically, a backup scheduled on whichever server happens to exist can silently stop running. Our recommendation is to name the backup node in the recovery plan, monitor that each night's archive actually appears in its destination, and alert when it does not.
Checking the result
Define the acceptance record before the rehearsal: the backup identifier, exact GitLab version and edition, storage inventory, secrets-backup reference, omitted components and the point at which writes were stopped. Record which repositories, LFS objects, artifacts and application records were checked. A completed restore command establishes only that its own work completed; it cannot prove that an excluded object store, hooks or external service was protected. Keep the original backup set and configuration separately from the working restore, so a failed validation does not consume the only recovery path.
After a restore, the Restore GitLab page suggests a secrets check, "especially if /etc/gitlab/gitlab-secrets.json was restored, or if a different server is the target for the restore", plus integrity checks for artifacts, LFS objects and uploads.
The migration guide adds practical checks that apply to a restore on new hardware. Compare background job counts with the old server, test "read-only operations", such as browsing repositories, merge requests and issues, before opening the server to users, and only then update DNS. It also warns to "Avoid uncoordinated data processing by both the new and old servers". For example, if both instances process incoming email at the same time, "both instances miss some data."
What to choose and why
These are our recommendations, drawn from the documentation.
- Back up three things every time: the backup archive, the configuration including the secrets file, and object storage where it is used.
- Record the exact version and edition with every backup, and keep a way to install that version.
- Keep the secrets file separate but findable, encrypted and access-controlled, refreshed after every upgrade.
- Quiet background jobs before the backup, as the documentation requires for consistency.
- Set retention on remote backup storage yourself.
- Restore to a fresh installation of the same version, with matching storage names and the restored configuration in place first.
- Prevent both servers acting at once during a restore on new hardware.
- Run the documented checks and validate browsing, repository access and an isolated test pipeline using harmless test inputs. Keep production runners, mail delivery and external integrations disconnected until the restored system is approved for service.
- Rehearse on a schedule, and move to the large-environment approach when backups take too long.
Risks and limits
- The archive is incomplete on its own: configuration, secrets, object storage, Redis and hooks are outside it.
- Losing the secrets file breaks encrypted values; the recovery path can lose data.
- Restores need exactly the same version and edition.
- The restore erases the target database; use a fresh installation.
- Object storage is not cleared on restore, which can leave orphaned data.
- The simple procedure suits smaller databases; large installations need component backups.
- Nothing here was run. Behaviour is quoted from GitLab's documentation for planning; rehearse on a separate server.
Sources
- GitLab Docs, Back up and restore overview: https://docs.gitlab.com/administration/backup_restore/
- GitLab Docs, Back up GitLab: https://docs.gitlab.com/administration/backup_restore/backup_gitlab/
- GitLab Docs, Restore GitLab: https://docs.gitlab.com/administration/backup_restore/restore_gitlab/
- GitLab Docs, Troubleshooting GitLab backups: https://docs.gitlab.com/administration/backup_restore/troubleshooting_backup_gitlab/
- GitLab Docs, Migrate to a new server: https://docs.gitlab.com/administration/backup_restore/migrate_to_new_server/
- GitLab Docs, Back up and restore large GitLab environments: https://docs.gitlab.com/administration/backup_restore/backup_large_reference_architectures/
This article was originally published on .