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), merging parts
s3:AbortMultipartUploadCleaning up failed multipart uploads and merges
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/*"
    }
  ]
}

Eager merge ​

With EAGER_MERGE the server merges parts inside the bucket using UploadPartCopy, covered by the permissions above. A server killed mid-merge leaves an incomplete multipart upload that S3 keeps and bills. Add a lifecycle rule that aborts them:

json
{
  "Rules": [
    {
      "ID": "abort-incomplete-merges",
      "Status": "Enabled",
      "Filter": { "Prefix": "gh-actions-cache/" },
      "AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 1 }
    }
  ]
}

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