How GitLab object storage configuration works and when to use it
Move GitLab object storage off local disk when a self-managed instance runs on more than one node, needs its data to survive the loss of a server, or has outgrown what a single disk can hold. The fix is to point GitLab's object storage settings at an S3 bucket, Google Cloud Storage, Azure Blob Storage or an S3-compatible endpoint such as a Ceph Object Gateway. Object storage holds the large objects GitLab produces and serves, including CI job artifacts, LFS objects, uploads, packages, container registry layers and job logs. These are usually the largest and fastest-growing data in the instance. Each data type can use its own bucket or share one, and that choice drives the rest of the configuration work.
The main reason teams make the move is topology. Local disk ties artifacts and uploads to one host, which blocks the multi-node layouts described in GitLab's reference architectures and makes backups and restores harder to reason about. Those references recommend a high availability strategy for 3,000 or more users, and the Cloud Native Hybrid layout keeps stateful services on virtual machines or managed services. Object storage fits that split because every application node can reach it. Confirm your sizing and component choices against the GitLab reference architectures documentation for your user count and CI load. Licensing matters at the edges: GitLab's source is open core, with the ee/ directory under the Enterprise Edition license and the rest under MIT, and Geo, the warm-standby option for disaster recovery, is available on Premium and higher tiers. Confirm which tier your storage and recovery design depends on before you commit to it.
Ceph is a reasonable target when you run your own data center. Its Object Gateway offers an S3-compatible API, and the Ceph source is licensed under LGPL with no paid edition from the project itself. Compatibility with the S3 API is the first check, not a guarantee that every GitLab data type will behave the same way. Ask GitLab which providers it tests against and which are only community-documented, and confirm that your Ceph release is on the Ceph active releases list before you build on it. Avoid MinIO for new builds: its community repository is archived, and the README says it is no longer maintained and points to commercial alternatives.
Choosing between AWS S3, Google Cloud Storage, Azure Blob and Ceph RGW
Amazon S3 is a managed service, so AWS runs the storage fabric and your team manages buckets, identities and encryption settings. GitLab's consolidated object storage form can point at S3 for the object types you enable, and the access model is usually an IAM role or a dedicated access key scoped to the GitLab buckets.
- Identity: Prefer an IAM role attached to the GitLab hosts over long-lived access keys, and scope the policy to the GitLab buckets only.
- Encryption: Decide between S3-managed keys and KMS keys before the first upload, because changing the key model later means re-encrypting stored objects.
- Recurring cost: Storage, requests and data transfer out are billed by AWS every month, and backup retention multiplies the stored volume.
- Lifecycle rules: Backup and artifact retention should be set in S3 lifecycle policies that match the retention your GitLab backups use.
Ask your AWS account team or a partner to confirm the regions and endpoint behavior your GitLab release documents for S3, and test one full backup and restore against a non-production bucket before cutover.
Google Cloud Storage is also a managed object service, and it is typically chosen when the GitLab deployment already runs on Google Cloud VMs or Cloud SQL. The main design work is deciding how buckets map to GitLab data types and which service account gets access to each bucket.
- Bucket layout: Plan one bucket per object type, such as artifacts, LFS, uploads and backups, so retention and access can differ by type.
- Service accounts: Grant each bucket the narrowest role that lets GitLab read and write objects, and record the service account key location in your secrets process.
- Recurring cost: Storage, operations and network egress are billed by Google every month, and restores from a distant region can add transfer charges.
- Access logging: Confirm which audit and access logs your security team needs and enable them on the buckets before production data arrives.
Confirm with GitLab's documentation which Cloud Storage features your release supports for each object type, and run a restore test after the first full backup so the results are known before an incident.
Azure Blob Storage uses storage accounts and containers, and it fits organizations that already standardize on Azure identity, networking and monitoring. The deployment choice here is mostly about how GitLab authenticates to the storage account and how containers are separated by data type.
- Containers: Assign separate containers to GitLab object types so access, retention and lifecycle rules can be set per type.
- Authentication: Decide between account keys, shared access signatures and Microsoft Entra ID based access, then confirm with GitLab's documentation which method your release accepts for each setting.
- Network controls: Restrict the storage account with private endpoints or firewall rules that allow the GitLab hosts and backup jobs, and test that the restrictions do not block CI artifact uploads.
- Recurring cost: Capacity, transaction and egress charges continue each month, and soft-delete or versioning features increase the stored volume.
Before relying on Azure Blob for backups, confirm the GitLab-supported settings for your release, then restore a test project into a clean instance to prove the full path works.
Ceph RGW, the Ceph Object Gateway, exposes S3-compatible and Swift-compatible APIs on a cluster your team operates. In GitLab terms it is a community-documented target rather than a tested first-party one, so your team or implementation partner must prove each GitLab data type works against the endpoint in your environment.
- Operations: You run the cluster, its upgrades and its monitors. cephadm deploys Ceph as containers, and the Ceph releases page lists Tentacle (20.2.x) and Squid (19.2.x) as the actively maintained releases.
- Licensing: Ceph is licensed under LGPL-2.1 or LGPL-3, and the project itself offers no paid edition, so commercial support comes from a vendor or partner you choose.
- Hardware and failure domains: The Ceph hardware recommendations advise a dedicated drive per OSD, shared SSDs for WAL and DB on HDD-based OSDs, and bonded network links across two switches. Failure domains in CRUSH determine how replicas survive a host or rack loss.
- Capacity planning: Size memory for peak use and benchmark before buying, because cluster behavior depends on the workload.
Test multipart uploads, large CI artifacts and backup restores against your RGW endpoint before cutover, and confirm the GitLab release you plan to run is supported with it.
Air-gapped and data-center networks cannot reach public cloud endpoints, so the object store usually runs on-premises, and that makes Ceph RGW or another self-hosted S3-compatible service the common path. GitLab documents offline and air-gapped operation, but the object store, its certificates and its update path become your responsibility.
- Endpoints and trust: Plan internal DNS names and a certificate chain that every GitLab node trusts, and test TLS from each node before configuring GitLab.
- Offline updates: Plan how Ceph packages and GitLab packages reach the isolated network, including the transfer and verification steps your change process requires.
- Licensing review: The MinIO community repositories are archived, the README says the community edition is distributed as source only, and it is licensed under AGPLv3. Have legal review any MinIO option before selecting it.
- Backup target: Keep at least one backup copy outside the primary object store, and confirm the offsite path is also air-gapped or approved.
For each option, confirm the offline procedures in GitLab's offline environments documentation and run a full restore in an isolated test network before the first production backup.
Configuration options and when each fits
This table compares the object storage choices for self-managed GitLab: the consolidated form, separate per-type settings, the credential method and the Ceph RGW endpoint. Read each row against your cloud provider, the data types you need to separate and the skills your team already has. The main takeaway is that the connection method is only one decision, and the provider and GitLab release combination must also be confirmed in writing.
| Option | When it fits | What the team needs |
|---|---|---|
| Consolidated object storage form | One provider and one credential set cover most data types with a simple layout. | Bucket design, one access policy and a check of which data types the form covers in your release. |
| Separate per-type settings | Data types need different buckets, retention, encryption or access rules. | A bucket per data type, a settings entry for each and a backup test for every bucket. |
| Direct access keys | On-premises, Ceph or any setup where IAM roles or managed identity are not available. | A secrets store, a rotation schedule and a documented owner for the keys. |
| IAM roles on AWS | GitLab runs on AWS VMs and the team wants no static keys in configuration files. | An instance role scoped to the GitLab buckets and confirmation that your release documents this method. |
| Managed identity on Azure | GitLab runs on Azure and the team wants to avoid stored storage keys. | A managed identity with blob permissions and confirmation of documented support for your release. |
| Ceph RGW endpoint | Object storage must stay in your data center and the team runs Ceph with its S3-compatible gateway. | Endpoint, TLS and addressing checks, ongoing Ceph operations and tests of GitLab uploads, backups and artifacts. |
| MinIO community server | Existing deployments only, where a migration plan is already in place. | Review of the archived repository and AGPLv3 terms before any new build. Plan for a different S3 target. |
Which data types still need separate settings
The consolidated object storage form does not cover every data type GitLab writes to object storage, so each category needs its own check against GitLab's documentation before go-live. Confirm how artifacts, LFS objects, uploads, packages, the container registry, Pages content and backups are configured for your GitLab version. Some of these categories have their own settings blocks, bucket names or encryption options, and some are only documented as separate sections. Treat the list of categories as a checklist you work through with the version you are installing, not as a list you remember from an earlier release.
The gap matters most for backup and restore. If backups or artifacts land in a bucket or path that your backup process does not read, a restore can complete without error and still be missing data. Record, for every data type, the bucket or path, the access policy, the lifecycle or retention rule and whether the data is included in your backup task. Note that the GitLab backup task does not capture every component the same way, so your plan should cover the application secrets and configuration files separately. The sibling content group on GitLab backup and disaster recovery sets out the specific backup, restore and Geo procedures, and you should validate your restore plan against those before relying on it.
In multi-node setups, a shared object store is only useful if every node points at the same buckets with the same settings. Confirm that each Rails, Sidekiq and Gitaly-adjacent node receives identical storage configuration after every change, and test that a job artifact written by one node can be read by another. Ask your team to confirm which Ceph RGW features your chosen data types depend on, such as bucket policies or multipart behavior, because community-documented providers need the same verification as tested ones. Include these checks in your upgrade rehearsal, since a configuration key that changes between GitLab releases can break one data type while the others keep working.
Setting up GitLab object storage step by step
This sequence moves GitLab's uploads, job artifacts, LFS objects and backups from local disk to buckets or containers you control, and checks each data type before the instance is treated as ready. Each step closes with a check the team can run and record.
Confirm the GitLab version and deployment method
The platform team records the running GitLab version, the installation method (Linux package, Helm chart, Operator, Docker or source) and the topology, including whether the instance is single-node or multi-node. Object storage settings differ by installation method and release, so the team checks the configuration reference for that exact version before changing anything. Confirm the target version against the upgrade stops if an upgrade is planned in the same window. The step is done when the version, method and topology appear in the change record and the team has a current configuration backup to roll back to.
Confirm provider support and choose the target
Decide between AWS S3, Google Cloud Storage, Azure Blob Storage or a self-hosted Ceph Object Gateway, and confirm in GitLab's documentation which providers it tests and which are community-documented. Ceph RGW is typically supported through community guidance, so the team should ask whether the chosen provider's S3-compatible behavior meets each feature's requirements. Note that the MinIO community server is archived and should not be selected for new work without a supported alternative. The step is done when the provider decision is written down, with the named owner who accepts the support risk.
Create buckets or containers and access policies
The cloud or storage team creates one bucket or container per object type that the instance will use, such as uploads, artifacts, LFS, packages, registry and backups, and sets private access by default. Access policies grant the GitLab service identity only the object actions it needs on those buckets, and separate identities are used where backups are written. Versioning, encryption at rest and retention rules are set according to the organization's own policy, and the team confirms each setting with the provider's console or API. The step is done when a listing of every bucket shows private access and the policy review has been signed off.
Configure credentials and connection settings
The administrator stores the access key or service credential in the secrets mechanism the installation uses, then sets the provider connection, endpoint and region in the object storage configuration file (for example,
gitlab.rbfor Linux package installs or the chart values for Helm). For Ceph or other S3-compatible endpoints, the team confirms the endpoint URL, path style addressing and TLS trust chain. The step is done when a reconfigure completes without errors and the team has confirmed that no credentials appear in plain text in version-controlled files or shared logs.Enable the consolidated and separate object settings
The administrator enables the consolidated object storage form so that supported data types share one connection definition, then adds separate bucket settings for any data types the release still requires to be configured individually. Review the documentation for the target release to confirm which data types, such as CI job artifacts, LFS objects, container registry, packages or backups, need their own keys. Run the reconfigure and restart services as the installation method documents. The step is done when the configuration check reports each data type with its intended bucket and the team has recorded any type still on local storage as an exception.
Run a test upload and confirm each object type
An administrator uploads a test file to a test project's repository through the web interface, then attaches a file to an issue or merge request so the upload path is exercised. The team checks that each object appears in the matching bucket with the expected prefix and that the file downloads correctly from GitLab. Verify storage use in the provider's console after each upload. The step is done when every enabled data type has one successful write, one successful read and a matching object in the intended bucket.
Verify CI job artifacts and LFS objects
A test pipeline runs on a project with a runner that is registered and healthy, produces a job artifact, and completes successfully. The team then downloads the artifact through the pipeline page and confirms the object is stored in the artifacts bucket. A repository tracked with Git LFS is pushed and pulled from a clean clone to confirm LFS objects are written and read from object storage. The step is done when the artifact and LFS downloads succeed and the team has checked that local disk is not growing for these data types.
Run a backup and restore test
The administrator runs a backup with object storage enabled and confirms which data types are included in the archive and which are uploaded to remote buckets, following the backup and restore documentation for the installed release. The team then restores into a separate test instance, checks that the test project, its artifacts and an LFS object are present, and confirms that the secrets file was preserved as the documentation requires. The step is done when the restore check passes and the result, including any data type handled outside the backup, is recorded as the baseline for the recovery runbook.
Securing object storage credentials, encryption and access
Object storage holds repositories' large files, job artifacts, logs, packages and backups, so its access path is part of the GitLab security boundary. The cards below cover the controls to decide on before GitLab is pointed at a bucket, from the policy attached to each credential to the logs that prove who touched the data.
Scope bucket policies per object type
Give GitLab its own credentials for each bucket or prefix it uses, such as artifacts, LFS, uploads, packages and backups, instead of one administrator key for everything. Allow only the object actions each bucket needs, and keep backup buckets writable only by the backup job. Check that no GitLab principal can delete or change objects in a backup bucket it only reads from. Confirm the exact action set with your provider's documentation and your GitLab version's object storage settings.
Rotate and store credentials
Store access keys, secrets and service account keys in a secrets manager or the protected gitlab.rb configuration, with file permissions limited to the root and GitLab service accounts. Plan rotation as a runbook: create the new key, update the configuration, reconfigure, verify a test upload and download, then revoke the old key. Ask your provider how to rotate without downtime, and test the procedure on a non-production instance first.
Encrypt data at rest
Enable server-side encryption on each bucket, and decide whether provider-managed keys meet your key custody requirements or whether customer-managed keys are needed. Confirm that backups, which GitLab writes to object storage when configured, follow the same key policy as live data. Ask the provider whether encryption applies to existing objects and how key deletion is handled, since losing a key can make backups unreadable.
Encrypt traffic in transit
Require HTTPS endpoints for every object storage connection and trust only the certificate chain your organization operates or a public root that you have verified. For Ceph RGW, decide where TLS terminates and confirm that the path between GitLab and the gateway is encrypted, including any internal load balancer. Ceph's messenger v2 secure mode protects traffic inside the cluster, which is a separate question from the client connection to the gateway.
Private endpoints and air-gapped paths
In air-gapped or tightly segmented networks, route object storage traffic over private addresses, such as a private endpoint, a VPC endpoint or an internal Ceph gateway, and block the public internet path at the network layer. Keep GitLab, runners and backup hosts on routes that reach the storage endpoint without egress to third-party services. Test name resolution and certificate validation from every host that talks to the bucket, not only from the GitLab application server.
Log and review object access
Turn on access logging or audit logging at the bucket or gateway level, and send those logs to a store that GitLab administrators cannot edit. Review denied requests, requests from unexpected source addresses and deletes. Ask your provider which events are recorded, how long logs are kept and whether they can be exported to your security monitoring. Logs do not replace bucket policy, but they let you prove who accessed the data during an incident.
Questions buyers ask about GitLab object storage cost and effort
What drives the cost of GitLab object storage setup?
The main cost driver is the work of mapping each GitLab data type to a bucket, credentials and lifecycle rules, then testing that uploads, downloads and backups behave correctly. Storage volume, egress charges and provider pricing are ongoing bills paid to the cloud or hardware owner, not to the consultant. Staff time is the one-time cost, covering design, configuration, testing and handover. Ask any partner to split the quote into design, configuration, testing and documentation, and to state which items are one-time and which recur.
Which costs are one-time and which recur for object storage?
One-time costs include the design, configuration, migration of existing data, verification and runbook writing. Recurring costs include the bills for stored capacity, requests and data transfer out of the provider, plus the ongoing staff time to monitor storage, rotate credentials and handle upgrades. For Ceph, recurring costs also include the hardware refresh and support contract if you buy one. Ask the quote to show the recurring items separately so you can budget them for each year of operation.
How long does GitLab object storage setup take?
Duration depends on how many GitLab data types you move to object storage, whether you migrate existing data, and whether the target is a managed provider or a self-run Ceph cluster. A new instance with a few buckets is a much smaller job than moving years of artifacts, packages and LFS objects off NFS. No fixed duration applies to every environment. Ask for a timeline in the written scope that lists each data type, the test steps and the cutover window.
What does the work need from our team?
You will need to provide access to the GitLab instance and its configuration, a cloud account or Ceph cluster where the buckets will live, and a decision on naming, encryption and retention. Someone on your side must approve IAM or access policies, network paths from GitLab to the storage endpoint, and a maintenance window for cutover. Your team also needs to confirm which data types must stay on separate settings and who owns the backup of those buckets. Put these inputs in the scope so gaps show up before work starts.
Can our internal team set up GitLab object storage without a partner?
Yes, a capable platform team can configure the consolidated object storage form and the separate settings for each data type, provided it has the time and the testing discipline. The risk lies in the details: different data types use different buckets or settings, and a missed one can leave data on local disk or break backups. Before deciding, check the provider or Ceph version against the GitLab documentation, confirm the test matrix, and estimate the staff hours your team can commit. A partner is most useful when the deadline is fixed or the environment is unusual.
Which storage providers does GitLab test, and what changes for Ceph?
GitLab documents the consolidated object storage form for S3, Google Cloud Storage and Azure, and those are the providers most teams treat as the default path. Ceph RGW is S3-compatible and is documented by the community rather than tested by GitLab on the same basis, so it needs more validation in your own environment. Confirm the exact versions and any known gaps against the GitLab documentation before committing. Ask your partner to show the test results it has for your provider and version.
What risks should we check before choosing object storage?
The main risks are data left on local disk after cutover, backups that do not include every bucket, incompatible API behavior on a non-tested provider, and credentials with more access than needed. Upgrade timing also matters, because GitLab upgrades pass through required upgrade stops, and storage settings must still work after each stop. Ask for a test plan that covers upload, download, backup and restore for every data type, and a rollback path for the cutover.
How DeployOpen scopes an object storage change
DeployOpen agrees the scope of an object storage change in writing before any configuration starts. For GitLab, that scope names each storage provider in use (S3, GCS, Azure Blob or a Ceph RGW endpoint), the data types moving to object storage, the environments affected and the verification tests that must pass before cutover. Most engagements are fixed-scope, so the deliverables, responsibilities and acceptance criteria are written down ahead of time and a quote follows from that document. The reader should expect the scope to name the consolidated object storage form and every data type that still needs its own settings, because a change that covers only part of the data is the most common source of surprises after go-live.
The scope should list the environments separately: development, staging and production, plus any Geo secondary or restore environment that must read the same buckets. For each environment, the reader should confirm bucket names, endpoints, access policies, encryption requirements and network paths from the GitLab nodes, because these differ between a managed cloud service and a self-hosted Ceph RGW. Verification tests belong in the scope as well: uploads and downloads of artifacts, LFS objects, packages, uploads and job logs, a backup and restore exercise that reads back from object storage, and a check of the connection behavior after a provider endpoint change. Each test should have an agreed pass condition, so the reader knows what evidence will exist at the end.
The deliverables are a configuration set applied to the GitLab instance, test evidence showing which checks ran and what they returned, and a runbook that covers day-two work such as credential rotation, bucket lifecycle rules, backup verification and rollback steps. DeployOpen does not promise a specific outcome, timeline or duration for the change, because those depend on the size of existing data, the provider's throughput, the network path and the access arrangements the client can provide. Before asking for a quote, the reader should confirm the current storage layout, the GitLab version in use, who holds the cloud or Ceph credentials and which change window is available. Those answers determine the effort, and the written scope is where they are recorded.
How DeployOpen delivers GitLab storage work
DeployOpen treats object storage for self-managed GitLab as a planned change to a running system, not a configuration file edit. We scope the storage types, the provider and the access model in writing first, then build and verify each data type against your own GitLab instance inside your infrastructure.
How we scope
- Written scope lists each GitLab data type that will move to object storage and where it will live
- Provider choice is documented against the platform you run: S3, GCS, Azure Blob or a Ceph RGW cluster
- Engagements are fixed-scope, with scope, deliverables and timelines agreed in writing before work starts
How we build
- We configure the consolidated object storage form, then set separate settings for data types that still need them
- Each bucket or container is tested with uploads, downloads and a backup restore before cutover
- Where a provider is community-documented rather than tested by GitLab, we record the gaps we found and the checks we ran
How we hand over
- You receive the configuration, bucket layout, access policies and a record of every data type and its storage target
- Runbooks cover credential rotation, lifecycle rules and what to check after each GitLab upgrade stop
- Verification evidence is delivered so your team and any independent auditor can review the storage design
How we support
- Ongoing support, upgrades and continuous testing are available as retainers after handover
- Retainers can cover re-testing object storage settings when you move to a new GitLab release
- Support work is scoped in writing, so changes to providers or storage layouts are priced from a clear baseline

