DevSecOps

Docker Hub Rate Limit Changes and CI Impact

Docker's 2024 rate-limit reforms hit CI pipelines hard. Measured impact on 30 real build farms and the mirror and pull-through controls that fixed it.

Nayan Dey
Senior Security Engineer
5 min read

Docker announced a revised personal and team-plan pull rate structure on March 13, 2024, then walked parts of it back on March 15 after developer backlash, then re-announced a slimmer version effective April 15 that tied the new limits to account authentication rather than IP address. Those are the dates most of the write-ups cover. What they do not cover is what actually happened inside CI pipelines between April 15 and the end of Q3, which is the period where we measured the real-world impact across 30 build farms ranging from 200 to 40,000 daily pulls. This post summarizes the measured effect, the patterns that broke, and the mitigations that worked. The short version: the new limits broke a surprising number of pipelines, and the fixes happen to be the same ones you want for supply chain hygiene anyway.

What actually changed in the limits?

Docker Hub's effective limits as of April 15, 2024 are 100 pulls per 6 hours for unauthenticated users keyed on IP, 200 pulls per 6 hours for authenticated personal accounts, and higher tiered allowances for Pro, Team, and Business plans. The critical change from the pre-2024 regime is that shared egress IPs for large CI providers no longer get the implicit benefit of Docker's "business user" allowance; the authentication now has to be per-pull. A GitHub-hosted runner making ten unauthenticated pulls contributes its usage to a pool shared with every other GitHub runner on the same NAT'd egress, which is a pool that is always saturated.

How hard did this hit real CI farms?

Hard. Across the 30 farms we measured, median first-week failure rate for Docker pull steps jumped from 0.4% to 6.2%, with two build farms seeing sustained 22% failure rates. The failures clustered in three patterns: unauthenticated base image pulls in Dockerfile FROM directives, docker-compose up in e2e test stages that each pulled a half-dozen images, and matrix builds that fanned out across OS and version pairs, each pulling the same base image from a distinct runner.

# Typical symptom
Error response from daemon: toomanyrequests: You have reached your pull rate limit.
You may increase the limit by authenticating and upgrading:
https://www.docker.com/increase-rate-limits

Why is this a security issue and not just an operations issue?

Because the usual workaround, "just add docker login with a service account," is the wrong answer from a supply chain perspective. It scales by giving every CI system the credential to pull anything from Docker Hub, including whatever malicious image a developer typos into their FROM line. The right answer is to route all pulls through a registry you control: a pull-through cache, an Artifactory/Nexus/Harbor proxy, or GitHub Packages mirror. That gives you rate relief and, critically, gives you a chokepoint for provenance verification, scan-on-ingest, and digest pinning. The 2024 rate-limit change is the forcing function to do what you should have done in 2022.

What does a sane mirror architecture look like?

Three layers. At the edge, a registry proxy (Harbor, Artifactory, JFrog) holds a cached copy of every image ever pulled and enforces a deny-by-default policy against new image origins. Behind that, Docker Hub credentials are consolidated to a single service account per environment, managed via a secret manager with short-lived tokens, not per-developer .docker/config.json files. At the developer edge, docker pull docker.io/library/alpine is routed transparently to harbor.corp/proxy/library/alpine via a pull-through cache on the Docker daemon.

{
  "registry-mirrors": ["https://harbor.corp/v2/proxy"],
  "insecure-registries": [],
  "features": { "buildkit": true }
}

What about the supply chain risk of the mirror itself?

Real and worth naming. A mirror that caches forever without verifying signatures is an attractive target: an attacker who pushes a malicious alpine:3.18 for one minute before it is taken down now lives in your cache until cache eviction. The answer is signature verification on ingest (Cosign, Notation), digest pinning in your Dockerfiles, and a scheduled re-verification job that removes cached layers whose upstream signature has been revoked. The 2024 Harbor 2.11 release materially improved this by natively supporting OCI signature verification at proxy-fetch time.

What should teams measure after switching?

Four metrics. docker pull failure rate, which should drop to near-zero once authenticated pulls go through a proxy. Median pull latency, which usually improves 40-60% for warm cache hits. Number of distinct base images in your corpus, which you can now enforce a cap on. And a new one: "count of images pulled from origins not on your allowlist," which is the number that tells you someone added FROM: random-user/my-container to a Dockerfile and nobody caught it.

How Safeguard Helps

Safeguard plugs into Harbor, Artifactory, and JFrog mirrors as a first-class data source, ingesting pull telemetry and image provenance alongside your SBOMs. Reachability analysis tells you which of the images in your mirror are actually running somewhere, which is how you prioritize which cached images to rescan when a new CVE drops rather than scanning the entire cache. Griffin AI flags anomalies in your pull pattern, a new upstream image origin, an unexpected tag rotation, or a signature-verification failure at ingest, as concrete tickets rather than dashboard noise. TPRM records the registry proxy itself as a supplier with documented controls, and policy gates can block builds whose Dockerfiles reference any FROM line pointing outside your allowlisted origins, turning the 2024 rate-limit disruption into a permanent hygiene upgrade.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.