GitLab zero-downtime upgrades

A planning guide to upgrading multi-node self-managed GitLab without an outage, covering the required upgrade stops, HA prerequisites, component order and the effort drivers that set the schedule.

On this page

When a GitLab zero-downtime upgrade is worth planning

Plan a zero-downtime upgrade when your self-managed GitLab serves users who cannot accept a maintenance window and the instance is several versions behind the release you need. You cannot jump there in one step. GitLab requires upgrades to pass through required upgrade stops, which since GitLab 17.5 fall at versions x.2, x.5, x.8 and x.11, so a move from 18.0 to 19.2 runs through 18.2, 18.5, 18.8, 18.11 and then on to 19.2. Bug fixes are backported only to the current stable release, so waiting on an older line means you will not get fixes there. Treat the path as one minor release at a time, and confirm each hop against the GitLab upgrade paths documentation before you schedule anything.

Zero-downtime is realistic only on a topology built for it. GitLab's reference architectures start at 1,000 users, and the documentation generally recommends a high availability strategy for 3,000 or more users. That strategy uses Gitaly Cluster with Praefect for repository storage, PostgreSQL with Patroni, Redis Sentinel and Consul. Each stateful layer needs a working failover path and enough redundancy that one node can be taken out of service while the others keep serving. If your current instance is a single Linux package node or a single database, the first decision is whether to build that HA layer before the upgrade, because the upgrade procedure assumes it exists.

Before you commit to the project, confirm three things with your own team: which reference architecture you actually run, whether your load balancer can drain and re-add nodes during a rolling change, and whether your last backup restores cleanly onto a separate host. Also check that your version gap does not cross a release that changes database or runtime prerequisites, since those add work beyond the procedure itself. If any of these checks fail, the upgrade is a prerequisite project in its own right, and a fixed-scope written plan from a partner can separate that work from the upgrade so each has a clear exit.

What changes across the 18.x and 19.x releases and what to do about it

This table lists the release mechanics that shape a multi-node GitLab upgrade and the action each one calls for, so you can plan stops and check versions before touching production. Read each row against the release notes for your exact target version, because component floors and removed features change between releases. The main takeaway is that you should plan your path through the required stops rather than jumping to the newest minor release.

ChangeImpactAction needed
Required upgrade stops at x.2, x.5, x.8 and x.11Instances cannot skip these versions, so an instance on 18.0 must pass through 18.2 and later stops on the way to 19.x.Map your current version to the stops in the upgrade paths documentation and schedule one hop per stop.
Minor release on the third Thursday of each monthMinor releases arrive often, so a fleet can fall several versions behind between planned windows.Set a cadence for reviewing new minor releases and confirm which stop each one belongs to.
Patch releases twice a monthPatch releases carry fixes that may be needed between minor upgrades, and each one still needs a rollout plan.Track patch releases for your current line and apply them in a tested order across all nodes.
Major release planned yearly, with 20.0 planned for May 2027The 19.x line will end its role as the stop before 20.0, so 19.x planning now affects the next major upgrade.Plan your 19.x path now and review the 20.0 preparation guidance before scheduling work.
Maintained versions: 19.5, 19.4 and 19.3Only these lines receive fixes, so an instance on an older line is outside the support window.Confirm your target version is in the maintained list at the time you schedule the upgrade.
Bug fixes backported only to the current stable releaseTeams on older minor versions may not receive bug fixes and must move forward to get them.Avoid long pauses on one minor version and confirm whether a needed fix requires a newer release.
Security fixes backported to the previous two monthly releasesSecurity fixes reach a wider range of versions, which can ease emergency patching but does not remove the need to upgrade.Monitor security advisories and confirm which patch applies to your installed version.
Component version floors and removed features in each target releaseDatabase, Redis, operating system and bundled component requirements can change, which can block an otherwise valid stop.Check the release notes for each target version and confirm component floors before each hop.

How the required upgrade stops set the upgrade path

Plan the upgrade backward from your target version. Start with the release you intend to run, identify every required upgrade stop between your current version and that target, and then list the hops in order. Since GitLab 17.5, the required stops fall at x.2, x.5, x.8 and x.11, for example 18.2, 18.5, 18.8, 18.11, 19.2, 19.5, 19.8 and 19.11, according to the GitLab upgrade paths documentation. Use that page as the authority for your path, because the stop list is the constraint that decides how many hops you need. An instance several versions behind may need several hops, and each hop is a full upgrade cycle with its own checks, rollback point and window.

Do not skip a stop. GitLab requires upgrades to pass through each required stop because the stops carry the database migrations and data changes that the next line depends on. Between hops, every batched background migration must finish before you move to the next version. Those migrations run after an upgrade and rewrite data in the background, so a hop that looks complete can still be mid-migration. Check their status in the Admin area or through the database before you start the next hop, and confirm that none are failed or pending. Measure how long each hop and its background work take in a test environment built from a recent backup, because the duration depends on your data volume and the migrations involved, and no general figure applies.

Patch releases still matter at each stop. GitLab backports bug fixes only to the current stable release, while security fixes also reach the previous two monthly releases, according to the GitLab maintenance policy. Land on the latest patch of each stop before you move on, since a stop that is not patched may carry bugs or security issues that the later hop assumes are resolved. Record the exact version at every stop in your change log, so that auditors and your rollback plan can see which release each node ran at each point. Confirm the current patch versions with the GitLab maintenance policy on the day you plan each hop.

What to prepare before the first upgrade hop

Most failed multi-node upgrades were decided before the first package was installed. These six checks establish a restore point, a known configuration, healthy traffic paths and a rehearsal environment, so each upgrade hop starts from a state you can roll back to.

Prove the backup restores

Take a fresh backup with the GitLab backup task and restore it into a separate test instance. Confirm that repositories, the database and object storage data all come back and that users can sign in. Keep the gitlab-secrets.json file and the configuration files with the backup, because a restore without the matching secrets leaves encrypted data unreadable. Record how long the restore took so the maintenance window is based on a measured figure.

Inventory gitlab.rb and integrations

List every customized key in gitlab.rb, every external database or object storage setting, and each integration that calls the instance, such as SSO, runners, webhooks and monitoring. Some settings are deprecated or removed between stops, so compare each key against the release notes for every version on the path. Store the file under version control so you can diff it after each hop.

Confirm load balancer health checks

Before touching any node, confirm that the load balancer marks every web and API node healthy and drains traffic from a node it removes. Check that Gitaly, PostgreSQL, Redis and Sidekiq report healthy in their own status checks. An upgrade that begins with a degraded node pool makes it hard to tell an upgrade fault from an existing one. Write the drain and re-add steps for each node type down before the window opens.

Build a reference architecture staging copy

Run the upgrade in a staging environment that matches your production topology: the same node counts, the same split of stateful and stateless components and the same external services. Include the load balancer, because routing behavior during rolling restarts is one of the things most often missed in a single-node test. Use a copy of production data or a representative subset, and keep the staging instance on the same upgrade path as production.

Review the installation method

Confirm how the instance was installed: Linux package, Helm chart, GitLab Operator, Docker or from source. The procedure differs for each, and the Helm chart and Operator follow their own upgrade sequences. Also confirm whether PostgreSQL and Redis are bundled or external, because that decides which components you upgrade and when. Mixed installs, where a tool was used for one component and a script for another, need a written plan before the first hop.

Align runners with GitLab

List every GitLab Runner in the fleet with its current version and executor type. The Runner documentation advises keeping major and minor versions aligned with the GitLab server, so plan the Runner upgrade alongside the server hops and confirm which runners need a registration or executor change. Note which pipelines depend on a specific Runner tag, since a runner that is paused during the window will hold jobs in the queue.

The upgrade sequence, one minor release at a time

The sequence moves a multi-node GitLab instance from its current version to each required stop in turn, so every hop has its own checks, rollback point and verified health before the next one starts. Each hop follows the component order the installation method documents, with migrations allowed to finish between steps.

  1. Confirm the next required stop

    Start by reading the upgrade path from your current version to the target version. Since GitLab 17.5, required upgrade stops fall at x.2, x.5, x.8 and x.11, so a jump from one stop to the next is the unit of work, not the final version. Your platform or upgrade lead confirms the next stop against the upgrade paths documentation and records it in the change ticket. The step is done when the hop, its source version and its destination version are written down and approved.

  2. Run pre-upgrade checks and take a backup

    Before touching any node, confirm that every node runs the same version, that the instance is healthy, and that no batched background migrations from the previous hop are still pending. Take a full backup with the backup task and copy the secrets file, since a backup without the matching secrets cannot restore a working instance. Record the backup location, checksum and the GitLab version it was taken from. The step is done when the backup is confirmed restorable in a test environment or the team has a documented restore plan.

  3. Set the rollback point for this hop

    Define what can be returned to before the hop starts. A backup taken at this stop lets you restore the database and repositories to the pre-hop state, but it does not reverse database migrations that already ran against a newer schema, and it does not undo changes made to object storage, Runner versions or external systems after the backup. Agree with the owners of each system which changes are reversible and which require a forward fix. The step is done when the rollback owner and the decision point are named in the change record.

  4. Upgrade database and Redis prerequisites when the hop requires them

    Check the destination stop's prerequisites for PostgreSQL and Redis. If the hop requires a newer database or Redis version, upgrade those components first, in the order your installation method documents, while the application still runs on the old version. Minor PostgreSQL or Redis changes are handled differently from major upgrades, and major database upgrades fall outside the zero-downtime procedure, so schedule them separately. The step is done when the database and cache report the required versions and the application still passes health checks.

  5. Upgrade application nodes in the documented order

    Upgrade the GitLab application nodes one at a time, following the component order your installation method documents, typically with the node that runs migrations first and the remaining nodes after it. Keep at least one node serving traffic through the load balancer while each node is updated and removed from rotation only for its own upgrade. Your team confirms each node reports the destination version before the next one starts. The step is done when every application node runs the same destination version and the load balancer has returned them all to service.

  6. Let post-deployment and background migrations finish

    After the application nodes are on the new version, post-deployment migrations run and batched background migrations process existing data in the background. Do not start the next hop until the Admin area or database shows every batched background migration as finished, because the next stop expects that work to be complete. Migrations can run for a long time on large instances, so the timing depends on your data volume and should be checked, not assumed. The step is done when no migrations remain pending and no migration has failed.

  7. Verify health checks and Runner alignment

    Run the GitLab health checks, review application logs for errors that started after the upgrade, and confirm that repository access, CI pipelines, container registry and sign-in work from a test account. Check that GitLab Runners are on a version aligned with the new GitLab major and minor version, since the Runner documentation advises keeping them in step. Record the results in the change ticket. The step is done when the checks pass and the team signs off that the instance is ready for the next hop.

  8. Repeat the cycle for each subsequent stop

    Return to the first step and repeat the hop for the next required stop, with a fresh backup and a new rollback point. Each hop starts from a verified, fully migrated instance, so a problem found at one stop is isolated to that hop. Stop and reassess if any hop shows failed migrations, unexpected health results or a need to change the path. The sequence is complete when the instance reaches the target version with all hops documented and signed off.

Testing and rollback for each hop

Staging is where a hop is proven before production sees it. The staging instance should match production as closely as practical: the same GitLab version before the hop, the same multi-node layout, the same external PostgreSQL and Redis versions, and a recent copy of production data restored from a backup. A hop that works on a small test instance with no runners, no SSO and no large repositories tells you very little.

  • API and Git operations: Run scripted calls against the API and clone, push and fetch against repositories of different sizes, including the largest ones, through the load balancer address users will use.
  • Runner jobs: Run representative pipelines on runners registered to staging, covering artifacts, caches, container registry pushes and deployment jobs, and confirm runner and server versions are aligned for the target release.
  • Authentication logins: Test each sign-in path in use, such as SAML, LDAP, OAuth or two-factor enforcement, with test accounts for every group and role.
  • Background migration status: Confirm that batched background migrations from the previous hop show as finished before you start the next one, and record how long the staging hop took.
  • Rollback rehearsal: Restore the pre-hop backup to a separate host and confirm it boots, so the rollback path is known to work.

Document every failed check and do not promote the hop to production until the staging results are repeatable. Confirm the exact check list with your own platform and security owners.

What makes a GitLab upgrade take longer

This table lists the factors that stretch a multi-node GitLab upgrade beyond the basic procedure, and what to ask a partner or your own team to confirm for each one. Read each row against your current instance, since several drivers compound each other. The main takeaway is that hop count, external database versions and the size of the test environment usually set the schedule more than the upgrade commands do.

DriverWhy it mattersWhat to ask for
Repository and object data sizeLarger repositories and LFS, package and artifact stores lengthen backup, verification and any post-upgrade data work, and they widen the window for a failed restore.A measured inventory of repository storage, object storage buckets and backup duration from your current environment, plus the backup method you will use before the first hop.
Number of required hopsEach required upgrade stop is a separate upgrade with its own checks, and background migrations must finish between stops, so the path from an old version can add many cycles.The exact list of required stops from your current version to your target, taken from the upgrade paths documentation, and a plan showing where each hop is validated.
Custom gitlab.rb settingsHand-edited configuration can conflict with changed defaults or removed settings in later releases, and each conflict must be found in staging rather than during the production window.A diff of your gitlab.rb and related files against the release notes for every version in the path, and a list of settings that must change before each hop.
External PostgreSQL or Redis versionsA database or cache that is behind the version a target release expects forces an extra upgrade track, and major database upgrades fall outside the zero-downtime procedure.The current PostgreSQL and Redis versions, whether they are bundled or external, and the minimum versions required by each target release, confirmed in the release documentation.
Integrations with CI systems and Kubernetes clustersConnected CI systems, Kubernetes agents and webhooks can break when the server API or chart mapping changes, and their owners may need to change their side in the same window.An inventory of every integration with its owner, its version, and a test case that proves it still works after each hop.
Runner fleet size and version alignmentRunners must stay aligned with the server by major and minor version, so a large or mixed fleet needs an ordered rollout that can add a runner-side step to every hop.The number of runners, their executors, their current versions and the order in which you will upgrade them relative to the server.
Geo sitesA Geo deployment has a primary and secondary that must be upgraded in a planned order, and replication must be handled so the disaster recovery checkpoint stays clean.Whether Geo is in use, the upgrade order for each site, and when replication will pause and resume during each hop.
Size of the test environmentA staging environment that does not match production in node count, data volume or integrations hides the problems that appear only at scale, so issues surface in the production window.A written description of how the test environment differs from production, the data it holds, and the rehearsal runs you will complete before the production date.

Questions buyers ask before hiring help with a GitLab upgrade

What drives the cost of a GitLab zero-downtime upgrade, and can it be quoted at a fixed scope?

Yes, most DeployOpen engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work starts. The cost follows from the number of upgrade hops, the size and topology of the instance, and how many components (application nodes, Gitaly, PostgreSQL, Redis, load balancer, runners) must change. Other drivers are the state of the current installation, whether a staging environment exists for rehearsal, and how many integrations (SSO, registry, runners, external databases) need retesting. Ask for a quote that lists each hop, the rehearsal runs, the production change windows, the rollback plan and the post-upgrade checks as separate line items. Clarify which items are one-time and which would move to a retainer for ongoing support.

How long does a multi-hop GitLab upgrade take, and what sets that duration?

The duration depends on the number of required upgrade stops between your current version and the target, and on how long each hop's background migrations take to finish. GitLab requires upgrades to pass through stops at versions x.2, x.5, x.8 and x.11 since GitLab 17.5, so an instance several versions behind will cross multiple stops, each needing its own stabilization period. Confirm the expected duration for your path with the GitLab upgrade paths documentation and with your own staging results, because migration time scales with data volume. Ask your partner to report the observed time for each hop in rehearsal rather than a single estimate.

What does our internal team need to provide for the upgrade?

Your team should provide administrative access to the GitLab nodes, load balancer, database and object storage, plus credentials for the staging environment. You also need agreed change windows for production, named approvers, and a list of test users and critical projects that must be validated after each hop. Before any upgrade, ask for proof that a recent backup exists and has been restored successfully in a separate environment, not just that a backup job reports success. Confirm who owns DNS, certificates, runner fleets and external integrations, because those often block the schedule if their owners are not available.

Can our own team run the upgrade, and where does outside help usually matter?

A capable platform team can run a GitLab upgrade alone if it has the time, a staging copy of production and the discipline to stop at each required version. Outside help usually matters in three places: planning the hop sequence against your exact version and topology, rehearsing the component order including the load balancer drain and post-deployment migrations, and writing the rollback and verification steps before the first production change. Teams also tend to need help when background migrations stall, when a patch release has to be avoided, or when the instance uses an external PostgreSQL or Redis cluster. Decide early which of these your team can cover and which you want written into the scope.

Does a PostgreSQL major upgrade fall inside the zero-downtime procedure?

No. A PostgreSQL major version upgrade falls outside the zero-downtime procedure and needs its own planned outage or a separate migration approach. The zero-downtime steps cover GitLab application versions, so the database engine version must be handled as a distinct change with its own rehearsal and rollback. Check the required PostgreSQL version for your target GitLab release in the GitLab documentation before you plan the window, and confirm whether your database is bundled or external. Ask any partner to schedule the database change separately so that a failure there does not get mixed up with the application hops.

How do Geo sites change the upgrade sequence?

A Geo deployment has a primary site and one or more secondary sites, so the upgrade has to be coordinated across both and the order matters. Replication is usually paused at a planned point so that you keep a clean disaster recovery checkpoint, and the secondary must not be left on a version that cannot replicate from the primary. Geo is available in the Premium and Ultimate tiers for self-managed GitLab, according to the GitLab Geo documentation. Ask whether the Geo procedure is included in your scope, since it adds steps, a longer window and additional verification of replicated data on each site.

Does the upgrade include a guarantee of no downtime?

DeployOpen does not guarantee outcomes such as zero downtime, and no responsible partner can promise it for every environment. What can be defined in writing is the scope, the rehearsal plan, the component order, the verification steps and the rollback path. Zero-downtime depends on a highly available topology, a load balancer that can drain nodes, and a compatible version path, so the feasibility check is part of the first phase. Ask for the assumptions behind the plan and what happens if one of them does not hold in production.

What to verify and maintain after the final hop

Treat the last required stop as a checkpoint, not the finish line. Before you close the change, confirm that every node runs the same GitLab version, that background migrations report finished, and that gitlab-ctl status shows all services up on each application, Gitaly, PostgreSQL and Redis host. Then run the built-in health checks through the /-/health, /-/readiness and /-/liveness endpoints from the load balancer's point of view, not only from inside the network. Confirm that Prometheus is scraping the GitLab exporters on every node, that request latency and error rates match the pre-upgrade baseline, and that a test push, merge request and pipeline complete end to end. Record the exact versions in your change log so the next upgrade starts from a verified state.

Runners are the part of the estate most often left behind. Check that each GitLab Runner fleet is on the same major and minor version as the server, because the documentation advises keeping them aligned, and that jobs still start on autoscaling executors after the change. Review your monitoring alerts as well: if they were tuned to the previous version's metric names or thresholds, they may be silent. Confirm that a fresh backup completed after the upgrade, since it is the only restore point that matches the new schema. Your own runbook should say who owns each check and where the evidence is stored.

Start planning the next hop as soon as this one is verified. Read the GitLab upgrade paths documentation to identify the next required stop in the 19.x line and whether it lies before or after the 20.0 date. GitLab ships a minor release on the third Thursday of each month and patch releases twice a month, so you must choose a target that is still supported. The maintenance policy currently names 19.5, 19.4 and 19.3 as maintained, and it states that bug fixes are backported only to the current stable release, while security fixes also reach the previous two monthly releases. A version left in place for several months therefore receives security fixes for a shrinking window. Confirm the 20.0 release is planned for May 2027 against the current maintenance policy, and schedule the work to clear 20.0 prerequisites before that date.

How DeployOpen scopes and delivers a GitLab upgrade

DeployOpen plans, implements, tests, documents and supports self-hosted open-source platforms inside your own infrastructure, and that includes multi-node GitLab upgrades. We agree the scope in writing first, then build and verify the upgrade in stages you can review before production work begins.

How we scope

  • Inventory your current GitLab version, edition, install method and every node in the topology.
  • Confirm the load balancer, PostgreSQL, Redis, Gitaly and object storage layout before choosing a path.
  • Map the required upgrade stops and agree the minor release sequence in the written scope.
  • Record which runners, integrations and Helm or Operator components must move with the server.

How we build

  • Prepare a staging copy of the topology and rehearse each minor hop in the agreed order.
  • Check batched background migrations and post-deployment migrations finish before each next step.
  • Run backups and a restore test before the first production change, and document the rollback point.
  • Capture health checks and API and Git smoke tests that prove service stayed available during each stage.

How we hand over

  • Deliver a runbook with the exact component order, node by node, for your change window.
  • Document the verification steps, observed results and any deviations from the planned sequence.
  • Record version pins, configuration changes and open follow-up items for your own team to own.
  • Walk your operators through the procedure so they can repeat it for the next required stop.

How we support

  • Engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work starts.
  • Ongoing support, upgrades and continuous testing are available as a retainer if you want DeployOpen to stay involved.
  • Ask any partner to confirm how they handle rollback, out-of-hours windows and post-upgrade monitoring in the quote.
  • Quotes follow from the written scope, so ask for each cost component and what recurs after the upgrade.

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.