Why teams move GitLab off NFS and what the move involves
Moving self-managed GitLab off NFS is a storage re-platforming project. It is not a data import. Repositories, uploads, LFS objects, packages, job artifacts and job logs already exist on the instance, and the work is to change where those bytes live and how GitLab reaches them without losing history. The effort depends on three things: how much data sits on the NFS share, whether the instance runs as one node or several, and how much job log and artifact history must survive cutover. Pull those three numbers from your own environment before you ask any partner for a scope.
The main reason teams make this move is that NFS works against the way GitLab uses Gitaly. GitLab's hardware baseline discourages NFS for Gitaly storage, and the reference architectures assume local SSD for Git data. Latency, file locking behavior and backup consistency on network mounts show up as slow clones, stuck operations and backups that are hard to verify. Object storage handles the other large data types, but it is not a drop-in target for everything. The move is therefore planned by data type, with each type given its own storage setting, its own copy and verification step and its own rollback point, rather than as one copy job run over a weekend.
Before the build starts, the team should inventory every storage path in gitlab.rb and map each data type to a destination. For multi-node setups, confirm that incremental job logging and artifact object storage are configured so that logs and artifacts do not depend on a shared disk that is being retired. Decide how much artifact and log history to keep and whether older data is archived or expired. Take a full backup before the first change and run a restore test on a separate host, since a move that cannot be reversed is the highest risk in the project. The GitLab backup and restore documentation is the next read for the backup side of this plan.
Which NFS-backed data maps to object storage and which stays on Gitaly
This table maps each NFS-backed data type in self-managed GitLab to its target storage and the migration notes that matter for planning. Read the target column first to see what moves to object storage and what stays on local SSD Gitaly storage. Before the design is final, confirm each data type against GitLab's current object storage documentation, because some types need separate settings even when object storage is in use.
| Data type | Current NFS location | Target storage | Migration notes |
|---|---|---|---|
| Git repositories | Gitaly repository storage path on the NFS mount | Local SSD on each Gitaly node | Repositories stay in Git storage. Plan a maintenance window and confirm the Gitaly storage path settings for your release. |
| Uploads | Uploads directory on the NFS mount | Object storage bucket for uploads | Configure uploads as its own object storage section. Confirm the current configuration keys before cutover. |
| LFS objects | LFS objects directory on the NFS mount | Object storage bucket for LFS | Copy objects with the documented migration tasks. Compare object counts and checksums after the copy. |
| CI job artifacts | Artifacts directory on the NFS mount | Object storage bucket for artifacts | Decide artifact retention before migrating, because expired artifacts may be removed during the move. |
| Job logs | Local trace files on the NFS mount | Object storage with incremental job logging enabled | Multi-node setups need incremental job log settings. Confirm the defaults and behavior for your GitLab version. |
| Packages | Packages directory on the NFS mount | Object storage bucket for packages | Use a separate bucket. Confirm which package types the object storage configuration covers in your release. |
| Container registry | Registry storage path on the NFS mount | Object storage bucket for registry data | Registry storage is configured separately from the consolidated object storage form. Confirm the registry settings and test pulls and pushes. |
| Pages | Pages directory on the NFS mount | Object storage for Pages content | Pages has its own object storage settings. Confirm them and plan to redeploy Pages sites if content does not migrate cleanly. |
Which documented tools move data and which only prepare for the move
GitLab offers a few documented paths that touch instance data, and each covers a different part of the move. Only the backup and restore procedure moves repositories and instance storage between hosts and layouts. The other items either move project content from other platforms or prepare the target so the restore can land on it.
Backup and restore task
This is the documented path for moving instance data between hosts and storage layouts. Plan a restore onto the new Gitaly storage and object storage targets, then test it on a copy before cutover. Back up the configuration and secrets files separately, as the backup documentation describes, because a restore without them can leave the instance unable to decrypt stored values. Ask your team to confirm which backup types apply to your version and layout.
Import features for projects and issues
GitLab's importers bring projects, issues and related data in from other platforms such as GitHub, Bitbucket, Gitea and other GitLab instances. They do not move NFS data. Repositories and uploads already on your NFS mount stay where they are unless you move them through the backup and restore path. Use importers to consolidate work from other hosts, and confirm each importer's supported data before relying on it.
Object storage configuration in gitlab.rb
Changing gitlab.rb points GitLab at object storage for data types such as artifacts, uploads and LFS objects. The setting governs where new data is written, so it does not by itself migrate existing files. Confirm with your team how each object type is handled in your version, which buckets or containers are used, and how existing content is copied across before you switch the setting for production.
Repository consistency checks
Moving repositories between storage layouts depends on the backup and restore process, but it also depends on checks your team runs itself. Compare project and repository counts before and after the restore, and run Git integrity checks on a defined sample of repositories. Include large and heavily used projects in the sample. Record the results as evidence for the cutover decision.
Reference architecture for the target
The reference architectures do not move data, but they define the target the restore lands on. They set out the component layout, Gitaly placement and sizing that your local SSD storage and new services should match. Review the published sizing for your user count and API load before you provision hardware. Ask your team to confirm the chosen architecture and the matching database and Redis versions for your GitLab release.
How job logs, artifacts and repositories move without losing history
Repositories, job logs and artifacts do not move the same way, and the first decision is which of them need to arrive intact. Repositories live in Gitaly, which on a single-node instance sits on the same machine as everything else. Once you run several nodes, Gitaly data has to sit on local SSD on each Gitaly node, or in a Gitaly Cluster with Praefect for larger installations. GitLab's reference architectures recommend a high availability strategy for 3,000 or more users, and that is the point where a Gitaly Cluster with Praefect, PostgreSQL with Patroni, Redis Sentinel and Consul usually enters the design. Your NFS replacement is therefore a storage layout decision, not a like-for-like copy of the old mount. Confirm the current Gitaly and Praefect guidance for your target release before you size disks.
Job logs and artifacts are usually the largest files you move and the ones that most affect cutover time. GitLab supports incremental job log and artifact handling so that data can be transferred while the instance keeps running, which shortens the window in which the old storage must be frozen. The settings that control this are version-specific, so take the exact configuration keys, their default values and the order in which they must be enabled from the current GitLab documentation for your release, and test them on a copy of production. Record the pre-cutover sync state for each object type so you can prove what was copied and what remains.
Retention policies decide how much history is worth moving at all. Ask which pipelines, artifacts and log ages the business and auditors actually need, and set expiry rules before the copy starts, so you do not pay to transfer data that will be deleted within weeks. Keep repositories, merge request history and audit-relevant job evidence on the migration plan, and treat expired artifacts as an accepted loss that is signed off in writing. Verify restored history with a sample of projects, a count of pipelines per project and a spot check of archived logs before you decommission the NFS export.
Sequence for inventory, pilot, parallel run, cutover and decommission
The sequence turns an NFS-backed GitLab into one where repositories, job artifacts, logs and backups sit on storage you can restore and operate, with a rollback path until the old storage is retired.
Inventory every NFS path and data type
Your team lists each mount point GitLab uses, the data type behind it (repositories, LFS objects, CI artifacts, job logs, uploads, packages, container registry, backups and pages), its current size, growth rate and retention rule. DeployOpen reviews the gitlab.rb settings and mount table with you and produces a written inventory that maps each path to its target: object storage for blob types, Gitaly on local SSD for repositories. The step is done when every path has an owner, a size and a destination, and nothing is left unassigned.
Confirm target version and upgrade stops
Record the GitLab version you run today and the target version, then check the upgrade paths documentation for any required upgrade stops between them. Storage changes should not be bundled with an unplanned version jump, so the target is fixed before the move is scheduled. Your team confirms the Runner versions and any integrations that depend on the current release. The output is a signed-off version plan with each required stop listed in order.
Build the target storage and Gitaly layout
Provision object storage buckets for each blob type, with access keys scoped per bucket, and local SSD volumes for Gitaly on the Gitaly nodes, following the hardware guidance for your reference architecture. DeployOpen writes the object storage and Gitaly settings, applies them to a non-production instance, and confirms that GitLab reads and writes each bucket. Your team supplies the cloud or data center accounts, network paths and key management. The step closes when a test upload, clone and artifact write succeed against the new storage.
Pilot the move on a restored copy
Restore a recent backup into a separate environment built on the target layout, then migrate the object types and repository data to the new storage. Measure how long the restore and the storage migration take, and record any errors, orphaned files or permission problems. Use these measurements, not estimates, to set the production window. The pilot is complete when a full restore runs end to end, the measured times are recorded, and users' test workflows pass against the copy.
Run a parallel period with tested rollback
Keep the NFS-backed instance available while the new layout receives a copy of recent data, and define the trigger, owner and steps for rolling back to NFS. Test the rollback on the pilot before relying on it. Your team checks the sync logs, backup jobs and job log and artifact behavior on the new layout during this period. The step produces a written rollback runbook that someone other than its author has rehearsed successfully.
Cut over in an agreed maintenance window
Announce the window, put GitLab into maintenance mode, run the final data sync, switch the configuration to the new storage and restart the services in the order your runbook states. DeployOpen runs the procedure with your team on call and checks repository access, CI job execution, artifact downloads and the backup job. Your team confirms business-level checks with a named user group. The cutover is done when those checks pass and the rollback window is formally closed or extended.
Decommission NFS after backups verify
Keep the NFS export read-only for the agreed retention period, then retire it only after a backup has been taken on the new layout and restored successfully to a separate environment. Remove mounts from the GitLab configuration and the hosts, and update the inventory and runbooks to show the new storage as the system of record. The step is complete when the verified restore is documented and the NFS export is removed under change control.
What drives the cost and duration of an NFS move
This table lists the factors that change how much effort an NFS move takes and how long it runs, with the question to ask a partner for each. Read it before requesting a quote, because the same platform can need a short cutover or a multi-week program depending on these inputs. The main takeaway is that data volume, Gitaly topology and external scripts usually matter more than the object storage choice itself.
| Driver | Effect on effort | What to ask in a quote |
|---|---|---|
| Total data volume per type | Repositories, LFS, uploads, packages, artifacts and job logs each need their own copy, verification and cutover window. Larger volumes lengthen copy and checksum runs. | How will each data type be measured, copied and verified, and which copy tool and window are assumed? |
| Number of Gitaly nodes and high availability | Each Gitaly node holds its own local SSD data, so every node needs a repository move and placement check. A high availability layout adds Praefect and replica validation. | Does the scope cover one Gitaly node or a Gitaly Cluster with Praefect, and how are replicas checked after the move? |
| Job log and artifact history to keep | Keeping years of logs and artifacts multiplies transfer and object count. Deciding what to migrate versus expire adds review work with project owners. | Which job log and artifact ages are migrated, which are expired, and who signs off on the cut list? |
| Retention rules | Legal hold, regulatory retention and backup retention each change what must be copied and kept on object storage after the move. | Which retention policies apply to each data type, and how are they configured on the new storage? |
| Number of users and CI load | More users and pipelines raise the volume of writes during the freeze window and the number of runners to re-test. Higher load also narrows the usable cutover time. | What user count, daily pipeline volume and freeze window are assumed, and what load test is included? |
| Custom integrations and scripts that read NFS paths | Backup jobs, monitoring, log shippers and in-house scripts that read /var/opt/gitlab paths directly must be found, rewritten or retired. This is often the least visible cost. | Will the partner inventory every host-level path reader, and is rewriting these scripts included in the fixed scope? |
| Upgrade stops in the same window | If a required upgrade stop falls inside the migration window, the team must sequence two changes and test both rollbacks. The upgrade paths document lists these stops by version. | Is any required upgrade stop scheduled in the migration window, and is the upgrade counted in this scope or a separate one? |
| Number of environments (staging, pilot, production) | Each environment repeats the migration rehearsal, configuration and verification. A staging rehearsal with production-sized data is the main driver of a longer schedule. | How many environments are in scope, and is a full production-sized rehearsal included before the cutover? |
What usually goes wrong in NFS moves and how to prevent it
NFS-backed GitLab moves tend to fail in the places the pilot did not exercise: full-size data volumes, background jobs, scripts and disks sized for the old layout. The cards below pair each common risk with one action to take before cutover.
Restores that run far longer at full size
A restore that completes quickly on a pilot dataset can take much longer against the full repository set, object store and database. Restore time depends on repository count, artifact volume and the object storage path, so a small trial does not predict the production window. Rehearse a full-size restore on hardware matching production, time each stage, and record the result as the basis for your cutover window.
Job logs and artifacts that fail after cutover
Job logs and artifacts stored on NFS may not resolve once object storage is configured, and older pipelines can show missing downloads even when new jobs work. Check how incremental job logs are enabled and whether existing artifacts were migrated, expired or left behind. Test a download of a pre-migration artifact and a pre-migration job log on the target instance before you declare the move complete.
Scripts and backup jobs still pointing at NFS
Cron jobs, backup wrappers, monitoring checks and mount-based housekeeping scripts often reference NFS paths that no longer hold current data. These jobs may keep reporting success while protecting an empty or stale location. Inventory every script, scheduled task and monitoring rule that touches the GitLab data directory, then update or retire each one and confirm it against the new storage layout.
Gitaly on undersized local disks
Gitaly storage on local SSDs must hold repositories, their growth and the working space that Git operations need. Disks sized from the current NFS usage often leave little headroom, and a full disk stops pushes and fetches on the affected node. Size local storage from measured repository growth, set disk usage alerts, and confirm the sizing against the current hardware requirements before you order or provision machines.
Skipped integrity checks after the copy
A copy that finishes without errors does not prove that repositories, uploads and object references match the source. Silent corruption, partial transfers and mismatched object paths can surface days later when a user opens an old project. Run GitLab's consistency checks on repositories and attachments, compare object counts per data type, and sample-restore a set of projects before the source is decommissioned.
Rollback plans that were never tested
A rollback plan that exists only on paper may fail when the team needs it, especially if writes continue on the new storage after cutover. Decide the point of no return, keep the NFS source readable for the agreed period, and rehearse switching back in a staging copy. Name the owner who can authorize the rollback and the data written after cutover that would need to be reconciled.
What buyers ask before moving GitLab off NFS
What does it cost to move self-managed GitLab off NFS?
The cost comes from planning, data work, infrastructure and the people doing the work, and no supplied figure sets a number for it. Your own team pays in time for inventory, testing and cutover, plus any new object storage, local SSD capacity and backup tooling you add. A partner is paid for the written scope, which usually covers discovery, configuration, data movement, verification and handover. Costs go up with repository count and total data size, the number of storage paths in use, how many CI artifacts and job logs you keep, and how many test cycles you need. Ask any quote to itemize one-time work (migration, testing, cutover) separately from recurring work (monitoring, upgrades, backup operations) and to state which environments are included.
How long does an NFS to object storage and local SSD move take?
The duration depends on the factors that set the work, not on a fixed number. The main drivers are the volume of data in each storage path, the number of Gitaly storage nodes you are moving to, how many object types need separate settings, how much CI history you keep, and how many rehearsal runs your change process requires. Environment access, approval cycles and the availability of test windows also add time. The timeline is set in the written scope, which should list the phases, the rehearsal count and the cutover date that your team has agreed to.
Can our own team move GitLab off NFS without a partner?
Yes, if your team has the skills and the time to rehearse the work. The move draws on three areas: GitLab administration (configuration files, Gitaly, backup and restore, and version-specific upgrade rules), storage engineering (object storage buckets, access policies, local SSD layout and filesystem settings) and operations (monitoring, change control and rollback). Check whether you have people who have restored a GitLab backup in a test environment, who can run a multi-node instance and who know your object storage provider's access model. A gap in any of these areas is the usual reason teams bring in a partner for planning, rehearsal or handover.
Does moving GitLab off NFS require a maintenance window?
Plan for a maintenance window at cutover, because the final data sync and the switch to new storage need GitLab to be quiet. A parallel run lowers that risk: you stage data in the new storage, run rehearsal restores in a separate environment, and compare results before the cutover. This lets you measure the real window during rehearsal instead of guessing. Confirm with GitLab's documentation for your release how maintenance mode and backups behave in a multi-node layout, and agree the window with the teams that depend on CI and repository access.
Should we upgrade GitLab in the same project as the storage move?
In most cases, keep the storage move and the version upgrade as separate changes. GitLab requires upgrades to pass through required upgrade stops, and the supplied upgrade paths list stops at versions x.2, x.5, x.8 and x.11, such as 18.2, 18.5, 18.8 and 18.11. If your current version is far behind, the upgrade path may add work that you should schedule and test on its own. Doing both changes at once makes failures hard to trace. Settle the target version first, confirm the stops in the upgrade paths documentation, and then plan the storage move on that version.
What must our team provide before a GitLab storage migration starts?
Your team provides access, information and test time. Access means administrative access to the GitLab instance, the storage hosts, the object storage account and the backup location, along with change approval. Information means an inventory of storage paths, repository and LFS data, CI job logs and artifacts, container and package registries, uploads and backups, plus the GitLab version and the retention rules that apply to each. Test windows mean dedicated time for rehearsal restores and verification. Ask the implementation partner to confirm this list in writing before the scope is signed.
Which job log and artifact settings matter for a multi-node GitLab setup?
Incremental job log and artifact settings matter because large CI logs and artifacts can make storage and backup work grow faster than repositories. Decide where job logs and artifacts live, whether they move to object storage and how long you keep them. Check the GitLab documentation for your release for the exact configuration keys, because these change between versions. Test that artifacts are still reachable from pipelines after cutover and that retention jobs still run. Record these settings in your change record so the backup and restore procedure matches what is actually stored.
How DeployOpen scopes, delivers and supports an NFS to object storage move
DeployOpen plans, implements, tests, documents and supports self-hosted open-source platforms inside your own infrastructure, so the move off NFS runs in your cloud accounts, data centers or air-gapped network. Engagements are usually fixed-scope, with the data inventory, storage targets and cutover steps agreed in writing before work starts.
How we scope
- Inventory which GitLab data sits on NFS today, including repositories, uploads, artifacts, LFS, packages and job logs.
- Confirm target object storage provider, Gitaly node layout and local SSD sizing with your team before any change is made.
- Agree in writing the migration rehearsal, cutover window, rollback point and the GitLab version and upgrade stops involved.
How we build
- Configure consolidated object storage and incremental job log and artifact settings in the test environment first.
- Migrate repositories to local SSD Gitaly storage and verify object counts, checksums and sample restores against the source.
- Run a full rehearsal of backup, data move and rollback so the production cutover follows a tested sequence.
How we hand over
- Document each storage path, bucket, configuration key and the data type each one holds for your operations team.
- Provide a runbook covering backup verification, restore tests and the post-cutover checks your team repeats.
- Walk through the decisions taken, open risks and the items your own team must confirm with GitLab or your provider.
How we support
- Offer ongoing support, upgrades and continuous testing as a retainer after handover, scoped in writing.
- Plan upgrade work around GitLab required upgrade stops so storage changes do not block the next release.
- Review backup and restore results and storage growth with your team on the cadence agreed in the retainer.

