Skip to content

why I built Stashito ​

At work we are mostly on Google Cloud, and Artifact Registry is the source of truth for our images. Then we went multi-cloud. The second provider is smaller and cheaper, and it has no artifact registry of its own, so every node there pulled straight from Google.

Three things hurt at once:

  • Egress. Every pull of the same image from every node on the other cloud left Google and was billed on the way out.
  • Docker Hub rate limits. Base images came from Hub, and a fleet of nodes pulling anonymously hits the cap fast.
  • Latency. Pulling across clouds is slower than pulling across a rack, and image pulls are on the critical path of every deploy and every CI job.

The fix is obvious: a pull-through cache on the second cloud. Pull each image once, serve it locally after that, one egress charge per image instead of one per node.

I looked at what existed and everything I found was enterprise software. Platforms with a database, a cache layer, a UI, a licence tier and a JVM. I ended up on the free version of Artifactory, which worked and was the only decent option I could find at the time.

I also missed registry:2 in proxy mode. The official Docker registry has had a pull-through cache mode for years, and it does most of what I needed. I did not find it, or did not recognise it for what it was, when I was looking. That is on me, and I am saying it here so nobody reads this post as "there was nothing out there".

what I wanted ​

A thing that is simple, fast and reliable. A single container that needs a volume and nothing else, with all of its configuration visible in the compose file, and not written in Java. The same thing should work on a node pool at work and on the dedicated servers I rent for my own projects. I have the same problem there on a smaller scale: my own images live in Amazon ECR, and every host pulling them directly from AWS costs money and waits on the network each time. Stashito sits in front of ECR there for the same two reasons, cost and latency.

And something that could grow. OCI images were the pain that week, but the same shape applies to Python packages, npm, apt, Go modules. The tools that cover all of those are the enterprise ones. I wanted the small version of that idea, starting with images.

what Stashito is ​

A single Go binary implementing the OCI Distribution Spec, shipped as rcm7/stashito for eight Linux platforms.

yaml
services:
  stashito:
    image: rcm7/stashito:0
    environment:
      PORT: "8080"
      STORAGE_PATH: "/data/stashito"
      LOG_LEVEL: "info"
      LOG_FORMAT: "text"
      TAG_TTL: "60s"
      UPSTREAM_DOCKERHUB_HOST: "registry-1.docker.io"
    volumes:
      - stashito_data:/data/stashito
    ports:
      - "8080:8080"

volumes:
  stashito_data:

Six env vars, all required, no hidden defaults. Add an UPSTREAM_<ALIAS>_HOST per registry and one instance fronts Docker Hub, GHCR, Quay, Artifact Registry, ACR and ECR at once, addressed as localhost:8080/<alias>/<image>. Set MIRROR_UPSTREAM and it works as a plain registry-mirrors entry, so existing docker pull postgres lines are served from the cache without changing anything.

In use: the first pull is a MISS from upstream, every later pull is a HIT from local disk. Digests and blobs are never revalidated; tags are, after TAG_TTL. If upstream is down, cached content is served stale. MAX_STORAGE caps the cache and evicts least recently pulled content inline, and a blob that will not fit on the filesystem is refused with 507 before any byte is written. ECR tokens are fetched and rotated for you. /healthz and optional Prometheus /metrics. The docs have the rest.

Stashito and registry:2 ​

Same niche. If registry:2 proxy mode does what you need, use it: it has TLS, auth, S3 and GCS backends and a decade of production behind it.

What Stashito does differently, as of October 2026: several upstreams in one instance instead of one remote per instance, env vars instead of a YAML file, a size cap with LRU eviction instead of a TTL plus manual garbage collection, stale serving when upstream is unreachable, and ECR handled natively. Both projects will move, so check the docs of each before deciding. For now the goal is that it is easy to get to and easy to run.

what it does not do yet ​

As of October 2026:

  • It does not accept pushes. It only caches what it pulls from upstream.
  • No authentication on the pull side and no TLS. Run it on a private network, or behind something that does both.
  • No web UI.
  • OCI images only.
  • Instances do not share state. Two Stashitos are two caches.
  • No scanning, no replication, no retention policies beyond the size cap.

Any of these may change. None of them is a design decision, they are the things I have not needed myself so far.

where it stands ​

It is small, it is fast, and it does one thing. Code is AGPL-3.0 at github.com/RCM7/stashito, and the quickstart takes a few minutes. If you run it and something is missing, open an issue.