Skip to content

Create Docker "pull through" registry cache

Although we now have shared storage for our persistent container data, our docker nodes don't share any other docker data, such as container images. This results in an inefficiency - every node which participates in the swarm will, at some point, need the docker image for every container deployed in the swarm.

When dealing with large container (looking at you, GitLab!), this can result in several gigabytes of wasted bandwidth per-node, and long delays when restarting containers on an alternate node. (It also wastes disk space on each node, but we'll get to that in the next section)

The solution is to run an official Docker registry container as a "pull-through" cache, or "registry mirror". By using our persistent storage for the registry cache, we can ensure we have a single copy of all the containers we've pulled at least once. After the first pull, any subsequent pulls from our nodes will use the cached version from our registry mirror. As a result, services are available more quickly when restarting container nodes, and we can be more aggressive about cleaning up unused containers on our nodes (more later)

The registry mirror runs as a swarm stack, using a simple docker-compose.yml. Customize your mirror FQDN below, so that Traefik will generate the appropriate LetsEncrypt certificates for it, and make it available via HTTPS.




Create /var/data/config/registry/registry.yml as per the following docker-compose example:

version: "3"


    image: registry:2
      - traefik_public
        - traefik.frontend.rule=Host:<your mirror FQDN>
        - traefik.port=5000
      - 5000:5000
      - /var/data/registry/registry-mirror-data:/var/lib/registry
      - /var/data/registry/registry-mirror-config.yml:/etc/docker/registry/config.yml

    external: true

Unencrypted registry

We create this registry without consideration for SSL, which will fail if we attempt to use the registry directly. However, we're going to use the HTTPS-proxied version via [Traefik][traefik], leveraging Traefik to manage the LetsEncrypt certificates required.

Create the configuration for the actual registry in /var/data/registry/registry-mirror-config.yml as per the following example:

version: 0.1
    service: registry
    blobdescriptor: inmemory
    rootdirectory: /var/lib/registry
    enabled: true
  addr: :5000
    X-Content-Type-Options: [nosniff]
    enabled: true
    interval: 10s
    threshold: 3


Launch Docker registry stack

Launch the registry stack by running docker stack deploy registry -c <path-to-docker-compose.yml>

Enable Docker registry mirror

To tell docker to use the registry mirror, edit /etc/docker-latest/daemon.json 1 on each node, and change from:

    "log-driver": "journald",
    "signature-verification": false


    "log-driver": "journald",
    "signature-verification": false,
    "registry-mirrors": ["https://<your registry mirror FQDN>"]

Then restart docker itself, by running systemctl restart docker

Chef's notes 📓

  1. Note the extra comma required after "false" above ↩

Tip your waiter (sponsor) 👏

Did you receive excellent service? Want to compliment the chef? (..and support development of current and future recipes!) Sponsor me on Github / Ko-Fi / Patreon, or see the contribute page for more (free or paid) ways to say thank you! 👏

Employ your chef (engage) 🤝

Is this too much of a geeky PITA? Do you just want results, stat? I do this for a living - I'm a full-time Kubernetes contractor, providing consulting and engineering expertise to businesses needing short-term, short-notice support in the cloud-native space, including AWS/Azure/GKE, Kubernetes, CI/CD and automation.

Learn more about working with me here.

Flirt with waiter (subscribe) 💌

Want to know now when this recipe gets updated, or when future recipes are added? Subscribe to the RSS feed, or leave your email address below, and we'll keep you updated.

Your comments? 💬