Nx migration guide

Replacing @nx/s3-cache, @nx/gcs-cache, @nx/azure-cache and @nx/shared-fs-cache

On 2026-05-21 Nx deprecated all four of its self-hosted cache packages because of a design flaw that, in Nx's words, "cannot be patched". This is one migration guide for all of them: what changed, the three honest options, the exact steps for each package, the messages you will see, and how to roll back.

Updated Reviewed by the Cachely team

What Nx deprecated, and when

Nx's deprecation notice for the self-hosted cache packages marks these four as deprecated as of 2026-05-21:

  • @nx/s3-cache - Amazon S3 and S3-compatible stores such as MinIO, Cloudflare R2, and DigitalOcean Spaces.
  • @nx/gcs-cache - Google Cloud Storage.
  • @nx/azure-cache - Azure Blob Storage.
  • @nx/shared-fs-cache - a shared directory, such as a network drive or a cache directory restored by your CI provider.

The reason is the CREEP cache-poisoning vulnerability, CVE-2025-36852. Each package gives every build one credential that can read and write the whole cache, and nothing records which branch produced which artifact. Nx says the flaw is in the design and cannot be patched. It also says it has no evidence the packages have been exploited in the wild.

What that means for an existing setup: the packages stay on npm, so installs and builds keep working today. They get no further updates or security patches, and Nx says they may be removed in the future. The latest release of each, 5.0.7, declares a peer dependency on nx 18 up to but not including 23, so they will not follow you onto the next major version. All four were part of Nx Powerpack and depend on the @nx/key license package. For the wider Powerpack story, see what replaced the Nx Powerpack cache.

Primary sources: the Nx deprecation notice, the Nx blog post on CREEP, and the CVE-2025-36852 record. Checked October 2026.

Your three options, compared

Nx recommends two paths: migrate to Nx Cloud, or disable the remote cache. For teams that need to keep a self-hosted cache, it points to the Nx remote cache OpenAPI specification and states that an implementation "must enforce 409 on existing keys". That third path splits into running a server yourself or using a managed one that speaks the same protocol.

Options for replacing the deprecated Nx self-hosted cache packages.
OptionWrite controlWhat you take onBest fit
Nx CloudManaged by Nx. Nx recommends it first.Nothing. Free to start; distributed task execution and CI agents on topTeams that want distributed execution, or would rather stay entirely inside the Nx product
Disable the remote cacheNot applicable. Nothing is shared, so nothing can be poisonedNothing. Each machine keeps its local cache onlySmall repos, or a safe holding position while you decide
A self-run server implementing the Nx OpenAPI spec (open source or your own)Yours to verify: scoped tokens plus 409 on existing keysServer, auth, TLS, storage, monitoring, upgradesData residency or on-premises rules, with a team to run it
CachelyRead-only tokens plus 409 on existing keys, enforced at the APINothing. Two environment variables, flat pricingTeams that only need a shared cache and no server to operate

Where each one stops being the right answer. Cachely is a remote cache only: it does not do distributed task execution or run CI agents, so it is not an Nx Cloud replacement if you need those - the Nx Cloud alternative guide covers that trade-off. Disabling the cache costs CI time on every run. And open-source servers vary: before adopting one, confirm it gives pull requests read-only credentials and returns 409 on existing keys, because a server that overwrites is the same flaw with more moving parts.

Keeping artifacts in a bucket you own is still correct when contracts or regulation require a specific account, region, or on-premises location. In that case put a compliant server in front of the bucket rather than a plugin holding a shared credential. Cachely vs a self-hosted Nx cache lists what running one involves. Pricing for the managed option is on the pricing page: a free tier and a flat Team plan with no per-seat fees.

Migration steps for every package

The client side of the move is the same whichever package you used. Your nx.json targets, inputs, outputs, and namedInputs stay as they are, because Nx computes the cache key the same way regardless of where artifacts are stored.

1. Upgrade to Nx 20.8 or later

The built-in HTTP remote cache client arrived in Nx 20.8. Upgrade every runner and pin the same Nx version locally and in CI, or hashes will drift between machines. If you cannot upgrade yet, read remote caching on older Nx versions first.

2. Remove the package and @nx/key

npm uninstall @nx/s3-cache @nx/key   # or @nx/gcs-cache, @nx/azure-cache, @nx/shared-fs-cache

Then delete the Powerpack license: the .nx/powerpack/license.ini file written by nx activate-powerpack, and any NX_POWERPACK_LICENSE secret in CI. Keep @nx/key only if you still use another Powerpack package that depends on it, such as @nx/conformance or @nx/owners. The package-specific cleanup is in the sections below.

3. Set two environment variables

NX_SELF_HOSTED_REMOTE_CACHE_SERVER=https://remote.cachely.dev
NX_SELF_HOSTED_REMOTE_CACHE_ACCESS_TOKEN=<your-token>

Use the bare host with no path; Nx appends the cache route itself. Set both everywhere builds run, laptops included, so developers reuse what CI produced. This is the official Nx self-hosted remote cache API, so no plugin is involved. The Nx remote cache guide explains how the variables work and how the protocol behaves.

4. Give untrusted builds read-only tokens

Issue a read-only token to pull-request and fork workflows and a read-write token only to protected branches. This is the step that closes CREEP; a read-only build can download cached results but cannot upload them. The token guide covers how many to issue and how to rotate them.

5. Verify the first remote hit

npx nx reset --only-cache
npx nx build my-app            # read-write token: this run uploads
npx nx reset --only-cache
rm -rf dist/my-app             # delete the outputs so Nx must restore them
npx nx build my-app            # look for [remote cache] in the output

The second build should replay from the remote cache and print [remote cache]. The full checklist is in the Nx verification steps. Only after that, revoke the old bucket or storage credentials - a credential nobody uses can still write.

From @nx/s3-cache (AWS S3, MinIO, Cloudflare R2, Spaces)

Delete the s3 block from nx.json. It usually looked like this, sometimes with endpoint and forcePathStyle for an S3-compatible store, or ssoProfile, accessKeyId, secretAccessKey, encryptionKey, cacheKeyPrefix, and disableChecksum:

{
  "s3": {
    "bucket": "my-nx-cache",
    "region": "us-east-1",
    "localMode": "read",
    "ciMode": "read-write"
  }
}

Remove the AWS access keys or role that CI used only for the cache, and any credentials embedded in nx.json itself. If you pointed the plugin at MinIO, LocalStack, R2, or Spaces, the same applies: changing the endpoint changed who stored the bytes, not the trust model, so the shared credential was exposed in exactly the same way.

From @nx/gcs-cache (Google Cloud Storage)

Delete the gcs block from nx.json (its bucket, optional endpoint, and localMode and ciMode settings). Then remove the service account key, or the workload identity binding, that gave CI write access to the bucket.

From @nx/azure-cache (Azure Blob Storage)

Delete the azure block from nx.json (its container, accountName, and mode settings). Then remove the role assignment for whichever service principal or managed identity CI used to write to the container.

From @nx/shared-fs-cache (network drive or restored directory)

This package shared the cache through a directory: a mounted network drive, or a cache folder your CI provider saved and restored between runs. If you pointed cacheDirectory in nx.json or the NX_CACHE_DIRECTORY variable at that shared location, return it to a local path or remove it. Then drop the CI step that saved and restored the Nx cache folder; the remote cache replaces it. Our GitHub Actions cache guide explains why that cache works differently from a task cache.

Ready to switch?

Create a workspace, issue one read-write token for protected branches and one read-only token for pull requests, then set the two variables.

The exact messages you will see

When you install any of the four packages, npm prints a deprecation warning like this, with the package name and version filled in:

npm warn deprecated @nx/s3-cache@5.0.7: This package has been deprecated. See https://nx.dev/docs/reference/deprecated/self-hosted-cache-packages for details

Each package's README on npm now opens with the same message: "@nx/s3-cache is deprecated." After the switch, these are the responses from Cachely that look like errors but are expected:

  • 409 Artifact already exists - a build tried to upload a key that is already stored. Artifacts are write-once, so this is the protection working. It also happens harmlessly when two runners produce the same hash at once.
  • 403 Insufficient permissions: write access required - a read-only token tried to upload. Expected in pull-request builds; the download half still works.
  • 401 Missing Authorization header - the token variable did not reach the build. Check that the CI secret is exposed to the job.

Anything else, including every request returning 404, is covered in troubleshooting.

What happens to your existing artifacts

They do not migrate. Nothing copies objects from the old bucket, container, or shared directory into a new backend, and Cachely does not import them. The first run on your default branch with a read-write token repopulates the cache, so expect one slower CI run per project while it warms up.

That is the safer outcome, not just the easier one. Anything in the old store was written with a credential every build held, so there is no way to prove an artifact came from a trusted branch. Starting from an empty cache means every artifact in the new one was produced under the new write rules. Keep the old store untouched, but no longer written to, until you have verified remote hits, then delete it so you stop paying for it.

Why the replacement must refuse writes

The CREEP attack needs no compromised package. A pull request off the default branch changes only the CI workflow, which is not part of the Nx task hash. Its build therefore hashes to the same key the default branch will use later, and it uploads first. Every trusted build that follows gets a cache hit and ships the planted output.

Two server-side rules close it, and both are enforced by the Cachely API:

  • Read-only tokens for untrusted builds. A pull-request token can download but every upload gets a 403.
  • 409 on existing keys. Once a key is stored it cannot be replaced, so a racing upload cannot overwrite a good artifact.

The full explanation is in the CREEP write-up, and our security model documents the rest of the trust boundary.

Rolling back

Because the client side of the migration is two environment variables, rolling back is unsetting them. Nx then falls back to its local cache on each machine - the "disable remote cache" option Nx itself recommends - and builds stay correct, just slower. Nothing in your task configuration needs to change back.

Avoid rolling back by reinstalling the deprecated package. It still installs, and a git revert would restore it, but that also restores the shared credential CREEP exploits. If you have to, treat it as temporary, keep the storage credential out of pull-request and fork workflows, and plan the move forward again.

Set it upSet up Nx with CachelyTwo environment variables, the bare-host rule, and the Nx versions that share a cache.

Related guides

Replace the deprecated plugin with two environment variables
Read-only tokens for pull requests, write-once artifacts enforced at the API, and no server to run.
Start freeSee pricing
FAQ

Deprecated Nx cache packages: frequently asked questions

Is @nx/s3-cache deprecated?
Yes. Nx deprecated @nx/s3-cache, @nx/gcs-cache, @nx/azure-cache, and @nx/shared-fs-cache as of 2026-05-21 because of the CREEP cache-poisoning vulnerability (CVE-2025-36852). Nx says the flaw is in their design and cannot be patched. The packages remain on npm so existing builds keep working, but they receive no updates or security patches and may be removed in the future.
What should I replace @nx/s3-cache with?
Nx recommends migrating to Nx Cloud or disabling the remote cache. If you need a self-hosted cache, use a server that implements the Nx remote cache OpenAPI specification and returns 409 Conflict on existing keys - either one you run, or a managed one such as Cachely, which needs two environment variables on Nx 20.8 or later. Cachely is a remote cache only; choose Nx Cloud if you need distributed task execution.
How do I migrate from @nx/gcs-cache or @nx/azure-cache?
The same way as from @nx/s3-cache. Upgrade to Nx 20.8 or later, uninstall the package and @nx/key, delete the gcs or azure block from nx.json, set NX_SELF_HOSTED_REMOTE_CACHE_SERVER NX_SELF_HOSTED_REMOTE_CACHE_ACCESS_TOKEN everywhere builds run, give pull requests a read-only token, verify a [remote cache] hit, then revoke the service account or role assignment CI used for the bucket or container.
What replaces @nx/shared-fs-cache?
A remote cache reached over HTTP. Uninstall the package and @nx/key, point cacheDirectory or NX_CACHE_DIRECTORY back to a local path if you had aimed it at a network drive or CI-restored folder, drop the CI step that saved and restored that folder, and set the two Nx self-hosted remote cache environment variables.
Do my existing cached artifacts carry over to the new cache?
No. Nothing copies objects from the old bucket, container, or shared directory, and Cachely does not import them. The first run on the default branch with a read-write token repopulates the cache. That is also the safer outcome, because artifacts written with a credential every build held cannot be proven to come from a trusted branch.
Do I still need @nx/key after removing the cache package?
Only if you still use another Nx Powerpack package that depends on it, such as @nx/conformance or @nx/owners. Otherwise uninstall it and delete the Powerpack license: the .nx/powerpack/license.ini file and any NX_POWERPACK_LICENSE secret in CI.
Can I still keep Nx cache artifacts in my own S3, MinIO, or R2 bucket?
Yes, but not through the deprecated plugin. Run a server that implements the Nx remote cache OpenAPI specification, enforces read-only tokens for untrusted builds and 409 on existing keys, and uses the bucket as its storage layer. Switching AWS for MinIO, Cloudflare R2, or another S3-compatible endpoint changes who stores the bytes, not the trust model.
How do I roll back if the migration goes wrong?
Unset the two NX_SELF_HOSTED_REMOTE_CACHE variables. Nx falls back to the local cache on each machine, builds stay correct, and no task configuration needs to change. Avoid reinstalling the deprecated package as a rollback, because that restores the shared credential CVE-2025-36852 exploits.