Skip to content

Getting Started

Cloud Cache Action allows you to store and retrieve GitHub Actions cache bundles using any S3-compatible cloud or self-hosted object storage service.

It is designed as a drop-in replacement for actions/cache@v4-v6, keeping your CI/CD workflows lightning fast while bypassing GitHub's 10GB default cache quotas and egress fees.

Quickstart

Add the following step to your GitHub Actions workflow:

yaml
- name: Cache dependencies to S3
  uses: xSAVIKx/cloud-cache-action@v1
  with:
    bucket: my-ci-cache-bucket
    endpoint: https://<account_id>.r2.cloudflarestorage.com # Or AWS, GCS, B2, MinIO, etc.
    access-key: ${{ secrets.S3_ACCESS_KEY }}
    secret-key: ${{ secrets.S3_SECRET_KEY }}
    path: |
      ~/.npm
      node_modules
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

How It Works

  1. Restore Phase (Pre/Main):

    • Connects to your S3 bucket using modern @aws-sdk/client-s3.
    • Checks if an exact match exists for the key parameter.
    • If not found, evaluates restore-keys in order and downloads the most recently updated matching archive.
    • Decompresses the archive using zstd (or gzip fallback) directly into your workspace.
    • Sets outputs (cache-hit, cache-primary-key, cache-matched-key, cache-size, cache-storage-provider, cache-s3-key).
  2. Save Phase (Post):

    • If read-only: true or if an exact key match occurred during restore, saving is automatically skipped.
    • Otherwise, archives the specified path directories using multi-threaded zstd compression.
    • Streams the compressed archive to your S3 bucket using multipart uploads via @aws-sdk/lib-storage.
    • Emits diagnostics and completes cleanly without breaking the build on non-fatal network interruptions.

Standalone Restore and Save Actions

Just like actions/cache/restore and actions/cache/save, you can invoke restore and save as independent steps:

Restore Only

yaml
- name: Restore cache
  id: restore-step
  uses: xSAVIKx/cloud-cache-action/restore@v1
  with:
    bucket: my-ci-cache-bucket
    key: ${{ runner.os }}-build-${{ hashFiles('**/lock') }}
    path: build/

Save Only

yaml
- name: Save cache
  uses: xSAVIKx/cloud-cache-action/save@v1
  with:
    bucket: my-ci-cache-bucket
    key: ${{ runner.os }}-build-${{ hashFiles('**/lock') }}
    path: build/

Action Metadata Definitions

You can inspect the full action specification files directly in the repository:

action.yml (Click to view unified action definition)
yaml
name: 'Cloud Cache Action'
description: 'Cache artifacts to any S3-compatible storage with 1:1 actions/cache parity and dual-caching'
author: 'Yurii Serhiichuk (https://serhiichuk.dev)'

inputs:
  # Official actions/cache standard inputs
  path:
    description: 'A list of files, directories, and wildcard patterns to cache and restore'
    required: true
  key:
    description: 'An explicit key for restoring and saving the cache'
    required: true
  restore-keys:
    description: 'An ordered multiline string listing the prefix-matched keys, used for restoring stale cache if no cache hit occurred for key'
    required: false
  upload-chunk-size:
    description: 'The chunk size used to split up large files during upload, in bytes'
    required: false
  enableCrossOsArchive:
    description: 'An optional boolean when enabled, allows windows runners to save or restore caches that can be restored or saved respectively on other platforms'
    default: 'false'
    required: false
  fail-on-cache-miss:
    description: 'Fail the workflow if cache entry is not found'
    default: 'false'
    required: false
  lookup-only:
    description: 'Check if a cache entry exists for the given input(s) (key, restore-keys) without downloading the cache'
    default: 'false'
    required: false
  read-only:
    description: 'Restore cache but do not save cache in post step'
    default: 'false'
    required: false
  save-always:
    description: 'Run the post step to save the cache even if another step before fails'
    default: 'false'
    required: false

  # S3 Storage inputs
  bucket:
    description: 'The S3 bucket name where cache archives are stored'
    required: true
  endpoint:
    description: 'Custom S3-compatible endpoint URL (e.g., https://<account_id>.r2.cloudflarestorage.com or https://storage.googleapis.com)'
    required: false
  region:
    description: 'AWS or S3 provider region (e.g., us-east-1, auto, etc.)'
    required: false
  provider:
    description: 'Storage provider preset: aws, r2, gcs, b2, fastly, garage, seaweedfs, minio'
    required: false
  access-key:
    description: 'S3 Access Key ID (falls back to AWS_ACCESS_KEY_ID or IAM/OIDC)'
    required: false
  accessKey:
    description: 'Alias for access-key'
    required: false
  secret-key:
    description: 'S3 Secret Access Key (falls back to AWS_SECRET_ACCESS_KEY)'
    required: false
  secretKey:
    description: 'Alias for secret-key'
    required: false
  session-token:
    description: 'S3 Session Token (falls back to AWS_SESSION_TOKEN)'
    required: false
  sessionToken:
    description: 'Alias for session-token'
    required: false
  force-path-style:
    description: 'Force path-style S3 URLs (bucket in path rather than subdomain)'
    required: false
  prefix:
    description: 'Subfolder prefix path within the bucket'
    required: false
    default: ''
  s3-key-pattern:
    description: 'Template pattern for S3 object key. Default: ${GITHUB_REPOSITORY}/${prefix}${key}/${archive_filename}'
    required: false
    default: '${GITHUB_REPOSITORY}/${prefix}${key}/${archive_filename}'
  scoped-to-repository:
    description: 'Whether to prefix bucket cache paths with GITHUB_REPOSITORY (default: true)'
    required: false
    default: 'true'
  retry:
    description: 'Enable retries with exponential backoff on failure for S3 operations'
    required: false
    default: 'true'
  retry-count:
    description: 'Number of times to retry S3 operations in case of failure'
    required: false
    default: '3'
  use-fallback:
    description: 'Fall back to official GitHub Actions Cache service if S3 operations fail or miss'
    required: false
    default: 'false'

  # Dual-cache inputs
  dual-cache:
    description: 'Cache to both S3-compatible storage and GitHub Actions Cache simultaneously'
    required: false
    default: 'false'
  restore-priority:
    description: 'Order of cache sources to query during restore: s3-first or github-first'
    required: false
    default: 's3-first'
  dual-cache-strategy:
    description: 'Sync strategy for saving in dual-cache mode: backfill (sync missing tier), independent, or skip-on-hit'
    required: false
    default: 'backfill'
  dual-cache-strict:
    description: 'Fail step if either tier encounters an error during dual-cache operations (default: false, fault-tolerant)'
    required: false
    default: 'false'

outputs:
  cache-hit:
    description: 'A boolean value to indicate an exact match was found for the primary key'
  cache-primary-key:
    description: 'A resolved cache key for which cache match was attempted'
  cache-matched-key:
    description: 'Key of the cache that was restored, either the primary key or a partial match'
  cache-size:
    description: 'The size of the restored/saved cache archive in bytes'
  cache-storage-provider:
    description: 'The resolved S3-compatible storage provider'
  cache-s3-key:
    description: 'The full S3 object key inside the bucket'
  cache-etag:
    description: 'The ETag checksum of the cache archive in S3'
  cache-hit-source:
    description: 'The cache tier that serviced the restore hit: s3, github, or none'
  cache-saved-sources:
    description: 'Comma-separated list of cache tiers successfully saved to: s3, github, or s3,github'

runs:
  using: 'node24'
  main: 'dist/restore/index.js'
  post: 'dist/save/index.js'
  post-if: 'success()'

branding:
  icon: 'cloud'
  color: 'blue'
restore/action.yml (Click to view restore action definition)
yaml
name: 'Restore Cache (S3-Compatible)'
description: 'Restore Cache artifacts from any S3-compatible storage with actions/cache parity'
author: 'Yurii Serhiichuk (https://serhiichuk.dev)'

inputs:
  path:
    description: 'A list of files, directories, and wildcard patterns to restore'
    required: true
  key:
    description: 'An explicit key for restoring the cache'
    required: true
  restore-keys:
    description: 'An ordered multiline string listing the prefix-matched keys, used for restoring stale cache if no cache hit occurred for key'
    required: false
  enableCrossOsArchive:
    description: 'An optional boolean when enabled, allows windows runners to restore caches that were saved on other platforms'
    default: 'false'
    required: false
  fail-on-cache-miss:
    description: 'Fail the workflow if cache entry is not found'
    default: 'false'
    required: false
  lookup-only:
    description: 'Check if a cache entry exists for the given input(s) (key, restore-keys) without downloading the cache'
    default: 'false'
    required: false

  # S3 Storage inputs
  bucket:
    description: 'The S3 bucket name where cache archives are stored'
    required: true
  endpoint:
    description: 'Custom S3-compatible endpoint URL'
    required: false
  region:
    description: 'AWS or S3 provider region'
    required: false
  provider:
    description: 'Storage provider preset: aws, r2, gcs, b2, fastly, garage, seaweedfs, minio'
    required: false
  access-key:
    description: 'S3 Access Key ID'
    required: false
  accessKey:
    description: 'Alias for access-key'
    required: false
  secret-key:
    description: 'S3 Secret Access Key'
    required: false
  secretKey:
    description: 'Alias for secret-key'
    required: false
  session-token:
    description: 'S3 Session Token'
    required: false
  sessionToken:
    description: 'Alias for session-token'
    required: false
  force-path-style:
    description: 'Force path-style S3 URLs'
    required: false
  prefix:
    description: 'Subfolder prefix path within the bucket'
    required: false
    default: ''
  s3-key-pattern:
    description: 'Template pattern for S3 object key. Default: ${GITHUB_REPOSITORY}/${prefix}${key}/${archive_filename}'
    required: false
    default: '${GITHUB_REPOSITORY}/${prefix}${key}/${archive_filename}'
  scoped-to-repository:
    description: 'Whether to prefix bucket cache paths with GITHUB_REPOSITORY (default: true)'
    required: false
    default: 'true'
  retry:
    description: 'Enable retries with exponential backoff on failure for S3 operations'
    required: false
    default: 'true'
  retry-count:
    description: 'Number of times to retry S3 operations in case of failure'
    required: false
    default: '3'
  use-fallback:
    description: 'Fall back to official GitHub Actions Cache service if S3 operations fail or miss'
    required: false
    default: 'false'

  # Dual-cache inputs
  dual-cache:
    description: 'Cache to both S3-compatible storage and GitHub Actions Cache simultaneously'
    required: false
    default: 'false'
  restore-priority:
    description: 'Order of cache sources to query during restore: s3-first or github-first'
    required: false
    default: 's3-first'
  dual-cache-strategy:
    description: 'Sync strategy for saving in dual-cache mode: backfill, independent, or skip-on-hit'
    required: false
    default: 'backfill'
  dual-cache-strict:
    description: 'Fail step if either tier encounters an error during dual-cache operations'
    required: false
    default: 'false'

outputs:
  cache-hit:
    description: 'A boolean value to indicate an exact match was found for the primary key'
  cache-primary-key:
    description: 'A resolved cache key for which cache match was attempted'
  cache-matched-key:
    description: 'Key of the cache that was restored, either the primary key or a partial match'
  cache-size:
    description: 'The size of the restored cache archive in bytes'
  cache-storage-provider:
    description: 'The resolved S3-compatible storage provider'
  cache-s3-key:
    description: 'The full S3 object key inside the bucket'
  cache-etag:
    description: 'The ETag checksum of the cache archive in S3'
  cache-hit-source:
    description: 'The cache tier that serviced the restore hit: s3, github, or none'

runs:
  using: 'node24'
  main: '../dist/restore-only/index.js'

branding:
  icon: 'cloud-download'
  color: 'blue'
save/action.yml (Click to view save action definition)
yaml
name: 'Save Cache (S3-Compatible)'
description: 'Save Cache artifacts to any S3-compatible storage with actions/cache parity'
author: 'Yurii Serhiichuk (https://serhiichuk.dev)'

inputs:
  path:
    description: 'A list of files, directories, and wildcard patterns to cache'
    required: true
  key:
    description: 'An explicit key for saving the cache'
    required: true
  upload-chunk-size:
    description: 'The chunk size used to split up large files during upload, in bytes'
    required: false
  enableCrossOsArchive:
    description: 'An optional boolean when enabled, allows windows runners to save caches that can be restored on other platforms'
    default: 'false'
    required: false

  # S3 Storage inputs
  bucket:
    description: 'The S3 bucket name where cache archives are stored'
    required: true
  endpoint:
    description: 'Custom S3-compatible endpoint URL'
    required: false
  region:
    description: 'AWS or S3 provider region'
    required: false
  provider:
    description: 'Storage provider preset: aws, r2, gcs, b2, fastly, garage, seaweedfs, minio'
    required: false
  access-key:
    description: 'S3 Access Key ID'
    required: false
  accessKey:
    description: 'Alias for access-key'
    required: false
  secret-key:
    description: 'S3 Secret Access Key'
    required: false
  secretKey:
    description: 'Alias for secret-key'
    required: false
  session-token:
    description: 'S3 Session Token'
    required: false
  sessionToken:
    description: 'Alias for session-token'
    required: false
  force-path-style:
    description: 'Force path-style S3 URLs'
    required: false
  prefix:
    description: 'Subfolder prefix path within the bucket'
    required: false
    default: ''
  s3-key-pattern:
    description: 'Template pattern for S3 object key. Default: ${GITHUB_REPOSITORY}/${prefix}${key}/${archive_filename}'
    required: false
    default: '${GITHUB_REPOSITORY}/${prefix}${key}/${archive_filename}'
  scoped-to-repository:
    description: 'Whether to prefix bucket cache paths with GITHUB_REPOSITORY (default: true)'
    required: false
    default: 'true'
  retry:
    description: 'Enable retries with exponential backoff on failure for S3 operations'
    required: false
    default: 'true'
  retry-count:
    description: 'Number of times to retry S3 operations in case of failure'
    required: false
    default: '3'
  use-fallback:
    description: 'Fall back to official GitHub Actions Cache service if S3 save fails'
    required: false
    default: 'false'

  # Dual-cache inputs
  dual-cache:
    description: 'Cache to both S3-compatible storage and GitHub Actions Cache simultaneously'
    required: false
    default: 'false'
  dual-cache-strategy:
    description: 'Sync strategy for saving in dual-cache mode: backfill, independent, or skip-on-hit'
    required: false
    default: 'backfill'
  dual-cache-strict:
    description: 'Fail step if either tier encounters an error during dual-cache save'
    required: false
    default: 'false'

outputs:
  cache-size:
    description: 'The size of the saved cache archive in bytes'
  cache-storage-provider:
    description: 'The resolved S3-compatible storage provider'
  cache-s3-key:
    description: 'The full S3 object key inside the bucket'
  cache-etag:
    description: 'The ETag checksum of the cache archive in S3'
  cache-saved-sources:
    description: 'Comma-separated list of cache tiers successfully saved to: s3, github, or s3,github'

runs:
  using: 'node24'
  main: '../dist/save-only/index.js'

branding:
  icon: 'cloud-upload'
  color: 'blue'