Since Nx 21, custom task runners (tasksRunnerOptions) don't run, and in May 2026 Nx deprecated its own bucket plugins (@nx/s3-cache, @nx/gcs-cache, @nx/azure-cache, @nx/shared-fs-cache). The replacement for teams that won't use Nx Cloud is the self-hosted HTTP cache that landed in Nx 20.8: two environment variables and any server that implements Nx's OpenAPI spec.
NX_SELF_HOSTED_REMOTE_CACHE_SERVER=https://cache.example.com
NX_SELF_HOSTED_REMOTE_CACHE_ACCESS_TOKEN=<token>
We tested how it behaves on Nx 23.2.1. Four things worth knowing before you switch.
1. Copying .nx/cache between CI runs no longer gives hits
The old free trick was actions/cache (or Azure Cache@2) on .nx/cache plus NX_REJECT_UNKNOWN_LOCAL_CACHE=0. With the database cache (default since Nx 20, the only cache since 21), Nx only restores artifacts it has metadata for, and that metadata isn't in .nx/cache. Restore the folder on a fresh runner and you get:
Unrecognized Cache Artifacts ... Nx can only restore artifacts it has metadata about
and zero hits. NX_REJECT_UNKNOWN_LOCAL_CACHE=0 is ignored. Background: https://nx.dev/docs/kb/unknown-local-cache
2. An unreachable cache server fails the run
Point NX_SELF_HOSTED_REMOTE_CACHE_SERVER at a closed port and Nx exits 1 with Failed to send request ... /v1/cache/<hash> before the task runs. If your cache can be down, gate it:
code=$(curl -s -o /dev/null --max-time 3 -w '%{http_code}' \
-H "Authorization: Bearer $NX_SELF_HOSTED_REMOTE_CACHE_ACCESS_TOKEN" \
"$NX_SELF_HOSTED_REMOTE_CACHE_SERVER/v1/cache/0") || code=000
if [ "$code" = "000" ]; then
echo "Remote cache unreachable, using local cache only"
export NX_SKIP_REMOTE_CACHE=true
fi
Any HTTP status (404 for the dummy hash is normal) means the server is up. We only tested a server that's down at the start of the run, not one that dies mid-run.
3. --skip-remote-cache and --skip-nx-cache are different
-
--skip-remote-cache/NX_SKIP_REMOTE_CACHE=true(Nx 20.5+): skips only the remote cache. Nx prints "Remote Cache Disabled" and still uses the local one. -
--skip-nx-cache/NX_SKIP_NX_CACHE=true: skips local and remote, so everything really reruns. -
nx resetonly ever clears the local cache. Nothing in the Nx CLI deletes remote entries.
4. The server must refuse overwrites
The API is just GET and PUT on /v1/cache/{hash}. The part that matters is that PUT on an existing hash returns 409, and a read-only token gets 403. That's the fix for CREEP (CVE-2025-36852), the cache-poisoning flaw that got the bucket plugins deprecated: a PR build must not be able to replace an artifact that main will later restore. If you run your own server, test both before you trust it. Spec: https://nx.dev/docs/kb/self-hosted-caching#build-your-own-caching-server
Disclosure: we build Cachely, a hosted implementation of that spec (read-only tokens and 409 enforced at the API, free plan). We wrote a migration guide for each of the four deprecated packages that also covers Nx Cloud and running your own server: https://cachely.dev/nx-s3-cache?utm_source=devto&utm_medium=social&utm_campaign=nx-migration
Top comments (0)