Skip to content

S3 / MinIO

Driver: s3

This storage driver stores the cache in a S3 compatible storage, e.g. AWS S3 or MinIO.

Configuration

docker-compose MinIO example

docker-compose.yml
yaml
version: '3.9'

services:
  cache-server:
    image: ghcr.io/falcondev-oss/github-actions-cache-server:latest
    ports:
      - '3000:3000'
    environment:
      API_BASE_URL: http://localhost:3000

      STORAGE_DRIVER: s3
      STORAGE_S3_BUCKET: gh-actions-cache

      AWS_ACCESS_KEY_ID: access_key
      AWS_SECRET_ACCESS_KEY: secret_key
      AWS_ENDPOINT_URL: http://minio:9000
    volumes:
      - cache-data:/app/.data

  minio:
    image: quay.io/minio/minio
    ports:
      - '9000:9000'
    environment:
      MINIO_ROOT_USER: access_key
      MINIO_ROOT_PASSWORD: secret_key

volumes:
  cache-data:

docker-compose AWS S3 example

This example assumes that credentials are being provided by the environment, e.g. via an instance profile or EKS IRSA.

docker-compose.yml
yaml
version: '3.9'

services:
  cache-server:
    image: ghcr.io/falcondev-oss/github-actions-cache-server:latest
    ports:
      - '3000:3000'
    environment:
      API_BASE_URL: http://localhost:3000

      STORAGE_DRIVER: s3
      STORAGE_S3_BUCKET: gh-actions-cache

    volumes:
      - cache-data:/app/.data

volumes:
  cache-data:

Environment Variables

The only required S3-related environment variables are STORAGE_DRIVER: s3 and STORAGE_S3_BUCKET. The rest of the environment variables are optional and depend on your S3-compatible storage provider.

The AWS SDK will automatically use any AWS credentials available in the environment, e.g. AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and AWS_REGION. Outside of AWS, these environment variables can still be used to authenticate with S3-compatible storage, as seen in the Minio example above.

Common environment variables are listed below. For a full list of configuration options, see the AWS SDK documentation.

STORAGE_S3_BUCKET

Example: gh-actions-cache

The name of the S3 bucket used for storage. This environment variable is always required.

AWS_REGION

Example: us-east-1

The AWS SDK relies on this variable being set. In the cache server, it defaults to us-east-1 if not provided. This has no effect if you are using a non-AWS S3-compatible storage provider, such as MinIO.

AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY

Example: AWS_ACCESS_KEY_ID: access_keyAWS_SECRET_ACCESS_KEY: secret_key

This is the access key/secret key used to authenticate with S3-compatible storage. If required to authenticate with your provider, these should be provided by the provider. Alternatively, you can use the AWS_PROFILE environment variable to specify a profile from your AWS credentials file.

AWS_PROFILE

Example: my-profile

If you wish to run the cache server locally and utilize a profile from your AWS credentials file or local AWS CLI configuration, you can set the AWS_PROFILE environment variable to the name of the profile. Note that this will also require mounting the AWS credentials file into the container in order for the SDK to be able to find it.

docker-compose.yml
yaml
version: '3.9'

services:
  cache-server:
    image: ghcr.io/falcondev-oss/github-actions-cache-server:latest
    ports:
      - '3000:3000'
    environment:
      API_BASE_URL: http://localhost:3000

      STORAGE_DRIVER: s3
      STORAGE_S3_BUCKET: gh-actions-cache

      AWS_PROFILE: my-profile

    volumes:
      - cache-data:/app/.data
      # Mount the AWS CLI credentials and config into the container
      - ~/.aws:/root/.aws:ro

volumes:
  cache-data:

AWS_ENDPOINT_URL

Example: http://minio:9000

This is the endpoint URL for the S3-compatible storage. This is only required if you are using a non-AWS S3-compatible storage provider, such as MinIO.

Minimal IAM permissions

The cache server only ever touches objects under the gh-actions-cache/ prefix in the bucket. These are all the actions it performs:

ActionUsed for
s3:ListBucketStartup bucket check, listing/counting cache objects
s3:GetObjectDownloading cache entries, existence checks
s3:PutObjectUploading cache entries (multipart)
s3:AbortMultipartUploadCleaning up failed multipart uploads
s3:DeleteObjectCache cleanup and abandoned uploads

A minimal policy, scoped to the bucket and prefix (replace YOUR_BUCKET):

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "github-actions-cache-server-bucket",
      "Effect": "Allow",
      "Action": ["s3:ListBucket"],
      "Resource": "arn:aws:s3:::YOUR_BUCKET"
    },
    {
      "Sid": "github-actions-cache-server-objects",
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject", "s3:AbortMultipartUpload", "s3:DeleteObject"],
      "Resource": "arn:aws:s3:::YOUR_BUCKET/gh-actions-cache/*"
    }
  ]
}

Troubleshooting

Checksum errors with S3-compatible storage (Garage, MinIO)

Symptoms

Large cache entries fail to restore and the download hangs, with the runner eventually reporting a Premature close. Smaller entries work fine. Server logs show:

Checksum mismatch: expected "…" but received "…" in response header "x-amz-checksum-crc32"

Cause

The cache server uploads larger entries as multipart objects. Some S3-compatible backends — notably Garage and some MinIO setups — compute multipart checksums differently from AWS S3. On download, the AWS SDK (which validates checksums by default) recomputes the checksum over the body, disagrees, and rejects the response mid-stream. Entries under the 5 MB multipart threshold aren't stored as multipart objects, which is why small caches are unaffected.

Fix

Tell the AWS SDK to only apply checksums when strictly required, restoring the behavior these backends expect:

docker-compose.yml
yaml
environment:
  AWS_REQUEST_CHECKSUM_CALCULATION: WHEN_REQUIRED
  AWS_RESPONSE_CHECKSUM_VALIDATION: WHEN_REQUIRED