Skip to content

Migration Guide ​

Migrating from official actions/cache or legacy tespkg/actions-cache to cloud-cache-action takes less than 2 minutes.


Automated Migration with AI Coding Agents ​

If you use an AI coding assistant or agent (such as Claude Code, Cursor, Copilot, Antigravity, Devin, Codex, or Gemini), copy and paste the prompt below into your agent.

The prompt instructs the agent to discover all cache steps in your repository, recommend or detect your cloud storage provider, perform 1:1 input preservation, and output a concise checklist of required GitHub Secrets.

Copy-Pastable Agent Prompt ​

markdown
Please migrate all GitHub Actions caching steps in this repository to `xSAVIKx/cloud-cache-action@v1`.

### Task Instructions:

1. **Scan & Discover Workflows**:
   - Inspect all workflow files in `.github/workflows/` (and any custom composite actions).
   - Find all steps using:
     - `actions/cache@*`
     - `actions/cache/restore@*`
     - `actions/cache/save@*`
     - `tespkg/actions-cache@*`

2. **Select Storage Provider & Secrets**:
   - Check if this repository already uses cloud infrastructure secrets, or ask me which provider I prefer:
     - **Cloudflare R2** (Recommended for zero egress fees):
       - Inputs: `bucket`, `endpoint: https://${{ secrets.R2_ACCOUNT_ID }}.r2.cloudflarestorage.com`, `access-key: ${{ secrets.R2_ACCESS_KEY_ID }}`, `secret-key: ${{ secrets.R2_SECRET_ACCESS_KEY }}`
     - **AWS S3 with OIDC** (Recommended for AWS workloads, no static keys):
       - Uses `aws-actions/configure-aws-credentials` with IAM role assumption, then `cloud-cache-action` with `bucket` and `key`.
     - **AWS S3 (Static IAM)**:
       - Inputs: `bucket`, `region`, `access-key: ${{ secrets.AWS_ACCESS_KEY_ID }}`, `secret-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}`
     - **Google Cloud Storage (GCS)**:
       - Inputs: `bucket`, `endpoint: https://storage.googleapis.com`, `access-key: ${{ secrets.GCS_HMAC_ACCESS_ID }}`, `secret-key: ${{ secrets.GCS_HMAC_SECRET }}`
     - **Backblaze B2**:
       - Inputs: `bucket`, `endpoint: https://s3.<region>.backblazeb2.com`, `access-key: ${{ secrets.B2_KEY_ID }}`, `secret-key: ${{ secrets.B2_APPLICATION_KEY }}`
     - **Self-Hosted (MinIO / Garage / SeaweedFS / RustFS)**:
       - Inputs: `bucket`, `endpoint`, `access-key`, `secret-key` (and `force-path-style: true` for MinIO).
   - If I have not specified a provider yet, prompt me or default to **Cloudflare R2** or **AWS S3**.

3. **Perform 1:1 Step Replacement**:
   - Replace `actions/cache@v...` with `xSAVIKx/cloud-cache-action@v1`.
   - Replace `actions/cache/restore@v...` with `xSAVIKx/cloud-cache-action/restore@v1`.
   - Replace `actions/cache/save@v...` with `xSAVIKx/cloud-cache-action/save@v1`.
   - Preserve all existing cache keys and options: `path`, `key`, `restore-keys`, `lookup-only`, `fail-on-cache-miss`, `enableCrossOsArchive`, `read-only`.
   - Add the required provider inputs (`bucket`, `endpoint`, `access-key`, `secret-key`) referencing GitHub Secrets (`${{ secrets.<SECRET_NAME> }}`).

4. **Verify & Summarize**:
   - Check that all modified YAML files have valid syntax and proper indentation.
   - List the modified workflow files with a summary of changes.
   - Provide a clear checklist of the exact secret names I need to configure in GitHub Repository Settings (`Settings > Secrets and variables > Actions`).

Required Secrets Cheat Sheet by Provider ​

Share this quick reference with your team or agent when setting up repository secrets:

ProviderRequired GitHub SecretsEndpoint Format / Note
Cloudflare R2R2_ACCOUNT_ID
R2_ACCESS_KEY_ID
R2_SECRET_ACCESS_KEY
https://<account-id>.r2.cloudflarestorage.com
(Zero egress fees)
AWS S3 (OIDC)No static keys neededConfigured via aws-actions/configure-aws-credentials and IAM Role
AWS S3 (Static)AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
Specify region (e.g. us-east-1)
Google Cloud StorageGCS_HMAC_ACCESS_ID
GCS_HMAC_SECRET
https://storage.googleapis.com
(Requires HMAC key for dedicated Service Account)
Backblaze B2B2_KEY_ID
B2_APPLICATION_KEY
https://s3.<region>.backblazeb2.com
Fastly StorageFASTLY_ACCESS_KEY
FASTLY_SECRET_KEY
https://object.<region>.fastlystorage.com
Garage S3GARAGE_ACCESS_KEY
GARAGE_SECRET_KEY
http://garage.internal:3900
SeaweedFS S3SEAWEED_ACCESS_KEY
SEAWEED_SECRET_KEY
http://seaweedfs.internal:8333
MinIO S3MINIO_ACCESS_KEY
MINIO_SECRET_KEY
http://minio.internal:9000
(Requires force-path-style: true)
RustFSRUSTFS_ACCESS_KEY
RUSTFS_SECRET_KEY
http://rustfs.internal:9000
(Set provider: rustfs)

Manual Migration from actions/cache (v4, v5, v6) ​

Because cloud-cache-action maintains 1:1 input and output parity, you simply change the uses: line and add your bucket configuration:

Before (actions/cache): ​

yaml
- name: Cache dependencies
  uses: actions/cache@v4
  with:
    path: node_modules
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

After (cloud-cache-action): ​

yaml
- name: Cache dependencies
  uses: xSAVIKx/cloud-cache-action@v1
  with:
    # 1. Add your S3 bucket & credentials
    bucket: my-ci-cache-bucket
    endpoint: https://${{ secrets.R2_ACCOUNT_ID }}.r2.cloudflarestorage.com # Optional for AWS S3
    access-key: ${{ secrets.S3_ACCESS_KEY }}
    secret-key: ${{ secrets.S3_SECRET_KEY }}

    # 2. Keep all your existing inputs exactly as they are
    path: node_modules
    key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-node-

All existing features (lookup-only, fail-on-cache-miss, enableCrossOsArchive, read-only) continue to work identically. save-always is not supported: use cloud-cache-action/save with if: always() instead.

ACTIONS_CACHE_MODE and denied writes ​

GitHub sets ACTIONS_CACHE_MODE from the cache-mode workflow key, at the workflow level or at jobs.<job_id>.cache-mode. When the workflow does not set the key, the GitHub documentation gives a default of read for low-trust triggers, such as pull_request_target, issue_comment and workflow_run, that resolve to the default-branch scope. The pull_request event is not affected.

The action obeys ACTIONS_CACHE_MODE in the S3 tier, with the same rules as @actions/cache 6.2.0 (and the 5.2.0 backport):

  • none skips the restore and the save.
  • read skips the save.
  • write-only skips the restore.
  • write, an unset variable and an unknown value change nothing.

A skip logs one info line and does not fail the step, also in strict dual-cache mode. A skipped restore is a cache miss: cache-hit is false, and fail-on-cache-miss: true fails the step.

An S3-only job on one of those low-trust triggers can stop saving after the upgrade. To save from such a job again, set cache-mode: write on the job. Only do this when the job cannot run untrusted code.

When the storage refuses an S3 save with AccessDenied (or a 403 that is not a credential error), the step warns that the credentials may not be allowed to write to the bucket and that read-only: true skips the save. The job does not fail, except in strict dual-cache mode.


Manual Migration from tespkg/actions-cache ​

If you are using legacy tespkg/actions-cache, migrating to cloud-cache-action solves multiple architectural issues:

  1. Active Runner Support: Runs natively on Node 24 (node24) with zero runner deprecation warnings.
  2. Official AWS SDK v3: Avoids minio-js URL signing and header quirks with Cloudflare R2 and Fastly.
  3. Cross-Platform Path Safety: Normalizes Windows cache keys to POSIX forward slashes (/), avoiding broken backslash keys in object storage.
  4. Modern Parity: Includes actions/cache@v6 features such as lookup-only, fail-on-cache-miss, and read-only.

Input Differences: ​

  • All legacy inputs (bucket, endpoint, region, insecure, accessKey, secretKey, sessionToken) remain supported for backward compatibility.
  • Kebab-case aliases are also supported (access-key, secret-key, session-token, force-path-style).
  • Fallback Behavior: use-fallback is false by default in cloud-cache-action rather than true. If you want automatic GitHub Actions Cache fallback on S3 errors or cache miss, set use-fallback: true.

Branch Isolation and Trust Model ​

Like actions/cache, restores only use caches from the current ref, the pull request's base branch and the default branch. A feature branch can reuse main's cache, but main never restores a cache that a feature branch saved. Set scoped-to-ref: false to share caches across every ref.

Unlike GitHub's cache service, the bucket itself does not enforce this: anyone who holds the bucket's write credentials can write any cache object, including ones main will restore. Archives are extracted with absolute paths allowed, as actions/cache does. So:

  • Do not expose cache credentials to workflows that run untrusted code, such as pull_request_target jobs that check out fork code.
  • Prefer short-lived credentials (OIDC) scoped to the cache bucket or prefix.
  • Use a separate bucket or prefix for caches that must not be shared between repositories or trust levels.

Tag-triggered runs and refs ​

The Git ref segment in the object key comes from whatever triggered the run. A run started by pushing a tag saves its cache under refs/tags/<tag>, which is a ref no pull request or branch build will ever restore from, since restores only search the current ref, the pull request base branch and the default branch — never arbitrary tags. If a release workflow triggered by a tag push is your only place that populates a cache other jobs expect to reuse, that cache will never be found.

Prefer saving on the default branch instead — a push to main or a manual workflow_dispatch run there — so the cache is saved under a ref other jobs actually search. If you do need caches shared across tags, branches and pull requests, set scoped-to-ref: false together with the trust model warning above: every ref then reads and writes the same object, so anyone who can push a branch or tag can also overwrite what main restores.