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:

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 typeprovider valueUse with
S3-compatibleAWSAmazon S3 and any S3-compatible service
Google Cloud StorageGoogleGoogle Cloud Storage
Azure Blob StorageAzureRMAzure 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:

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.

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:

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 specified

If 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.

SettingDescription
enabledEnable or disable object storage.
proxy_downloadSet 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_modesTransfer 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.
connectionVarious connection options described below.
storage_optionsOptions to use when saving new objects, such as server side encryption.
objectsObject-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:

Within each object type, four parameters can be defined:

SettingRequired?Description
bucketcheck-smBucket name for the object type. Not required if enabled is set to false.
enabledNoOverrides the common parameter.
proxy_downloadNoOverrides the common parameter.
allowed_download_modesNoOverrides 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:

ruby
gitlab_rails['object_store']['objects']['artifacts']['enabled'] = false

A bucket is not needed if the feature is disabled entirely. For example, no bucket is needed if CI artifacts are disabled with this setting:

ruby
gitlab_rails['artifacts_enabled'] = false

Configure 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 typeSupported by consolidated form?
BackupsNo
Container registry (optional feature)No
MattermostNo
Autoscale runner caching (optional for improved performance)No
Secure Filescheck-sm
Job artifacts including archived job logscheck-sm
LFS objectscheck-sm
Uploadscheck-sm
Merge request diffscheck-sm
Packages (optional feature)check-sm
Dependency Proxy (optional feature)check-sm
Terraform state filescheck-sm
Pages contentcheck-sm

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:

SettingDescriptionDefault
providerAlways AWS for compatible hosts.AWS
aws_access_key_idAWS credentials, or compatible.
aws_secret_access_keyAWS credentials, or compatible.
aws_signature_versionAWS signature version to use. 2 or 4 are valid options. Some S3-compatible providers may need 2.4
enable_signature_v4_streamingSet 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
regionAWS region.
hostDEPRECATED: 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
endpointCan 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_styleSet 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_profileSet to true to use IAM profile instead of access keys.false
aws_credentials_refresh_threshold_secondsSets the automatic refresh threshold in seconds when using temporary credentials in IAM. Must be an integer.151
disable_imds_v2Force 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:

To set up an instance profile:

  1. Create an IAM role with the necessary permissions. The following example is a role for an S3 bucket named test-bucket:

    json
    {
        "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:GetObjectTagging to the object-level Action list for source buckets and s3:PutObjectTagging for destination buckets. Without these permissions, GitLab CopyObject operations fail with AccessDenied when copying tagged objects.

  2. Attach this role to the EC2 instance hosting your GitLab instance.

  3. Set the use_iam_profile GitLab configuration option to true.

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:

SettingDescription
server_side_encryptionEncryption mode (AES256 or aws:kms).
server_side_encryption_kms_key_idAmazon 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_profile is true in 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:

SettingDescriptionExample
providerProvider name.Google
google_projectGCP project name.gcp-project-12345
google_json_key_locationJSON key path./path/to/gcp-project-12345-abcde.json
google_json_key_stringJSON key string.{ "type": "service_account", "project_id": "example-project-382839", ... }
google_application_defaultSet to true to use Google Cloud Application Default Credentials to locate service account credentials.
universe_domainUniverse 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.signBlob permission. Typically this is done by granting the Service Account Token Creator role 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:

    shell
    Google::Apis::ClientError (insufficientPermissions: Request had insufficient authentication scopes.)

To use bucket encryption with customer-managed encryption keys, use the consolidated form.

  1. Edit /etc/gitlab/gitlab.rb and add the following lines, substituting the values you want:

    ruby
    gitlab_rails['object_store']['connection'] = {
     'provider' => 'Google',
     'google_project' => '<GOOGLE PROJECT>',
     'google_json_key_location' => '<FILENAME>'
    }

    To use ADC, use google_application_default instead:

    ruby
    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):

    ruby
    gitlab_rails['object_store']['connection'] = {
     'provider' => 'Google',
     'google_project' => '<GOOGLE PROJECT>',
     'google_application_default' => true,
     'universe_domain' => '<UNIVERSE DOMAIN>'
    }
  2. Save the file and reconfigure GitLab:

    shell
    sudo gitlab-ctl reconfigure
  1. Put the following content in a file named object_storage.yaml to be used as a Kubernetes Secret:

    yaml
    provider: Google
    google_project: <GOOGLE PROJECT>
    google_json_key_location: '<FILENAME>'

    To use ADC, use google_application_default instead:

    yaml
    provider: Google
    google_project: <GOOGLE PROJECT>
    google_application_default: true

    To use a non-default universe domain (for example, Google Cloud Dedicated):

    yaml
    provider: Google
    google_project: <GOOGLE PROJECT>
    google_application_default: true
    universe_domain: <UNIVERSE DOMAIN>
  2. Create the Kubernetes Secret:

    shell
    kubectl create secret generic -n <namespace> gitlab-object-storage --from-file=connection=object_storage.yaml
  3. Export the Helm values:

    shell
    helm get values gitlab > gitlab_values.yaml
  4. Edit gitlab_values.yaml:

    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-uploads
  5. Save the file and apply the new values:

    shell
    helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
  1. Edit docker-compose.yml:

    yaml
    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_default instead:

    ruby
    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):

    ruby
    gitlab_rails['object_store']['connection'] = {
      'provider' => 'Google',
      'google_project' => '<GOOGLE PROJECT>',
      'google_application_default' => true,
      'universe_domain' => '<UNIVERSE DOMAIN>'
    }
  2. Save the file and restart GitLab:

    shell
    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.

SettingDescriptionExample
providerProvider name.AzureRM
azure_storage_account_nameName of the Azure Blob Storage account used to access the storage.azuretest
azure_storage_access_keyStorage 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_domainDomain 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
  1. Edit /etc/gitlab/gitlab.rb and add the following lines, substituting the values you want:

    ruby
    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:

    ruby
    gitlab_rails['object_store']['connection'] = {
      'provider' => 'AzureRM',
      'azure_storage_account_name' => '<AZURE STORAGE ACCOUNT NAME>',
      'azure_storage_domain' => '<AZURE STORAGE DOMAIN>'
    }
  2. Save the file and reconfigure GitLab:

    shell
    sudo gitlab-ctl reconfigure
  1. Put the following content in a file named object_storage.yaml to be used as a Kubernetes Secret:

    yaml
    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.net

    If you are using a workload or managed identity, omit azure_storage_access_key:

    yaml
    provider: AzureRM
    azure_storage_account_name: <YOUR_AZURE_STORAGE_ACCOUNT_NAME>
    azure_storage_domain: blob.core.windows.net
  2. Create the Kubernetes Secret:

    shell
    kubectl create secret generic -n <namespace> gitlab-object-storage --from-file=connection=object_storage.yaml
  3. Export the Helm values:

    shell
    helm get values gitlab > gitlab_values.yaml
  4. Edit gitlab_values.yaml:

    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-uploads
  5. Save the file and apply the new values:

    shell
    helm upgrade -f gitlab_values.yaml gitlab gitlab/gitlab
  1. Edit docker-compose.yml:

    yaml
    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'