Object storage
- Tier: Free, Premium, Ultimate
- Offering: GitLab Self-Managed
GitLab supports using an object storage service for holding numerous types of data. It’s recommended over NFS and in general it’s better in larger setups as object storage is typically much more performant, reliable, and scalable.
To configure the object storage, you have two options:
Recommended. Configure a single storage connection for all object types: A single credential is shared by all supported object types. This is called the consolidated form.
Configure each object type to define its own storage connection: Every object defines its own object storage connection and configuration. This is called the storage-specific form.
If you already use the storage-specific form, see how to transition to the consolidated form.
If you store data locally, see how to migrate to object storage.
Object storage provider support
GitLab uses the Fog library for object storage and supports the following three connection types. Other Fog providers are not supported.
| Connection type | provider value | Use with |
|---|---|---|
| S3-compatible | AWS | Amazon S3 and any S3-compatible service |
| Google Cloud Storage | Google | Google Cloud Storage |
| Azure Blob Storage | AzureRM | Azure Blob Storage |
If your object storage service is compatible with one of these connection types, configure it using the corresponding connection settings below. The choice of provider is yours.
Providers with active test coverage
GitLab actively tests the following providers:
- Amazon S3 -
AWSconnection type. Object Lock is not supported. For more information, see issue 335775. - Google Cloud Storage -
Googleconnection type. - Azure Blob Storage -
AzureRMconnection type.
Community-documented providers
The following providers have been documented by the community. GitLab does not test these providers. Configuration examples are provided as a convenience. If you use one of these providers and encounter issues, GitLab Support might be unable to help.
- Digital Ocean Spaces. S3-compatible, see provider-specific configuration examples.
- Oracle Cloud Infrastructure. S3-compatible, see provider-specific configuration examples.
- OpenStack Swift (S3-compatible mode).
- Storj Gateway. S3-compatible, see provider-specific configuration examples.
- Ceph RGW. S3-compatible, see provider-specific configuration examples
- Hitachi Vantara HCP. S3-compatible, see provider-specific configuration examples.
- On-premises hardware and appliances that expose an S3-compatible API.
Configure a single storage connection for all object types (consolidated form)
Most types of objects, such as CI artifacts, LFS files, and upload attachments can be saved in object storage by specifying a single credential for object storage with multiple buckets.
For GitLab Helm Charts, see how to configure the consolidated form.
Configuring the object storage using the consolidated form has a number of advantages:
- It can simplify your GitLab configuration because the connection details are shared across object types.
- It enables the use of encrypted S3 buckets.
- It uploads files to S3 with proper
Content-MD5headers.
When the consolidated form is used, direct upload is enabled automatically. Thus, only the following providers can be used:
The consolidated form configuration can’t be used for backups or Mattermost. Backups can be configured with server side encryption separately. See the table for a complete list of supported object storage types.
Enabling the consolidated form enables object storage for all object types. If not all buckets are specified, you may see an error like:
Object storage for <object type> must have a bucket specifiedIf you want to use local storage for specific object types, you can disable object storage for specific features.
Configure the common parameters
In the consolidated form, the object_store section defines a
common set of parameters.
| Setting | Description |
|---|---|
enabled | Enable or disable object storage. |
proxy_download | Set to true to enable proxying all files served. When set to false, clients download directly from remote storage, reducing egress traffic on the GitLab server. |
allowed_download_modes | Transfer modes that API clients are allowed to request with the download_mode parameter on supported endpoints. Defaults to only the mode set by proxy_download. Set to [proxy, direct] to let clients request direct downloads. |
connection | Various connection options described below. |
storage_options | Options to use when saving new objects, such as server side encryption. |
objects | Object-specific configuration. |
For an example, see how to use the consolidated form and Amazon S3.
Configure the parameters of each object
Each object type must at least define the bucket name where it will be stored.
The following table lists the valid objects that can be used:
| Type | Description |
|---|---|
artifacts | CI/CD job artifacts |
external_diffs | Merge request diffs |
uploads | User uploads |
lfs | Git Large File Storage objects |
packages | Project packages (for example, PyPI, Maven, or NuGet) |
dependency_proxy | Dependency Proxy |
terraform_state | Terraform state files |
pages | Pages |
ci_secure_files | Secure files |
ci_catalog_bundles | CI/CD component bundles |
Within each object type, four parameters can be defined:
| Setting | Required? | Description |
|---|---|---|
bucket | Bucket name for the object type. Not required if enabled is set to false. | |
enabled | No | Overrides the common parameter. |
proxy_download | No | Overrides the common parameter. |
allowed_download_modes | No | Overrides the common parameter. |
For an example, see how to use the consolidated form and Amazon S3.
Disable object storage for specific features
As seen previously, object storage can be disabled for specific types by
setting the enabled flag to false. For example, to disable object
storage for CI artifacts:
gitlab_rails['object_store']['objects']['artifacts']['enabled'] = falseA bucket is not needed if the feature is disabled entirely. For example, no bucket is needed if CI artifacts are disabled with this setting:
gitlab_rails['artifacts_enabled'] = falseConfigure each object type to define its own storage connection (storage-specific form)
With the storage-specific form, every object defines its own object storage connection and configuration. You should use the consolidated form instead, except for the storage types not supported by the consolidated form. When working with the GitLab Helm charts, refer to how the charts handle consolidated form for object storage.
The use of encrypted S3 buckets with non-consolidated form is not supported. You may get ETag mismatch errors if you use it.
For the storage-specific form, direct upload may become the default because it does not require a shared folder.
For storage types not supported by the consolidated form, refer to the following guides:
| Object storage type | Supported by consolidated form? |
|---|---|
| Backups | No |
| Container registry (optional feature) | No |
| Mattermost | No |
| Autoscale runner caching (optional for improved performance) | No |
| Secure Files | |
| Job artifacts including archived job logs | |
| LFS objects | |
| Uploads | |
| Merge request diffs | |
| Packages (optional feature) | |
| Dependency Proxy (optional feature) | |
| Terraform state files | |
| Pages content |
Configure the connection settings
Both consolidated and storage-specific form must configure a connection. The following sections describe parameters that can be used
in the connection setting.
S3-compatible providers
These settings apply to Amazon S3 and any S3-compatible service using the AWS connection type.
When not using AWS directly, set endpoint to your provider’s URL.
S3-compatible services vary in how closely they implement the AWS S3 API. GitLab uses specific S3 behaviors, including pre-signed URLs, multipart uploads, and optionally chunked signature streaming, that not every S3-compatible implementation supports identically. If a provider works in other tools but not in GitLab, the settings that most likely need adjustment are:
aws_signature_version.enable_signature_v4_streaming.
The connection settings match those provided by fog-aws:
| Setting | Description | Default |
|---|---|---|
provider | Always AWS for compatible hosts. | AWS |
aws_access_key_id | AWS credentials, or compatible. | |
aws_secret_access_key | AWS credentials, or compatible. | |
aws_signature_version | AWS signature version to use. 2 or 4 are valid options. Some S3-compatible providers may need 2. | 4 |
enable_signature_v4_streaming | Set to true to enable HTTP chunked transfers with AWS v4 signatures. Some S3-compatible providers require this to be false. GitLab 17.4 changed the default from true to false. | false |
region | AWS region. | |
host | DEPRECATED: Use endpoint instead. S3 compatible host for when not using AWS. For example, localhost or storage.example.com. HTTPS and port 443 is assumed. | s3.amazonaws.com |
endpoint | Can be used when configuring an S3-compatible service, by entering a URL such as http://127.0.0.1:9000. This takes precedence over host. Always use endpoint for consolidated form. | (optional) |
path_style | Set to true to use host/bucket_name/object style paths instead of bucket_name.host/object. Set to true for S3-compatible services that require path-style addressing. Leave as false for AWS S3. | false |
use_iam_profile | Set to true to use IAM profile instead of access keys. | false |
aws_credentials_refresh_threshold_seconds | Sets the automatic refresh threshold in seconds when using temporary credentials in IAM. Must be an integer. | 151 |
disable_imds_v2 | Force the use of IMDS v1 by disabling access to the IMDS v2 endpoint that retrieves X-aws-ec2-metadata-token. | false |
S3 compatibility and known failure modes
Claiming S3 compatibility does not guarantee a provider works correctly with GitLab. If you encounter errors with an S3-compatible provider, try the following adjustments before raising a support request:
- Signature streaming: Some providers reject chunked transfer encoding used by AWS Signature
Version 4 streaming. Set
enable_signature_v4_streaming: false. - Signature version: Some providers do not fully support Signature Version 4. Set
aws_signature_version: 2. - Path-style URLs: Some providers require path-style bucket addressing. Set
path_style: true. - ETag validation: Some providers return ETags that do not match the MD5 of the uploaded object, which GitLab validates. See ETag mismatch.
GitLab Support can help troubleshoot configuration, but cannot guarantee resolution for issues specific to providers not in the tested set.
Use Amazon instance profiles
Instead of supplying AWS access and secret keys in object storage configuration, you can configure GitLab to use Amazon Identity Access and Management (IAM) roles to set up an Amazon instance profile. When this is used, GitLab fetches temporary credentials each time an S3 bucket is accessed, so no hard-coded values are needed in the configuration.
Prerequisites:
- GitLab must be able to connect to the instance metadata endpoint.
- If GitLab is configured to use an internet proxy, the endpoint IP
address must be added to the
no_proxylist. - For IMDS v2 access, ensure the hop limit is sufficient. If GitLab is running in a container, you may need to raise the limit from 1 to 2.
To set up an instance profile:
Create an IAM role with the necessary permissions. The following example is a role for an S3 bucket named
test-bucket:{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::test-bucket/*" }, { "Effect": "Allow", "Action": [ "s3:ListBucket" ], "Resource": "arn:aws:s3:::test-bucket" } ] }If an external process tags objects in your S3 buckets (for example, AWS GuardDuty Malware Protection), add
s3:GetObjectTaggingto the object-levelActionlist for source buckets ands3:PutObjectTaggingfor destination buckets. Without these permissions, GitLabCopyObjectoperations fail withAccessDeniedwhen copying tagged objects.Attach this role to the EC2 instance hosting your GitLab instance.
Set the
use_iam_profileGitLab configuration option totrue.
Encrypted S3 buckets
When configured either with an instance profile or with the consolidated form, GitLab Workhorse properly uploads files to S3 buckets that have SSE-S3 or SSE-KMS encryption enabled by default. AWS KMS keys and SSE-C encryption are not supported because this requires sending the encryption keys in every request.
Server-side encryption headers
Setting a default encryption on an S3 bucket is the easiest way to
enable encryption, but you may want to
set a bucket policy to ensure only encrypted objects are uploaded.
To do this, you must configure GitLab to send the proper encryption headers
in the storage_options configuration section:
| Setting | Description |
|---|---|
server_side_encryption | Encryption mode (AES256 or aws:kms). |
server_side_encryption_kms_key_id | Amazon Resource Name. Only needed when aws:kms is used in server_side_encryption. See the Amazon documentation on using KMS encryption. |
As with the case for default encryption, these options only work when the Workhorse S3 client is enabled. One of the following two conditions must be fulfilled:
use_iam_profileistruein the connection settings.- Consolidated form is in use.
ETag mismatch errors occur if server side encryption headers are used without enabling the Workhorse S3 client.
Google Cloud Storage (GCS)
Here are the valid connection parameters for GCS:
| Setting | Description | Example |
|---|---|---|
provider | Provider name. | Google |
google_project | GCP project name. | gcp-project-12345 |
google_json_key_location | JSON key path. | /path/to/gcp-project-12345-abcde.json |
google_json_key_string | JSON key string. | { "type": "service_account", "project_id": "example-project-382839", ... } |
google_application_default | Set to true to use Google Cloud Application Default Credentials to locate service account credentials. | |
universe_domain | Universe domain to use for Google Cloud requests. Use this to connect to Google Cloud Dedicated or other non-default universe domains. | googleapis.com |
GitLab reads the value of google_json_key_location, then google_json_key_string, and finally, google_application_default.
It uses the first of these settings that has a value.
The service account must have permission to access the bucket. For more information, see the Cloud Storage authentication documentation.
Google Cloud Application Default Credentials
Google Cloud Application Default Credentials (ADC) are typically
used with GitLab to use the default service account or Workload Identity Federation.
Set google_application_default to true and omit google_json_key_location and google_json_key_string.
If you use ADC, be sure that:
The service account that you use has the
iam.serviceAccounts.signBlobpermission. Typically this is done by granting theService Account Token Creatorrole to the service account.If you are using Google Compute virtual machines, ensure they have the correct access scopes to access Google Cloud APIs. If the machines do not have the right scope, the error logs may show:
Google::Apis::ClientError (insufficientPermissions: Request had insufficient authentication scopes.)
To use bucket encryption with customer-managed encryption keys, use the consolidated form.
Edit
/etc/gitlab/gitlab.rband add the following lines, substituting the values you want:gitlab_rails['object_store']['connection'] = { 'provider' => 'Google', 'google_project' => '<GOOGLE PROJECT>', 'google_json_key_location' => '<FILENAME>' }To use ADC, use
google_application_defaultinstead:gitlab_rails['object_store']['connection'] = { 'provider' => 'Google', 'google_project' => '<GOOGLE PROJECT>', 'google_application_default' => true }To use a non-default universe domain (for example, Google Cloud Dedicated):
gitlab_rails['object_store']['connection'] = { 'provider' => 'Google', 'google_project' => '<GOOGLE PROJECT>', 'google_application_default' => true, 'universe_domain' => '<UNIVERSE DOMAIN>' }Save the file and reconfigure GitLab:
sudo gitlab-ctl reconfigure
Put the following content in a file named
object_storage.yamlto be used as a Kubernetes Secret:provider: Google google_project: <GOOGLE PROJECT> google_json_key_location: '<FILENAME>'To use ADC, use
google_application_defaultinstead:provider: Google google_project: <GOOGLE PROJECT> google_application_default: trueTo use a non-default universe domain (for example, Google Cloud Dedicated):
provider: Google google_project: <GOOGLE PROJECT> google_application_default: true universe_domain: <UNIVERSE DOMAIN>Create the Kubernetes Secret:
kubectl create secret generic -n <namespace> gitlab-object-storage --from-file=connection=object_storage.yamlExport the Helm values:
helm get values gitlab > gitlab_values.yamlEdit
gitlab_values.yaml:global: appConfig: artifacts: bucket: gitlab-artifacts ciSecureFiles: bucket: gitlab-ci-secure-files enabled: true dependencyProxy: bucket: gitlab-dependency-proxy enabled: true externalDiffs: bucket: gitlab-mr-diffs enabled: true lfs: bucket: gitlab-lfs object_store: connection: secret: gitlab-object-storage enabled: true proxy_download: false packages: bucket: gitlab-packages terraformState: bucket: gitlab-terraform-state enabled: true uploads: bucket: gitlab-uploadsSave the file and apply the new values:
helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
Edit
docker-compose.yml:version: "3.6" services: gitlab: environment: GITLAB_OMNIBUS_CONFIG: | # Consolidated object storage configuration gitlab_rails['object_store']['enabled'] = true gitlab_rails['object_store']['proxy_download'] = false gitlab_rails['object_store']['connection'] = { 'provider' => 'Google', 'google_project' => '<GOOGLE PROJECT>', 'google_json_key_location' => '<FILENAME>' } gitlab_rails['object_store']['objects']['artifacts']['bucket'] = 'gitlab-artifacts' gitlab_rails['object_store']['objects']['external_diffs']['bucket'] = 'gitlab-mr-diffs' gitlab_rails['object_store']['objects']['lfs']['bucket'] = 'gitlab-lfs' gitlab_rails['object_store']['objects']['uploads']['bucket'] = 'gitlab-uploads' gitlab_rails['object_store']['objects']['packages']['bucket'] = 'gitlab-packages' gitlab_rails['object_store']['objects']['dependency_proxy']['bucket'] = 'gitlab-dependency-proxy' gitlab_rails['object_store']['objects']['terraform_state']['bucket'] = 'gitlab-terraform-state' gitlab_rails['object_store']['objects']['ci_secure_files']['bucket'] = 'gitlab-ci-secure-files' gitlab_rails['object_store']['objects']['pages']['bucket'] = 'gitlab-pages'To use ADC, use
google_application_defaultinstead:gitlab_rails['object_store']['connection'] = { 'provider' => 'Google', 'google_project' => '<GOOGLE PROJECT>', 'google_application_default' => true }To use a non-default universe domain (for example, Google Cloud Dedicated):
gitlab_rails['object_store']['connection'] = { 'provider' => 'Google', 'google_project' => '<GOOGLE PROJECT>', 'google_application_default' => true, 'universe_domain' => '<UNIVERSE DOMAIN>' }Save the file and restart GitLab:
docker compose up -d
Azure Blob storage
Although Azure uses the word container to denote a collection of
blobs, GitLab standardizes on the term bucket. Be sure to configure
Azure container names in the bucket settings.
Azure Blob storage can only be used with the consolidated form because a single set of credentials are used to access multiple containers. The storage-specific form is not supported. For more details, see how to transition to consolidated form.
The following are the valid connection parameters for Azure. For more information, see the Azure Blob Storage documentation.
| Setting | Description | Example |
|---|---|---|
provider | Provider name. | AzureRM |
azure_storage_account_name | Name of the Azure Blob Storage account used to access the storage. | azuretest |
azure_storage_access_key | Storage account access key used to access the container. This is typically a secret, 512-bit encryption key encoded in base64. This is optional for Azure workload and managed identities. | czV2OHkvQj9FKEgrTWJRZVRoV21ZcTN0Nnc5eiRDJkYpSkBOY1JmVWpYbjJy\nNHU3eCFBJUQqRy1LYVBkU2dWaw==\n |
azure_storage_domain | Domain name used to contact the Azure Blob Storage API (optional). Defaults to blob.core.windows.net. Set this if you are using Azure China, Azure Germany, Azure US Government, or some other custom Azure domain. | blob.core.windows.net |
Edit
/etc/gitlab/gitlab.rband add the following lines, substituting the values you want:gitlab_rails['object_store']['connection'] = { 'provider' => 'AzureRM', 'azure_storage_account_name' => '<AZURE STORAGE ACCOUNT NAME>', 'azure_storage_access_key' => '<AZURE STORAGE ACCESS KEY>', 'azure_storage_domain' => '<AZURE STORAGE DOMAIN>' } gitlab_rails['object_store']['objects']['artifacts']['bucket'] = 'gitlab-artifacts' gitlab_rails['object_store']['objects']['external_diffs']['bucket'] = 'gitlab-mr-diffs' gitlab_rails['object_store']['objects']['lfs']['bucket'] = 'gitlab-lfs' gitlab_rails['object_store']['objects']['uploads']['bucket'] = 'gitlab-uploads' gitlab_rails['object_store']['objects']['packages']['bucket'] = 'gitlab-packages' gitlab_rails['object_store']['objects']['dependency_proxy']['bucket'] = 'gitlab-dependency-proxy' gitlab_rails['object_store']['objects']['terraform_state']['bucket'] = 'gitlab-terraform-state' gitlab_rails['object_store']['objects']['ci_secure_files']['bucket'] = 'gitlab-ci-secure-files' gitlab_rails['object_store']['objects']['pages']['bucket'] = 'gitlab-pages'If you are using a workload identity, omit
azure_storage_access_key:gitlab_rails['object_store']['connection'] = { 'provider' => 'AzureRM', 'azure_storage_account_name' => '<AZURE STORAGE ACCOUNT NAME>', 'azure_storage_domain' => '<AZURE STORAGE DOMAIN>' }Save the file and reconfigure GitLab:
sudo gitlab-ctl reconfigure
Put the following content in a file named
object_storage.yamlto be used as a Kubernetes Secret:provider: AzureRM azure_storage_account_name: <YOUR_AZURE_STORAGE_ACCOUNT_NAME> azure_storage_access_key: <YOUR_AZURE_STORAGE_ACCOUNT_KEY> azure_storage_domain: blob.core.windows.netIf you are using a workload or managed identity, omit
azure_storage_access_key:provider: AzureRM azure_storage_account_name: <YOUR_AZURE_STORAGE_ACCOUNT_NAME> azure_storage_domain: blob.core.windows.netCreate the Kubernetes Secret:
kubectl create secret generic -n <namespace> gitlab-object-storage --from-file=connection=object_storage.yamlExport the Helm values:
helm get values gitlab > gitlab_values.yamlEdit
gitlab_values.yaml:global: appConfig: artifacts: bucket: gitlab-artifacts ciSecureFiles: bucket: gitlab-ci-secure-files enabled: true dependencyProxy: bucket: gitlab-dependency-proxy enabled: true externalDiffs: bucket: gitlab-mr-diffs enabled: true lfs: bucket: gitlab-lfs object_store: connection: secret: gitlab-object-storage enabled: true proxy_download: false packages: bucket: gitlab-packages terraformState: bucket: gitlab-terraform-state enabled: true uploads: bucket: gitlab-uploadsSave the file and apply the new values:
helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
Edit
docker-compose.yml:version: "3.6" services: gitlab: environment: GITLAB_OMNIBUS_CONFIG: | # Consolidated object storage configuration gitlab_rails['object_store']['enabled'] = true gitlab_rails['object_store']['proxy_download'] = false gitlab_rails['object_store']['connection'] = { 'provider' => 'AzureRM', 'azure_storage_account_name' => '<AZURE STORAGE ACCOUNT NAME>', 'azure_storage_access_key' => '<AZURE STORAGE ACCESS KEY>', 'azure_storage_domain' => '<AZURE STORAGE DOMAIN>' } gitlab_rails['object_store']['objects']['artifacts']['bucket'] = 'gitlab-artifacts' gitlab_rails['object_store']['objects']['external_diffs']['bucket'] = 'gitlab-mr-diffs'