What "Nx S3 cache" means today
The phrase covers four different setups, and they have very different security and maintenance stories:
@nx/s3-cache- the first-party Nx Powerpack plugin that wrote artifacts straight to a bucket. Deprecated as of 2026-05-21.- Community plugins such as
nx-remotecache-s3- the same shared-bucket-credential shape, maintained outside Nx, with the same poisoning exposure. - Your own cache server backed by S3 - a service that implements the Nx remote cache OpenAPI specification and happens to store bytes in S3. This is the shape Nx now points self-hosters at, because the server can enforce write rules the bucket cannot.
- A managed implementation of the same protocol - identical two-variable client config, someone else operates the storage and the access control.
All four speak to Nx through the same place in the pipeline: Nx computes a content hash for a task, and something either returns a stored artifact for that hash or does not. If you want that mechanism explained first, start with the Nx remote cache guide.
The deprecated @nx/s3-cache plugin
@nx/s3-cache was configured with an s3 block in nx.json - a bucket, a region, and credentials, with optional settings for an alternate endpoint, path-style URLs, an encryption key, a cache key prefix, and separate local and CI modes:
{
"s3": {
"bucket": "my-nx-cache",
"region": "us-east-1",
"localMode": "read",
"ciMode": "read-write"
}
}It was licensed separately from Nx itself (the self-hosted cache license), and it is now deprecated together with @nx/gcs-cache, @nx/azure-cache, and @nx/shared-fs-cache. Nx's notice is blunt about the reason and the plan: the CREEP vulnerability (CVE-2025-36852) affects all four, the flaw is in their design and cannot be patched, and the packages stay on npm so existing builds keep working but will receive no updates or security patches and may be removed.
Read the primary sources rather than this page: Nx's deprecation notice for the self-hosted cache packages and CVE-2025-36852. Checked July 2026.
Why a shared bucket cannot be safe
The attack does not need a compromised package. It needs a bucket credential that can both read and write every key, which is exactly what a bucket-adapter plugin distributes to every build:
- Open a pull request off the default branch with no source changes, but a modified CI workflow.
- The workflow file is not part of the Nx task hash, so the pull-request build hashes to the same key the default branch will later hash to, and it can build whatever artifact it likes.
- It uploads first. Every later trusted build with that key gets a cache hit and ships the planted output without rebuilding.
Nothing in a bucket records which branch produced which artifact, so there is no server-side rule to break the tie. That is why the fix is not a patch: the write decision has to move to something that can refuse it. Two rules close the hole - untrusted builds get read-only credentials, and the server returns 409 Conflict instead of overwriting a key that already exists. Nx's own guidance to anyone building a cache server calls out the 409 requirement explicitly. The longer walkthrough is in the CREEP write-up, and our trust model documents how Cachely enforces both.
MinIO, R2, and other S3-compatible stores
A lot of "Nx S3 cache" searches are not about AWS at all - they are about MinIO, Cloudflare R2, DigitalOcean Spaces, or LocalStack behind the S3 API. The deprecated plugin supported those through an endpoint setting, with forcePathStyle for providers that need it.
Worth being clear about what that changes and what it does not: swapping AWS for an S3-compatible endpoint changes who stores the bytes and what egress costs. It does not change the trust model. A MinIO bucket handed to CI with one read-write credential is poisonable in exactly the same way as an S3 bucket. Object storage is the right place for the bytes - Cachely keeps artifacts in Cloudflare R2 - but the permission decision belongs in front of it.
Your four options, compared
| Option | Write control | You operate | Best fit |
|---|---|---|---|
| Deprecated @nx/s3-cache or a community bucket plugin | None. One credential reads and writes every key | Bucket, IAM, lifecycle rules, key rotation | Nothing new. Migrate off it |
| Your own server implementing the Nx OpenAPI spec, backed by S3 | Yours to build: scoped tokens plus 409 on existing keys | Server, auth, TLS, storage, monitoring, on-call | Data residency or on-premises requirements, with a team to run it |
| Nx Cloud | Managed by Nx | Nothing | Teams that also want distributed task execution and CI agents |
| Cachely | Read-only tokens plus immutable artifacts, enforced at the API | Nothing. Two environment variables | Teams that want the self-hosted protocol without running the backend |
Nx's own recommendation is Nx Cloud, or disabling the remote cache until you have something safe. If distributed task execution or managed CI agents are in your plans, Nx Cloud is the better product and we do not pretend otherwise - the trade-offs are laid out in the Nx Cloud alternative guide.
When keeping S3 yourself is right
Do not read this page as "never self-host". A bucket you control is the correct answer when:
- Artifacts must stay in a specific account, region, or on-premises environment for contractual or regulatory reasons.
- You already run a hardened cache server that enforces scoped tokens and rejects overwrites, and S3 is just its storage layer.
- Your monorepo pushes cache volumes where negotiated storage and egress pricing beats a subscription, and you have staffed the upkeep.
If that is you, keep the bucket and put a compliant server in front of it - not a plugin holding a shared credential. Cachely vs a self-hosted Nx cache lists what that upkeep actually includes.
Migrating off the S3 plugin
The migration is a backend swap, not an Nx migration. Your nx.json targets, inputs, outputs, and namedInputs stay exactly as they are, because the cache key is computed by Nx either way.
1. Remove the plugin and its config
npm uninstall @nx/s3-cache # or nx-remotecache-s3Then delete the s3 block from nx.json and remove the AWS access key, secret, region, and bucket variables from CI and from developer environments. Revoke the credential afterwards - a key that is no longer used is still a key that can still write.
2. Point Nx at a protocol-compliant server
NX_SELF_HOSTED_REMOTE_CACHE_SERVER=https://remote.cachely.dev
NX_SELF_HOSTED_REMOTE_CACHE_ACCESS_TOKEN=<your-token>That is the official Nx self-hosted remote cache API, available in the CLI since Nx 19.8, so no plugin or custom task runner is involved. Set both variables everywhere builds run, including laptops, so developers reuse what CI produced.
3. 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 still gets every cache hit, it simply cannot plant one. Verify it by re-running a task and confirming Nx reports it as read from the remote cache.
4. Confirm the cache is actually being used
A migration that silently stops caching looks like a successful migration until the CI bill arrives. Check hit rate after the switch, and if it is lower than expected, work through the hit-rate diagnosis guide before blaming the backend. The CI savings calculator turns that hit rate into minutes and money.
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.