If you maintain a monorepo with Nx or Lerna, you know how much remote caching helps: whatever a developer or a CI job has already built doesn't need to be built again by anyone else. The problem is where to host that cache without paying for a managed service and without building a server from scratch.
That's where Cacheiro comes in: a free, open source remote cache server for Nx and Lerna. And with the new Cacheiro Instants feature, spinning up one of these servers is now a single docker run.
In this post I'll cover where Cacheiro Instants came from, what it delivers and how to use each of the four variants.
What is Cacheiro
Cacheiro is a self-hosted, open source (MIT) server that implements the Nx remote cache OpenAPI specification. It works with Nx and Lerna (which relies on Nx for task orchestration), it's modular, and it has four storage backends: filesystem, Amazon S3, Google Cloud Storage and Azure Blob Storage.
Why does it exist? Nx deprecated its official self-hosted cache packages after CVE-2025-36852, a critical cache poisoning vulnerability, and left two alternatives: its paid service or building your own server. Cacheiro is my answer to the second one, built to be reused by anyone. The full story is in the launch article.
To make your workspace use the server, you only need to set two environment variables (the token is optional):
export NX_SELF_HOSTED_REMOTE_CACHE_SERVER="https://mycache.server"
export NX_SELF_HOSTED_REMOTE_CACHE_ACCESS_TOKEN="your-secure-token-here"
And run your usual commands:
nx run-many -t build
# or
lerna run build
The motivation: from library to ready-made image
Originally, Cacheiro was distributed only as a library. It could be embedded into existing systems and customized freely. That philosophy gives developers freedom, but it creates extra work for people who just want to use the cache server, especially DevOps teams.
The first step: Cacheiro Runner
To reduce that friction, the project already offered Cacheiro Runner, a ready-to-use example application that runs Cacheiro. It loads the configuration, validates it, creates the store and starts the server. But Cacheiro Runner is an opinionated implementation, meant to serve as a reference: to use S3, GCS or Azure, for example, you have to edit the source code and adjust the Dockerfile.
The solution: Cacheiro Instants ⚡
Cacheiro Instants is exactly the missing step: 100% ready-to-use Docker images, in four flavors (fs, s3, gcs and azure). You run the image in your CI or on your infrastructure, pass a parameter or two, and that's it.
No build, no code configuration, no custom deploy.
The images live on the GitHub Container Registry (GHCR):
| Flavor | Image |
|---|---|
| Filesystem | ghcr.io/rerodrigues/cacheiro-instants-fs |
| Amazon S3 | ghcr.io/rerodrigues/cacheiro-instants-s3 |
| Google Cloud Storage | ghcr.io/rerodrigues/cacheiro-instants-gcs |
| Azure Blob Storage | ghcr.io/rerodrigues/cacheiro-instants-azure |
Each image includes only the store package and the cloud SDK for its own flavor. The fs image, for example, carries no AWS, Google or Azure SDK.
The full documentation for every environment variable is on the Cacheiro Instants page of the project site.
Getting it running
All flavors share the same base configuration: port 3000 and host 0.0.0.0 are already set in the image, and authentication is done with a bearer token in CACHEIRO_AUTH_TOKEN. If you don't set the token, authentication is disabled, so always set one in exposed environments.
Filesystem (fs)
The simplest option to get started, or for a single server. Data lives in /cache, which you mount as a volume:
docker run -p 3000:3000 \
-e CACHEIRO_AUTH_TOKEN=my-secret-token \
-v $(pwd)/cache:/cache \
ghcr.io/rerodrigues/cacheiro-instants-fs
In this flavor, artifacts expire after 7 days by default (CACHEIRO_CACHE_TTL_DAYS, and 0 disables expiration).
Amazon S3 (s3)
It works with S3 and also with compatible alternatives such as MinIO, LocalStack, Cloudflare R2 and DigitalOcean Spaces (via S3_ENDPOINT):
docker run -p 3000:3000 \
-e CACHEIRO_AUTH_TOKEN=my-secret-token \
-e S3_BUCKET=my-nx-cache \
-e S3_REGION=us-east-1 \
-e AWS_ACCESS_KEY_ID=... \
-e AWS_SECRET_ACCESS_KEY=... \
ghcr.io/rerodrigues/cacheiro-instants-s3
Google Cloud Storage (gcs)
Credentials follow Google's standard mechanisms (service account file or workload identity):
docker run -p 3000:3000 \
-e CACHEIRO_AUTH_TOKEN=my-secret-token \
-e GCS_BUCKET=my-nx-cache \
-v $(pwd)/gcp-credentials.json:/creds.json:ro \
-e GOOGLE_APPLICATION_CREDENTIALS=/creds.json \
ghcr.io/rerodrigues/cacheiro-instants-gcs
Azure Blob Storage (azure)
docker run -p 3000:3000 \
-e CACHEIRO_AUTH_TOKEN=my-secret-token \
-e AZURE_CONTAINER=my-nx-cache \
-e AZURE_STORAGE_CONNECTION_STRING="..." \
ghcr.io/rerodrigues/cacheiro-instants-azure
Instead of the connection string, you can also use AZURE_ACCOUNT_NAME.
Features worth highlighting
Read-only token. With CACHEIRO_AUTH_READ_ONLY_TOKEN you create a second token that only allows reads (GET). A common pattern is to give the full-access token only to the main branch CI, and the read-only one to developer machines and PRs. There's a post on the project site that goes deeper into this fine-grained token control.
Built-in healthcheck. The images ship with a Docker HEALTHCHECK that queries the server's /health endpoint. It follows the port and TLS settings you configured, and it doesn't install anything extra in the image. The result shows up right in docker ps, and orchestrators like Docker Compose can use it to know when the cache is ready:
docker ps
# STATUS: Up 2 minutes (healthy)
Clear configuration errors. All configuration comes from environment variables and is validated before the server starts. If something required is missing or invalid, the container fails right away and lists all the problems at once, instead of making you find them one by one.
Native HTTPS. To enable TLS in any flavor, point to the certificate and key and mount them as a read-only volume:
docker run -p 3000:3000 \
-e CACHEIRO_AUTH_TOKEN=my-secret-token \
-e CACHEIRO_TLS_CERT_FILE=/certs/cert.pem \
-e CACHEIRO_TLS_KEY_FILE=/certs/key.pem \
-v $(pwd)/certs:/certs:ro \
ghcr.io/rerodrigues/cacheiro-instants-fs
Always up to date, predictably versioned
This was one of my priorities. Whenever any package in the Cacheiro ecosystem gets a new version, the corresponding images are updated too.
There are two kinds of tags:
-
Version tag (for example,
0.4.0): follows the version of the@renatorodrigues/cacheiro-instantspackage and never changes once published. It's the one to use in production, because it guarantees the same image today and six months from now. -
latest: points to the newest build of each flavor. Great for trying things out, but it can change without notice.
To pin the version in production:
docker pull ghcr.io/rerodrigues/cacheiro-instants-s3:0.4.0
Two more details:
- Each flavor is published independently, so only the flavor whose store changed gets a new
latest. - The images are multi-architecture (amd64 and arm64), which helps people developing on Apple Silicon Macs and running on ARM instances.
Runner or Instants: which one should you pick?
My recommendation:
- Just want a cache server up and running? Use Cacheiro Instants.
- Want to customize the behavior, write your own store or embed the server in another application? Use the Cacheiro library, with Cacheiro Runner as a starting point.
Testing locally before going to the cloud
The repository includes local emulators for S3, GCS and Azure Blob, so you can test each store without creating any real cloud resources. The instructions are in the emulators section of the README.
Conclusion
Remote caching shouldn't require an infrastructure project. With Cacheiro Instants, the path to a shared CI cache has three steps: pick a flavor, set a few environment variables and run a container.
If you use Nx or Lerna, give it a try. Pick the flavor that fits your infrastructure, start the container and run your build twice: the second run is the one that shows the value.
If the project helped you, leave a star on the repository, open an issue with suggestions or bugs, or tell me in the comments which flavor you'd use and why. Feedback is what moves Cacheiro forward the most.
Links
- Cacheiro repository: https://github.com/rerodrigues/nx-remote-cache-server
- Cacheiro Instants: https://github.com/rerodrigues/nx-remote-cache-server/tree/main/packages/cacheiro-instants
- Cacheiro Runner: https://github.com/rerodrigues/nx-remote-cache-server/tree/main/packages/cacheiro-runner
- Cacheiro site and documentation: https://rerodrigues.github.io/nx-remote-cache-server/
- Cacheiro Instants page on the site: https://rerodrigues.github.io/nx-remote-cache-server/packages/instants
- Cacheiro launch article: https://dev.to/rerodrigues/creating-your-own-remote-cache-server-for-nx-and-lerna-with-cacheiro-2dcg
Top comments (0)