GitLab incremental repository backups

Incremental repository backups shorten backup windows on large self-managed GitLab instances by capturing only changes since a previous backup. This page explains how increments chain, what stays full, and why deleting an increment breaks restores.

On this page

When GitLab incremental repository backups shorten the backup window

Incremental repository backups shorten the backup window when most of the run time goes to copying Git repositories that have not changed since the last backup. Instead of re-archiving every repository on each run, an incremental run captures only repository changes made since a previous backup, which GitLab identifies by its backup ID. Before you adopt them, measure where the time goes. Read the timing for each component in the backup output on a full run. If the database dump, uploads or object-storage handling dominate, incremental repository backups will not shorten the window much, and the bottleneck needs a different fix.

Each increment depends on the one before it. An incremental archive records the backup ID it builds on, so a restore needs the full chain back to the base backup. Deleting or losing any increment in the chain breaks every later restore that depends on it, which is why retention rules must delete whole chains rather than individual files. The GitLab backup and restore documentation describes the commands and variables for creating an incremental run and naming the previous backup, including the INCREMENTAL and PREVIOUS_BACKUP settings. Confirm the exact syntax for your installed version in that documentation, and test a restore of the full chain in a non-production environment before you rely on it.

Choose increments when repository data is large, changes slowly, and the backup window is the constraint your team must meet. Keep full backups on a schedule that caps the chain length, and document who decides when a new base is taken. For very large instances, compare the approach with disk snapshots, which need a planned read-only period, and with Geo, which provides a warm standby but still needs backups for recovery from logical errors. The GitLab backup and restore documentation is the next read for the component-by-component behavior that these choices depend on.

How increments chain to a previous backup ID

Each incremental repository backup records the ID of the backup it builds on, so a restore is only possible while every link in that sequence is present, running from the newest increment back to a full repository backup. The chain is the unit you protect. An increment on its own holds only the repository changes made since its parent, so it cannot rebuild a repository without everything before it. Before you rely on incrementals, confirm in the GitLab backup and restore documentation for your version how the backup ID is recorded, which parameters select an increment, and how the restore task locates the parent. Those details are version specific and should be checked against your release rather than assumed from older guidance.

The backup archive produced for each run carries the repository data for that run along with the metadata that identifies its place in the chain. Your own records must keep the whole sequence together, so the backup ID of every archive and its parent must survive in the same storage location as the archives. Naming and retention decide whether that stays true. Use a naming scheme that keeps timestamps and IDs legible, and set retention by chain, never by file age: a full backup that looks old may still be the base for the newest increments, and pruning it silently makes every dependent increment unrestorable. Store the gitlab-secrets.json file and gitlab.rb alongside the chain, because a restore needs matching secrets as well as the archives.

Test the chain on a non-production instance before you need it. Restore the newest increment, confirm the parent IDs resolve, and check that repository counts and sample pushes match expectations. Write a retention rule that refuses to delete any archive still referenced by a retained increment, and alert when a parent in the sequence goes missing. Ask your service provider or internal team to show exactly how they count the chain and what happens when a link is lost, since that answer determines your recovery point and the effort of a rebuild.

Backup options and when each fits

This table compares the main ways to protect Git repository data on self-managed GitLab and how each affects the backup window and restore. Read the restore dependency column first, because an incremental backup is only usable when every backup it chains to is still present. Full backups stand alone, while increments depend on their predecessors.

OptionWhat it captures and window effectRestore dependencyWhen it fits
Full repository backupCaptures every repository on each run. The window grows with total Git data because all of it is read each time.Standalone. One full backup can be restored without earlier backups.Smaller instances, or where a self-contained archive per run is the simplest policy to audit.
Incremental repository backupCaptures only changes since a previous backup, identified by backup ID. Runs are shorter after the first full backup.Chained. Each increment points to the previous backup ID, so deleting an earlier increment breaks restores that depend on it.Large instances where full runs no longer fit the window and retention keeps the whole chain intact.
Server-side repository backup to object storageGitaly streams repository data straight to object storage without passing through the backup archive. Reduces load on the backup host.Depends on the bucket holding the repository backups. Confirm how restores read from it before relying on it.Instances where network transfer or archive size is the bottleneck and object storage is already in the platform.
Disk snapshotsCopies the underlying volumes at one point in time. Window depends on whether GitLab is placed in read-only mode during the snapshot.Restores the volume set as a unit. Confirm consistency across Git data, database and secrets with the GitLab documentation.Large instances where a volume-level recovery point is acceptable and snapshots can be coordinated across all components.
Geo secondary siteContinuously synchronized warm standby in another region. It supports disaster recovery but does not shorten backup runs by itself.Replicates changes as they happen, so earlier recovery points still need backups.Organizations that need a warm standby site alongside backup archives, on Premium or Ultimate.

What stays full and what the increment does not cover

Incremental repository backups change one part of the GitLab backup run: how Git repository data is captured. The PostgreSQL database is still backed up in full on every run. It holds issues, merge requests, CI/CD job records, user accounts and the mapping between projects and their repositories, so a restore needs a database copy that matches the repository state in the same archive. Before you cut a window with increments, confirm in the backup documentation for your GitLab version which other components your instance writes to local disk, such as uploads, job artifacts and pipeline logs, and check whether each one is included in the full archive. Those components do not benefit from increments, so their size and run time set a floor for the window you can reach.

Two categories sit outside the repository path entirely. The first is configuration and secrets. The gitlab-secrets.json file and gitlab.rb are not part of the backup archive, and a restore without them can leave encrypted database values unreadable. They need their own copy, which is covered in the sibling topic on backing up GitLab secrets and configuration. The second is data offloaded to object storage. Artifacts, LFS objects, uploads and registry blobs stored in S3, Google Cloud Storage or Azure Blob Storage are skipped by the backup command, so an increment does nothing for them. Protection for that data comes from provider versioning and replication, as described in the sibling topic on backing up GitLab data in object storage.

Turn this into a short inventory before you change the schedule. List every data location on your instance, mark each as local disk or object storage, and record where the secrets file is kept. Then run a full restore into a test environment on the matching GitLab version and edition, and time each stage. The total restore time shows whether increments meet your recovery target, and any gap points to a component you have not covered. The GitLab backup and restore documentation is the next read for the exact archive contents and restore steps for your release.

How to set up incremental repository backups safely

This sequence turns on incremental repository backups without weakening restores. It confirms the version and install method, protects secrets and configuration, establishes a verified full baseline, and then adds increments whose chain you can prove and restore from.

  1. Confirm version and installation method

    The administrator records the exact GitLab version, edition and installation method, whether Linux package, Helm chart, GitLab Operator, Docker or source. Incremental behavior and restore rules depend on the release, so check the chosen version against the backup and restore documentation before changing anything. Confirm that the backup host has enough free disk space for a full baseline plus increments, and that a restore target can run the same version and edition. Done when the version, edition and install method are written in the runbook.

  2. Back up secrets and configuration first

    Copy gitlab-secrets.json and gitlab.rb to a location separate from the backup archives, with access limited to named administrators. The secrets file holds the keys that encrypted data depends on, so a restore without it can leave parts of the instance unreadable. Store the copies in a vault or encrypted location, and include any SSH host keys and TLS material your deployment uses. Done when a second administrator can locate both files, open them and confirm the checksums match the originals.

  3. Take and verify a full baseline

    Run a full repository backup before enabling increments so the chain has a complete starting point. Record the backup ID the run produces, then store the archive and its checksum alongside the baseline record. Review the run log for errors and warnings, and confirm the archive contains the repositories you expect. Investigate any warning before this backup becomes the base for an increment chain. Done when the baseline ID, timestamp and checksum are logged and the archive has been listed or inspected successfully.

  4. Enable incremental mode as documented

    Apply the incremental repository backup settings described in the GitLab backup and restore documentation for your version, changing only what that page specifies. Keep a dated copy of gitlab.rb before editing, and apply the change with the standard reconfigure process. Confirm the exact configuration keys with the documentation or your support contact before deploying them. Done when the next scheduled run shows incremental output that points to the baseline backup ID.

  5. Schedule runs and record backup IDs

    Schedule full and incremental runs at a cadence that matches your recovery objectives, and write down when a new full backup replaces the existing base. Record each backup ID with its timestamp, type, parent ID and storage location in a log the team can query. Increments depend on their predecessors, so retention jobs must never delete a backup that a later increment references. Done when the log shows an unbroken chain from the latest full backup through the newest increment.

  6. Test restore into a non-production instance

    Restore the newest chain into a separate, non-production GitLab instance running the same version and edition, restoring secrets and configuration first. Check that projects, merge requests, pipelines and repository contents match what production held at the backup point. Repeat the test from an older point in the chain, since a broken link often shows up only there. Record the elapsed time and every manual step as input for the runbook. Done when both restore tests pass and the team holds a dated record of the results.

Security and operational risks of backup chains

An incremental chain is only as strong as its weakest link. The risks below come from the chain itself, from the material stored alongside it, and from the people and monitoring around it. Each one is something to design for before the first increment runs, not after an incident.

Pruning an increment breaks restores

Each incremental repository backup references a previous backup ID, so removing a middle increment leaves later ones unusable without the full chain beneath them. Retention jobs that count files rather than chains are the usual cause. Before enabling any pruning, confirm that your cleanup logic deletes whole chains from the full backup forward, and test a restore from the oldest retained chain.

Encrypt and restrict archive access

Backup archives contain repository content, database records and user data, so they need the same access controls as the live instance. Confirm that the storage bucket or file share is limited to the backup service account and the restore operators, and that encryption at rest is enabled. Treat the archive location as a sensitive system, not a convenience copy.

Keep secrets with the backups

Repository archives alone cannot restore a working instance. The gitlab-secrets.json file and gitlab.rb hold the keys used to decrypt stored data, so an increment chain restored without them may leave encrypted values unreadable. Store these files in a separate, access-controlled location that is backed up on the same schedule, and record which secrets version matches which backup.

Retention drift between locations

When backups are copied to more than one storage location, each location can apply its own retention rules and fall out of step. One site may keep a full chain while another has only recent increments, which looks healthy until a restore is needed. Compare the oldest restorable chain in every location on a schedule, and align retention settings across them as a single policy.

Monitor failed and skipped runs

A backup job that fails quietly can leave a gap in the chain, and the next increment may still look successful. Alert on non-zero exit codes, on missing daily runs, and on any increment whose expected predecessor is absent. Check the backup logs as part of routine day-two operation, not only when someone asks for a restore.

Document who can restore

A restore from a chain needs people who know the order of operations, the matching GitLab version and edition, and where the secrets file lives. Name the operators, record their access to the archive and to the secrets, and keep a written restore runbook that is tested. If the only person who understands the chain is unavailable during an incident, the backups do not protect the service.

Questions teams ask before changing their backup chain

How much effort and time does moving to incremental backups take?

The effort depends on your instance, not on a fixed number of days. The main drivers are the number of repositories and their total size, how many storage nodes hold Git data, the retention policy you already run, whether backup jobs are scripted or driven by your scheduler, and how many test restores you need before cutover. No single duration applies to every instance, so treat any one figure with caution until the environment has been inspected. Ask for the work broken into phases: inventory of current backups, design of the full and incremental schedule, a test restore on a non-production instance, the production change and handover of runbooks. Ask which phases are fixed in the quote and which depend on what the inspection finds.

What makes the cost of an incremental backup change go up or down?

Cost has three main parts: internal staff time, any external fee for the written scope, and the infrastructure used for test restores. The size of the instance, the number of storage nodes and the number of restore tests you require usually move the total the most. Your GitLab edition matters only if it changes the backup tooling you run, so confirm that with the vendor before budgeting. GitLab's pricing page lists Free at 0 US dollars and Premium at 29 US dollars per user per month billed annually for self-managed installations, which is a licensing line separate from the backup work. Most of the cost is one-time. Monitoring, scheduled full backups and periodic restore tests recur, so ask a quote to state which items are one-time and which are ongoing.

Can our own platform team change the backup chain without outside help?

Yes, a platform team can make this change alone if it already administers Linux servers, runs GitLab's backup command, controls the backup storage and has a non-production instance for testing. The step teams most often skip is the restore test, and an untested chain is the most common reason an incremental setup fails when it is needed. Your team should own the schedule, the retention rules and the monitoring that confirms each backup finished. If any of those skills are thin, a partner can carry the design and test work while your staff take over the runbooks. Decide before starting who will run the chain after handover, because the chain needs someone watching it every week.

What access and environment do we need to provide for this work?

The work needs a short, specific set of inputs before it starts:

  • Administrator access to the GitLab server, including the current gitlab.rb configuration and the location of your backup archives.
  • A non-production GitLab instance on the same version as production, where increments can be created and restored without touching live data.
  • Access to the storage target that holds backup archives, with enough capacity to keep a full backup and its increments while testing.
  • A maintenance window if the cutover needs one. Confirm with the vendor's upgrade and backup documentation whether any step requires downtime on your version.

Ask the quote to list each input, who provides it and the date it is needed, so that delays on your side are visible in the schedule.

What does a fixed-scope engagement for backup chains cover?

A fixed-scope engagement covers the work written into the agreed scope, and the scope, deliverables and timeline are agreed in writing before work starts. For backup chains, that scope typically names the instance and storage in play, the target schedule for full and incremental backups, the retention rules, the configuration changes, the test restores that will be performed and the runbooks handed over. It should also name what is excluded, such as object storage artifacts, Geo or disaster recovery design, if those are not in the agreement. DeployOpen prepares systems, controls and evidence, but does not issue audit reports or certifications. Ongoing support, upgrades and testing can be added as a retainer, and that cost recurs.

What should we do if an increment was already deleted?

Stop any pruning or cleanup job first, because every increment depends on the backup ID it chains to, and further deletions can make more of the chain unusable. Then list the archives that remain and identify the most recent full backup and the increments that still link back to it. Test a restore of the newest set that you can prove is complete, on a non-production instance, before relying on it. If the chain is broken, take a new full backup as soon as the instance is stable, restart the increment schedule from that point and record the window of data that cannot be recovered from the chain. Check the backup and restore documentation for your version before running any command, and consider a scoped review by a specialist if the chain is critical.

Does an incremental backup chain still need regular full backups?

Yes. An increment only has meaning relative to an earlier backup, so the chain always starts from a full backup, and each increment records the ID of the backup it extends. A periodic full backup limits how long a chain grows and how many archives a restore must read, and it gives you a clean restore point if an increment is damaged. The right interval depends on your repository count, change rate, storage and recovery targets, so agree it with your team and test it. Confirm the exact commands, flags and chain behavior for your installed version in the GitLab backup documentation before you schedule anything.

Do we need a tested restore before we rely on incremental backups?

Yes. A backup that has never been restored is an assumption, and an incremental chain adds dependencies that a single full archive does not have. Restore testing should run the whole chain, from the full backup through each increment, into a non-production instance, and the result should be written down with the version, date and archive IDs used. Keep those records as evidence for internal reviewers and external assessors. Repeat the test after any change to the schedule, the storage target or the GitLab version. DeployOpen prepares systems, controls and evidence for compliance assessments, but independent auditors and certification bodies issue the reports.

Testing restores before an incident forces the question

A restore test has to replay the full chain, not just the most recent increment. An incremental repository backup records the backup ID of the backup it extends, so restoring the last increment means restoring every earlier link back to the full backup it depends on. A test that restores only the newest archive can pass while the real recovery fails, because the chain was never exercised end to end. Before the test, confirm with your backup tooling which full backup starts each chain and list every increment that points back to it. Run the restore against that complete set, on a host built to the same GitLab version and edition as the source instance, following the procedure in the GitLab backup and restore documentation.

Record the facts that make the test reproducible and useful to the next person. Keep the backup ID of every archive used, the GitLab version and edition on both the source and the test host, the Gitaly and database versions reported on the source, the start and end time of each restore step, and the total elapsed time. The elapsed time is your measured restore window, and it is the number to compare against the outage your business can tolerate. Store the record with the backup set itself, so an auditor or responder can see which chain was proven and when. If a step fails, record the exact error and the backup ID involved, since that is the detail the next attempt needs.

Verify the restored instance against the source rather than trusting that the restore command finished. Compare project and repository counts, confirm that a sample of repositories clones and that recent commits match the source's refs, and check that merge requests, CI pipelines and users appear as expected. Confirm that gitlab-secrets.json was restored alongside the data, because encrypted values will not decrypt without it. Use the built-in checks that GitLab provides for the installation, then run a few representative user workflows, such as a push, a pipeline and a web login. Repeat the test on a schedule, because a chain that restored cleanly last quarter can break after a configuration change or a version upgrade.

How DeployOpen scopes and delivers GitLab backup work

DeployOpen plans, implements, tests and documents incremental repository backup designs for self-managed GitLab inside the client's own infrastructure. The work starts with a written scope that fixes which backup chains, storage locations and restore tests are covered before any configuration changes are made.

How we scope

  • Inventory current backup windows, repository sizes and the storage each backup target uses today.
  • Confirm the GitLab version and edition, since restore rules depend on matching both.
  • Agree in writing which backups stay full, which become incremental and how the chain is retained.

How we build

  • Configure incremental repository backups and record the backup ID chain in a documented retention schedule.
  • Block deletion of increments that a retained backup depends on, using storage policies you control.
  • Run timed restore tests from a full backup plus its increments on a separate host before sign-off.

How we hand over

  • Deliver runbooks that show how to identify each increment's parent backup ID before any cleanup.
  • Document the restore sequence, including restoring gitlab-secrets.json and gitlab.rb first.
  • Provide the test results, configuration files and an owner list for each backup job.

How we support

  • Available as a retainer for reviewing backup chains after GitLab upgrades and configuration changes.
  • Re-run restore tests on a schedule agreed in the retainer scope, with results reported back.
  • Advise on changes when backup windows, storage or GitLab versions shift, within the agreed scope.

Talk to our team.

Tell us what you're working on, whether it's a deployment, an audit, a security test or a cyber range. You'll speak with an engineer who can help you scope it.

  • 30-minute call: free, with no obligation.
  • NDA on request: we can sign before you share details.
  • Clear next steps: a scope and plan after the call.