Docker Pull Rate Limit in GitHub Actions: Fixes
Last updated
toomanyrequests: You have reached your pull rate limit means Docker Hub refused a docker pull because the caller hit its limit, and in CI that is usually an anonymous pull from a shared IP address. The fix is to log in to Docker Hub in the workflow, or to pull the same images from a mirror or registry that you control. Limits below are from Docker's pull usage docs, checked October 2026.
The exact message
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
In BuildKit and docker/build-push-action output it shows up as a 429 Too Many Requests on a FROM line or a resolve image config step. The exact tail of the sentence varies between Docker versions, but the HTTP 429 and toomanyrequests are constant.
The current limits
Per a 6-hour window:
| Caller | Pulls |
|---|---|
| Unauthenticated | 100 per IPv4 address or IPv6 /64 subnet |
| Authenticated personal account | 200 per account |
| Pro, Team, Business | Unlimited, subject to fair use |
Docker returns HTTP 429 when the limit is reached. Check the page before relying on these numbers, since Docker has changed them in the past.
Why CI hits it so easily
- Shared IPs. Many jobs from many customers can leave the same egress addresses. Anonymous pulls count against the IP, so a small number from you may be the last straw.
- Many pulls per run. Every
FROM, service container anddocker runis a pull. A matrix of 20 cells pulling 3 images is 60 pulls. - No login. Without credentials, you share the IP's anonymous budget.
- Cache misses. Fresh VMs for every job mean images are pulled again each time.
Fixes, in the order to try them
1. Log in to Docker Hub
Create a Docker Hub access token (Account settings, then Personal access tokens), store it as secrets, and log in before any pull:
steps:
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
This moves you to the authenticated limit for your account. See the docker/login-action readme for registry options. Note that secrets are not available to workflows triggered by pull requests from forks, so those runs still pull anonymously.
2. Authenticate service containers
Job services and container blocks pull before any step runs, so give them credentials directly:
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
credentials:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
3. Pull from a different registry
Many official images are mirrored elsewhere. Copying your base images into GitHub Container Registry (GHCR) or your cloud registry removes Docker Hub from the path. GHCR images are pulled with ghcr.io/OWNER/IMAGE, and in workflows you can log in with GITHUB_TOKEN:
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
Mirror once with a scheduled workflow:
docker pull postgres:16
docker tag postgres:16 ghcr.io/OWNER/mirror/postgres:16
docker push ghcr.io/OWNER/mirror/postgres:16
Check the licence terms of images you mirror. Several clouds also provide public mirrors of Docker Hub official images; use the provider's documentation for the current address.
4. Use a pull-through cache
A registry in pull-through mode (Docker's registry image, or your cloud registry) fetches from Docker Hub once and serves everyone else. Configure the daemon to use it as a mirror:
sudo mkdir -p /etc/docker
echo '{"registry-mirrors": ["https://mirror.example.com"]}' | sudo tee /etc/docker/daemon.json
sudo systemctl restart docker
On GitHub-hosted runners the Docker daemon restart is fine inside a job. See the registry mirror docs.
5. Pull less
- Cache layers with BuildKit (
cache-fromandcache-to) so base images are not resolved from scratch. See Docker layer caching in CI. - Pin images by digest so a tag lookup is not repeated.
- Combine jobs that pull the same image, or save the image as an artifact once per workflow.
- Do not pull images you do not use; check
servicesblocks.
6. Upgrade the Docker plan
Pro, Team and Business accounts are listed as unlimited under fair use. If your team pulls a lot, a paid plan may cost less than the engineering time.
Runner choice and rate limits
Rate limits depend on identity or IP, not on the runner type. On a runner provider with dedicated machines and warm Docker layer caches, fewer pulls leave the machine in the first place. compiler.dev machines (8 vCPU / 16 GB, fresh VM per job) keep Docker layer caches ready and you switch with runs-on: [self-hosted, Linux, compiler-ci]; you should still log in to Docker Hub. See pricing. For Docker image build speed specifically, specialised builders like Depot are strong options; see the comparison.
How to prevent it
- Always run
docker/login-actionbefore builds and pulls. - Authenticate
servicesandcontainerblocks. - Mirror critical base images to GHCR or your own registry.
- Cache layers, and pin digests.
- Alert on 429s in CI so you see it before a release is blocked.
Rate-limit errors for GitHub itself are a different thing: see API rate limit exceeded. Large images are also a common cause of No space left on device.
FAQ
What is the Docker Hub pull limit?
100 pulls per 6 hours for unauthenticated users (per IPv4 address or IPv6 /64), and 200 per 6 hours for authenticated personal accounts. Pro, Team and Business are unlimited under fair use.
Do GitHub-hosted runners have a special Docker Hub limit?
Do not count on it. Treat anonymous pulls as limited and log in.
Does a failed pull count?
Docker's docs describe limits in terms of pulls. A request that returns 429 is not a successful pull, but the earlier ones in the window still count.
Is GHCR rate limited?
GitHub Container Registry has its own policies. Check GitHub's documentation for the current rules. Pulling public images from GHCR does not use Docker Hub's limits.
Related
- Docker Build Slow in CI? Find the Cause and Fix ItDocker build slow in CI? Diagnose build context size, .dockerignore, layer order, cache misses and BuildKit settings, then fix the biggest one first.
- Docker Layer Caching in CI With BuildxMake Docker builds in GitHub Actions fast with buildx cache-from and cache-to, the gha and registry backends, and a Dockerfile ordered for cache hits.
- API Rate Limit Exceeded in GitHub Actions: FixesFix "API rate limit exceeded for installation ID" in GitHub Actions: GITHUB_TOKEN allows 1,000 requests per hour per repo. Secondary limits and fixes.
- Docker Build GitHub Actions Workflow TemplateCopy-paste GitHub Actions workflow to build Docker images with Buildx, GitHub cache layers and a push to GHCR on main, with minimal permissions.