Tech Digest

Guide

Do Diun, Watchtower and docker compose pull count against Docker Hub's pull limit?

The 10 pulls an hour you read about was never enforced. The limit is 100 pulls per 6 hours anonymous and 200 on a free account, and a normal update checker barely touches it. Two specific habits do.

Last reviewed

Do Diun, Watchtower and docker compose pull count against Docker Hub's pull limit?

Mostly no. Diun and Watchtower check for updates with HEAD requests, which Docker says do not count, and pulling an image you already have measured as zero pulls. What costs one pull is downloading a new image. The real risks are Diun's diun.watch_repo=true, which fetches a manifest for every tag on its first run (Nextcloud has over 2,500), and scripts built on GET requests such as docker manifest inspect, which cost 9 pulls on an 8-architecture image. The limit is 100 pulls per 6 hours unauthenticated, 200 on a free account.

Mostly no. Diun and Watchtower check for updates with HEAD requests, which Docker says do not count, and a docker compose pull of an image that is already current cost nothing when we measured it. What costs a pull is downloading a new image, one per image per architecture. Two habits drain the budget: Diun's diun.watch_repo=true, and home-made checkers that use GET requests.

The limit is 100 pulls per 6 hours, not 10 per hour#

Docker's documentation gives the current limits per 6 hours:

AccountPulls per 6 hoursCounted per
Unauthenticated100IPv4 address, or IPv6 /64 subnet
Docker Personal (free, logged in)200Account
Pro, Team, BusinessUnlimited (fair use)Account

The other figure you have seen is real but was never applied. By February 2025 Docker's announced plan was that from 1 April 2025 unauthenticated users would be limited to 10 pulls per hour and free accounts to 100 per hour, as The Register reported at the time. Docker then did not enforce it. Its own update of 8 April 2025 says the 100 and 200 per 6 hours limits "will remain in place", and adds: "We will announce any future enforcement at least 6 months in advance." So if you hit a 429 today, the cause is not a secret 10-per-hour rule. It is something in your stack spending pulls faster than you think.

The per-hour number persists for an ordinary reason: it was widely reported, copied into blog posts and CI guides, and nobody goes back to correct a post about a change that did not happen.

What counts as a pull, measured#

Docker's definition has four lines worth knowing: a pull includes a version check plus any download that results from it; version checks do not count; a normal image costs one pull for its single manifest; and a multi-arch image "will count as one pull for each different architecture". Its documentation also says plainly that a GET for a manifest counts and a HEAD does not.

That is the rule. Here is what it costs in practice, measured against the live registry on 7 October 2026 from one server address, unauthenticated, reading ratelimit-remaining before and after each step (Docker Engine 28.1.1, Compose 2.35.1):

OperationPulls used
HEAD request for a tag's manifest (what Diun and Watchtower do each scan)0
Listing every tag of a repository through /v2/<repo>/tags/list0
docker pull or docker compose pull, image already current0
docker pull of a new image (multi-arch image, one platform)1
GET of a tag's manifest1
docker manifest inspect memcached:1.6 (8 architectures)9, every run

The last row is the one nobody writes down. docker manifest inspect is the obvious building block for a cron script that checks for updates, and on a typical official image it costs as much as nine real pulls. A script that runs it hourly against ten such images uses 90 pulls an hour.

Read your own counter before you guess#

The check costs nothing because it is a HEAD request. This is Docker's documented command, run from the machine that pulls:

bash
TOKEN=$(curl -s "https://auth.docker.io/token?service=registry.docker.io&scope=repository:ratelimitpreview/test:pull" | jq -r .token)
curl -s --head -H "Authorization: Bearer $TOKEN" \
  https://registry-1.docker.io/v2/ratelimitpreview/test/manifests/latest \
  | grep -i '^ratelimit'
# example output:
# ratelimit-limit: 100;w=21600
# ratelimit-remaining: 87;w=21600

w is the window in seconds, so w=21600 is 6 hours. Add --user 'name:token' to the first curl to see an authenticated account's counter instead of your IP's.

One thing we saw that the documentation does not mention: on 7 October 2026 the registry answered our server's address with ratelimit-limit: 100;w=3600, a one-hour window, while the documentation still says six hours. The headers are what the registry enforces against you, so trust them over any page, this one included. Run the check before and after a Diun scan or a nightly pull and you know what each job costs rather than estimating it.

Diun: a HEAD per image, a GET only when something changed#

Diun's registry code makes this explicit. Each scheduled check sends a HEAD to get the remote digest. If it matches the digest stored in Diun's database, the check ends there, and that costs nothing. Only when the digest differs, or the image is new to the database, does Diun GET the manifest to read its metadata, which costs one pull. Diun's own FAQ says the same: it does not GET at each scan, "only when an image has been updated or added on the registry".

So a normal setup is cheap. Forty watched images on a 0 */6 * * * schedule spend nothing on a quiet day and one pull for each image that actually changed.

diun.watch_repo=true breaks that arithmetic. It tells Diun to watch every tag in the repository instead of the one you run, and every tag Diun has never seen is "added", so the first scan fetches a manifest for each. The Diun FAQ warns that it "will fetch manifest for ALL tags". The tag lists themselves are free; it is the per-tag GETs that cost you. Counted on 7 October 2026:

RepositoryTags
library/redis1,196
library/postgres1,427
library/nextcloud2,523
jellyfin/jellyfin13,713

One watch_repo label on Postgres is fourteen times the anonymous budget on the first scan alone, and mutable tags such as 17, 17-alpine and latest keep changing digest, so later scans keep paying. The default for diun.max_tags is 0, which means all of them. If you want watch_repo, bound it:

yaml
labels:
  - "diun.enable=true"
  - "diun.watch_repo=true"
  - "diun.max_tags=10"
  - "diun.include_tags=^17\\.\\d+$"

The usual reason for wanting watch_repo is to hear about a new version when you pin exact tags, which is what An update strategy that does not lose data recommends. An include_tags regex for your current major gives you that for a few pulls.

Watchtower: free checks, but monitor-only still downloads#

Watchtower also checks with a HEAD request; its code comments that it does so "to prevent rate limiting". When the digests match it skips the image, which costs nothing.

Two things cost pulls. When the digests differ, Watchtower pulls the new image, and its documentation notes this happens even with WATCHTOWER_MONITOR_ONLY=true: to know what changed it "will still do a pull whenever the repository digest doesn't match". That is one pull per changed image, the same as you would pay updating by hand. And if the HEAD request fails, Watchtower falls back to a regular pull, logged as "Could not do a head request". By default that is a warning only for registries known to rate limit, mainly Docker Hub; WATCHTOWER_WARN_ON_HEAD_FAILURE=always makes it visible everywhere.

The bigger Watchtower question is not pulls at all: the original project was archived in December 2025. Watchtower vs Diun covers that, and why notify-only is the safer default for anything with a database.

docker compose pull costs what it downloads#

A docker compose pull across a stack is a version check per service, and in our measurement a current image cost nothing. So a nightly pull of 20 services usually spends a few pulls, one per image that changed. The cases that do spend real budget:

  • First deploys and rebuilt hosts. Every image is new. A 30-service host rebuilt from scratch uses 30 pulls or more in one go, before any retries. Moving a service to a new machine is the moment you will see a 429 if you see one at all.
  • Pruning between pulls. docker image prune -a on a schedule removes every image no container uses, so the next pull of any of them downloads again and counts.
  • Multi-platform builds. Docker counts one pull per architecture, so a build for amd64 and arm64 that fetches its base images fresh pays for each one twice.

The budget is per address, which at home means per household#

Unauthenticated pulls are counted per IPv4 address or IPv6 /64. Behind a home router, every machine shares one public IPv4 address: the server, the NAS, a laptop running builds and the Proxmox node all spend the same 100. A Diun instance that looks modest can be the thing that tips a CI job on another machine into a 429.

Logging in fixes both problems at once. The count follows the account rather than the address, and the budget doubles to 200. Diun takes credentials as variables:

yaml
environment:
  DIUN_REGOPTS_0_NAME: "docker.io"
  DIUN_REGOPTS_0_SELECTOR: "image"
  DIUN_REGOPTS_0_USERNAME: "yourname"
  DIUN_REGOPTS_0_PASSWORDFILE: "/run/secrets/dockerhub_token"

Watchtower reads a mounted config.json produced by docker login, and docker compose pull uses that same login on the host. Use an access token rather than your password, and keep it out of the git-tracked compose file, as Docker Compose conventions describes for every secret.

When this answer stops applying#

  • Docker changes the limits. It has promised 6 months' notice. The header check above is how you notice before a guide does.
  • Other registries. Images on ghcr.io or a vendor's own registry do not count against Docker Hub at all, and their limits are not covered here.
  • The window we observed. Our one-hour w=3600 reading was one address on one day, unauthenticated. We did not measure the authenticated window.
  • Watchtower forks. The behavior described is the archived v1.7.1 code. A fork may check differently, so measure it with the header check.

What to do next#

Run the header check now, and again after your update checker's next scan. If the number did not move, your checker is fine and you can stop worrying about this. If it dropped, look for diun.watch_repo labels without max_tags, then for any script calling docker manifest inspect, then log in. Send Diun's notifications somewhere you actually read: ntfy vs Gotify picks the receiver, and The minimum viable monitoring stack covers an alert for the day a pull fails. For whether to update automatically at all, start with An update strategy that does not lose data.

Questions#

Is the Docker Hub limit 10 pulls per hour?

No. Docker announced 10 pulls per hour for unauthenticated users and 100 per hour for free accounts, to start on 1 April 2025, and then did not enforce it. Its 8 April 2025 update says the limits stay at 100 pulls per 6 hours unauthenticated and 200 per 6 hours for Docker Personal, and that any future enforcement will be announced at least 6 months in advance. The 10 per hour figure persists because it was widely reported and copied into guides that were never corrected.

Does docker compose pull use up Docker Hub pulls when nothing changed?

Not in our measurement. On Docker Engine 28.1.1 with Compose 2.35.1, running docker compose pull and docker pull against an image that was already current left ratelimit-remaining unchanged. Docker's documentation describes this as a version check, which does not count. A pull that downloads a new image costs one. So a daily docker compose pull on a stack of 20 images costs only as many pulls as there are new images that day.

How do I see how many Docker Hub pulls I have left?

Request an anonymous token for Docker's ratelimitpreview/test repository, then send a HEAD request for its manifest and read the ratelimit-limit and ratelimit-remaining headers. The HEAD does not use up a pull, so you can run it as often as you like. Run it from the machine that does the pulling, because unauthenticated limits are counted per IPv4 address or IPv6 /64, not per machine. The command is on this page.

Does a multi-arch image count as several pulls?

Only when you fetch several architectures. Docker counts one pull per architecture. A host that pulls its own platform pays one, which is what we measured pulling hello-world. A tool that fetches every platform manifest pays one each: docker manifest inspect memcached:1.6, an image with 8 architectures, cost 9 pulls per run. Multi-platform builds and anything that inspects all platforms pay the multiplied price.

Does logging in to Docker Hub help Diun or Watchtower?

Yes. It doubles the budget from 100 to 200 pulls per 6 hours, and the count follows the account rather than your shared IP address. Diun takes credentials through regopts (DIUN_REGOPTS_0_* variables) or a mounted ~/.docker/config.json. Watchtower reads a mounted config.json. Use a personal access token rather than your account password, and keep it out of the compose file in git.

Do images from ghcr.io or other registries count?

Not against Docker Hub's limit. The limit belongs to Docker Hub, so images pulled from GitHub's registry or any other one are counted, if at all, by that registry's own rules. Moving the images you check most often to another registry they are published on is a real way to reduce Docker Hub usage, but check that the publisher keeps both in step before you switch.

Sources#

Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.