Skip to main content

Blob Storage

Upload release artifacts to S3, GCS, or Azure Blob Storage

The blob storage stage uploads release artifacts to cloud object storage. It supports Amazon S3 (and compatible backends), Google Cloud Storage, and Azure Blob Storage.

Classification

GroupRequired (default)RollbackToken scope
Assetsfalsedelete each object written (post-upload evidence snapshot)backend credentials (see Authentication)

See Release resilience for the full classification table and the Submitter gate semantics.

The required: field

Default: false — a blob storage upload failure is logged but does not fail the release.

Set required: true to make the release exit non-zero if this publisher fails:

blobs:
  - provider: s3
    bucket: my-release-bucket
    required: true

See Publish overview — the required: field for the full semantics.

Minimal config

crates:
  - name: myapp
    blobs:
      - provider: s3
        bucket: my-release-bucket

Full config reference

blobs:
  - id: ""                                     # optional; unique identifier
    provider: s3                               # required; s3 | gcs (gs) | azblob (azure)
    bucket: "my-bucket"                        # required; supports templates
    directory: "{{ ProjectName }}/{{ Tag }}"   # optional; key prefix (template)
    region: us-east-1                          # optional; AWS region (template)
    endpoint: ""                               # optional; custom endpoint for S3-compatible backends
    disable_ssl: false                         # optional; disable TLS (S3 only)
    s3_force_path_style: false                 # optional; auto-true when endpoint is set
    acl: ""                                    # optional; e.g. public-read, private
    cache_control: ""                          # optional; string or list
    content_disposition: "attachment;filename={{Filename}}"  # optional; "-" to disable
    kms_key: ""                                # optional; AWS KMS key ARN (S3 only)
    ids: []                                    # optional; filter by artifact IDs
    exclude: []                                # optional; drop artifacts whose name matches a glob
    disable: false                             # optional; bool or template string
    include_meta: false                        # optional; also upload metadata.json/artifacts.json
    extra_files: []                            # optional; additional files to upload
    extra_files_only: false                    # optional; skip artifacts, upload only extra_files

Excluding sidecars with exclude

A heavy release attaches a checksum, a signature, and an SBOM next to every archive — three sidecars per asset. When you mirror to a bucket you often want the archives there but not the sidecars, both to save space and to stay under provider rate limits. exclude is a list of globs matched against each artifact's file name (e.g. anodizer_0.12.4_x86_64.tar.gz); anodizer drops every artifact whose name matches at least one glob from this blob target only — other destinations still receive the full set.

blobs:
  - provider: s3
    bucket: my-mirror
    exclude:
      - "*.sha256"      # checksum sidecars
      - "*.sig"         # cosign / GPG signatures
      - "*.cdx.json"    # CycloneDX SBOMs

exclude composes with ids: — an artifact uploads only when it passes both filters. An empty or unset exclude keeps everything. Globs are validated at config-load, so a malformed pattern is rejected with a clear error rather than silently keeping (or dropping) the wrong assets; if a non-empty candidate set is reduced to zero, anodizer warns that the filter — not an empty release — is why nothing uploaded.

Authentication

Credentials are read from environment variables using each provider's standard chain.

Amazon S3

VariableDescription
AWS_ACCESS_KEY_IDAccess key ID.
AWS_SECRET_ACCESS_KEYSecret access key.
AWS_SESSION_TOKENSession token (for assumed roles).
AWS_REGIONDefault region (overridden by region field).
AWS_PROFILENamed profile from ~/.aws/credentials.

IAM instance profiles and ECS task roles are also supported automatically.

Google Cloud Storage

VariableDescription
GOOGLE_SERVICE_ACCOUNTPath to service account JSON key file.
GOOGLE_SERVICE_ACCOUNT_PATHAlias for GOOGLE_SERVICE_ACCOUNT.
GOOGLE_SERVICE_ACCOUNT_KEYJSON-serialized service account key (inline, not a file path).

Application Default Credentials (ADC) via gcloud auth application-default login are also supported.

Azure Blob Storage

VariableDescription
AZURE_STORAGE_ACCOUNT_NAMEStorage account name.
AZURE_STORAGE_ACCOUNT_KEYStorage account key.
AZURE_STORAGE_SAS_KEYShared Access Signature token (alias: AZURE_STORAGE_SAS_TOKEN).
AZURE_STORAGE_CONNECTION_STRINGFull connection string.
AZURE_CLIENT_IDService principal client ID (with AZURE_CLIENT_SECRET and AZURE_TENANT_ID).
AZURE_CLIENT_SECRETService principal client secret.
AZURE_TENANT_IDAzure AD tenant ID.

Common gotchas

  • s3_force_path_style defaults to true when endpoint is set. Virtual-hosted style (https://<bucket>.s3.amazonaws.com) is only valid for AWS proper — S3-compatible backends (MinIO, R2, Spaces) require path style.
  • disable template: the disable field accepts a template string, enabling conditional skip: "{{ if IsSnapshot }}true{{ end }}" skips blob upload for snapshot builds.
  • content_disposition: "-": set to the literal string "-" to disable the Content-Disposition header entirely (useful for direct-browser-download use cases).

Config fields

FieldTypeDefaultDescription
idstringUnique identifier for referencing this config.
providerstringrequiredStorage provider: s3, gcs (or gs), azblob (or azure).
bucketstringrequiredBucket or container name. Supports templates.
directorystring{{ ProjectName }}/{{ Tag }}Object key prefix within the bucket. Supports templates.
regionstringAWS region (S3 only). Supports templates.
endpointstringCustom endpoint URL for S3-compatible backends. Supports templates.
disable_sslboolfalseDisable TLS for the connection (S3 only).
s3_force_path_stylebooltrue when endpoint setUse path-style addressing instead of virtual-hosted style. Automatically enabled when endpoint is set.
aclstringCanned ACL for uploaded objects (e.g. public-read, private).
cache_controlstring or listHTTP Cache-Control header. Accepts a single string or a list joined with , .
content_dispositionstringattachment;filename={{Filename}}HTTP Content-Disposition header. Set to - to disable. Supports templates (includes {{ Filename }}).
kms_keystringAWS KMS key ARN for server-side encryption (S3 only).
idslistallFilter to artifacts with these IDs.
disablebool or templatefalseSkip this blob config. Accepts a bool or a template string (e.g. "{{ if IsSnapshot }}true{{ end }}").
include_metaboolfalseAlso upload metadata.json and artifacts.json.
extra_fileslistAdditional files to upload. Supports glob patterns and optional name templates.
extra_files_onlyboolfalseUpload only extra_files; skip all artifact uploads.

Extra files

Each entry under extra_files can have:

FieldDescription
globGlob pattern for files to upload (required).
name / name_templateOverride the upload filename. Supports templates including {{ Filename }}.
extra_files:
  - glob: dist/checksums.txt
  - glob: "release-notes/*.md"
    name: "release-notes-{{ Version }}.md"

S3-compatible backends

Any S3-compatible service can be used by setting endpoint. When endpoint is set, s3_force_path_style defaults to true because most compatible services (MinIO, Cloudflare R2, DigitalOcean Spaces) require path-style addressing.

MinIO

blobs:
  - provider: s3
    bucket: my-bucket
    endpoint: http://minio.internal:9000
    region: us-east-1

Cloudflare R2

blobs:
  - provider: s3
    bucket: my-bucket
    endpoint: https://<account-id>.r2.cloudflarestorage.com
    region: auto

DigitalOcean Spaces

blobs:
  - provider: s3
    bucket: my-space
    endpoint: https://nyc3.digitaloceanspaces.com
    region: nyc3

Full example

crates:
  - name: myapp
    blobs:
      - provider: s3
        bucket: "my-releases-{{ ProjectName }}"
        directory: "{{ Version }}"
        region: us-east-1
        acl: public-read
        cache_control:
          - "public"
          - "max-age=31536000"
        kms_key: arn:aws:kms:us-east-1:123456789012:key/my-key-id
        include_meta: true
        extra_files:
          - glob: dist/checksums.txt
        skip: "{{ if IsSnapshot }}true{{ end }}"
crates:
  - name: myapp
    blobs:
      - provider: gcs
        bucket: my-gcs-bucket
        directory: "releases/{{ Tag }}"
        acl: publicRead

      - provider: azblob
        bucket: my-container
        directory: "{{ ProjectName }}/{{ Version }}"