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.
| Option | Write control | What you take on | Best fit |
|---|---|---|---|
| Nx Cloud | Managed by Nx. Nx recommends it first. | Nothing. Free to start; distributed task execution and CI agents on top | Teams that want distributed execution, or would rather stay entirely inside the Nx product |
| Disable the remote cache | Not applicable. Nothing is shared, so nothing can be poisoned | Nothing. Each machine keeps its local cache only | Small 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 keys | Server, auth, TLS, storage, monitoring, upgrades | Data residency or on-premises rules, with a team to run it |
| Cachely | Read-only tokens plus 409 on existing keys, enforced at the API | Nothing. Two environment variables, flat pricing | Teams 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-cacheThen 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 outputThe 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 detailsEach 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.
Related guides
- Nx remote cacheSet up the official Nx self-hosted cache and see how the task hash works.
- Cachely vs self-hosted Nx cacheMaintenance, CVE-2025-36852, insights, and cost versus a DIY bucket.
- Cachely vs Nx PowerpackWhy the deprecated Powerpack cache packages leave CVE-2025-36852 open.
- Improve cache hit rateDiagnose over-keying, env drift, unstable outputs, and missing outputs.