Suggest an editImprove this articleRefine the answer for “Docker registry vs Docker Hub: what is the difference?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**A Docker registry** is any service that stores and serves images using the OCI Distribution Spec. **Docker Hub** is one specific public registry, run by Docker Inc, and the default when you `docker pull` without a registry prefix. ```bash $ docker pull nginx # implicit: docker.io (Docker Hub) $ docker pull ghcr.io/myorg/api:1.0 # GitHub Container Registry $ docker pull 1234.dkr.ecr.us-east-1.amazonaws.com/api:1.0 # AWS ECR $ docker pull harbor.internal/team/api:1.0 # self-hosted Harbor ``` **Key:** Docker Hub is one example of a registry. Production teams usually pick a different one (ECR, GCR, GHCR, Harbor) for control over auth, network, and pull rate limits. Same image format, same `docker push/pull` commands, different host.Shown above the full answer for quick recall.Answer (EN)Image**A Docker registry is the type; Docker Hub is one instance of that type.** Confusing the two is one of the more common framing mistakes when people start working with images outside of Docker Hub. ## Theory ### TL;DR - **Registry** = a server that stores and distributes images per the OCI Distribution Spec. Generic term. - **Docker Hub** = the specific public registry hosted by Docker Inc at `docker.io`. The default when no registry is specified in the image name. - Same protocol, same image format, same client commands. The only differences between registries are: who runs them, what auth they require, what network they live on, what limits they impose. - Production teams usually pick a private registry close to their workloads (AWS ECR for AWS-hosted services, GCR for GCP, GHCR for GitHub-centric teams). - Self-hosted options (Harbor, the open-source `distribution`/`registry`) exist when you need full control over storage, auth, and air-gapped operation. ### Quick example ```bash # Same image format, four different registries: $ docker pull nginx:1.27 # docker.io implicit $ docker pull ghcr.io/myorg/api:1.0 # GitHub $ docker pull 123456789.dkr.ecr.eu-west-1.amazonaws.com/api:1.0 # AWS ECR $ docker pull harbor.example.com/team/api:1.0 # self-hosted Harbor ``` The registry name is the prefix before the first `/`. No prefix means Docker Hub. ### What every registry does Every OCI-compliant registry implements the same set of HTTP endpoints (the OCI Distribution Spec): - `GET /v2/<name>/manifests/<tag-or-digest>` - fetch an image manifest - `GET /v2/<name>/blobs/<digest>` - fetch a layer or config blob - `PUT /v2/<name>/manifests/<tag>` - push a manifest - `POST /v2/<name>/blobs/uploads/` then `PATCH`/`PUT` - push a blob - `DELETE /v2/<name>/manifests/<digest>` - delete (where allowed) When you `docker push myreg.example.com/myapp:1.0`, the daemon hits these endpoints. There is no "Docker Hub protocol" - they all speak the same. ### Docker Hub specifically A registry, plus extras that come from Docker Inc running it: - **Free public repos**, unlimited count. - **Free tier private repos**, with strict limits beyond which you pay. - **Official Images** program: curated images for `nginx`, `postgres`, `redis`, etc. Maintained by Docker Inc and the upstream projects. - **Verified Publisher** program: vendor-published images (e.g., `mongo`, `bitnami/...`). - **Anonymous pull rate limit**: 100 pulls per 6 hours per IP. Authenticated pulls have higher limits. - **Webhooks** on push, integrations with GitHub/Bitbucket for autobuilds. - **Search and tag browsing** UI at `hub.docker.com`. If you remove the extras and look only at the protocol, Docker Hub is just a really big registry. ### When to use which registry ``` Docker Hub - good for: open-source projects, learning, public images AWS ECR - good for: AWS-hosted production (no egress fees from same region) Google GCR/Artifact - good for: GCP-hosted production Azure ACR - good for: Azure-hosted production GitHub GHCR - good for: GitHub-centric teams, free for public Harbor - good for: self-hosted, vulnerability scanning, air-gapped GitLab Registry - good for: GitLab-centric CI/CD Quay (Red Hat) - good for: enterprise OpenShift workflows ``` A common production pattern: build in CI -> push to two registries (a primary close to prod, a backup somewhere else) -> deploy from the primary. ### Pull-through caches and mirrors Docker Hub's anonymous rate limit caused real CI outages around 2020-2021. Modern fix: pull-through caches. ``` your CI / cluster -> caching mirror (ECR Pull-through, GAR mirror, Harbor) -> Docker Hub ``` First pull goes to Docker Hub once, gets cached in your mirror. Subsequent pulls hit the mirror only. You stop burning rate limits, and pulls are faster (closer network path). ### Authentication models | Registry | Typical auth | |---|---| | Docker Hub | Username + personal access token | | AWS ECR | IAM role (`aws ecr get-login-password \| docker login --password-stdin`) | | Google Artifact Registry | Service account key or workload identity | | GitHub GHCR | GitHub PAT or `GITHUB_TOKEN` in Actions | | Harbor | Robot accounts (long-lived tokens scoped to a project) | | Self-hosted (basic) | htpasswd, OIDC, or LDAP | The `docker login <registry>` command works the same for all. The credential mechanism differs. ### Common mistakes **Calling Docker Hub "the registry"** It is *a* registry, not *the* registry. Saying "push it to the registry" without naming which one leads to confused new hires. "Push to Docker Hub" or "push to our ECR" is more precise. **Forgetting the registry prefix on push** ```bash # WRONG: tries to push to Docker Hub under your username $ docker tag myapp:1.0 myapp:1.0 $ docker push myapp:1.0 # requested access to the resource is denied # RIGHT for ECR $ docker tag myapp:1.0 1234.dkr.ecr.us-east-1.amazonaws.com/myapp:1.0 $ docker push 1234.dkr.ecr.us-east-1.amazonaws.com/myapp:1.0 ``` Without the registry prefix, Docker assumes Docker Hub. The error message is misleading. **Hitting Docker Hub anonymous rate limits and not realizing** ``` ERROR: toomanyrequests: You have reached your pull rate limit. You may increase the limit by authenticating and upgrading. ``` Common in CI behind shared NAT IPs. Fix: authenticate (`docker login`) or set up a pull-through cache. Anonymous limit is 100 per 6h per IP - a busy CI shared with 20 jobs can burn it in minutes. **Self-hosting a registry without auth** The open-source `distribution` registry runs without authentication by default. Useful for testing, terrible for production. An exposed registry is read/write to anyone who can reach it. Front it with a reverse proxy that does basic auth, OIDC, or Harbor's full access control. **Mixing registry mirrors with credentials** If you point Docker at a private mirror via `registry-mirrors`, that mirror needs your credentials too. A common debugging tale: image pulls work locally but fail in CI because the mirror is configured but the CI has not authenticated to it. ### Real-world usage - **Cloud-native production:** ECR/GCR/ACR for the cloud you run on. Pulls are within the same VPC region, so they are fast and free of egress. - **Multi-cloud / on-prem:** Harbor as a single registry across clouds, with replication policies that mirror images to cloud-specific registries near each region. - **Open-source projects:** Docker Hub for the public image, GHCR mirror for free CI on public PRs, no Docker Hub rate-limit problems for contributors. - **Air-gapped environments (banks, defense, classified):** internal Harbor or `distribution` instance behind a one-way sync from public registries. Production never reaches the public internet directly. ### Follow-up questions **Q:** If I push to AWS ECR, can someone with Docker pull from it without using AWS tools? **A:** Only if they authenticate. The pull command itself is plain `docker pull`, but ECR requires an IAM-derived token. `aws ecr get-login-password | docker login ...` produces a short-lived token (12 hours) that the daemon uses. After that, `docker pull` works as usual. **Q:** Can the same image live in multiple registries? **A:** Yes. Tag the image once for each registry and push: ```bash $ docker tag myapp:1.0 docker.io/youruser/myapp:1.0 $ docker tag myapp:1.0 ghcr.io/youruser/myapp:1.0 $ docker push docker.io/youruser/myapp:1.0 $ docker push ghcr.io/youruser/myapp:1.0 ``` Same bytes, two homes. Used for redundancy, migrations, and serving the same image to consumers in different ecosystems. **Q:** What is the difference between Docker Hub and GitHub Container Registry? **A:** Both serve OCI images. GHCR is integrated with GitHub: it uses your GitHub account, integrates with `GITHUB_TOKEN` in Actions, and ties image visibility to repo visibility. Docker Hub is independent. For a project hosted on GitHub, GHCR removes one set of credentials and gives you free public hosting without Docker Hub's rate limits on pulls. **Q:** Can I run my own registry? **A:** Yes. The open-source CNCF `distribution` project (formerly `docker/distribution`) is the reference implementation. `docker run -d -p 5000:5000 registry:2` gives you a running registry in seconds. For production, use Harbor on top of `distribution` for auth, scanning, and a UI. Or run `distribution` directly behind nginx with basic auth. **Q:** (Senior) How would you design image distribution for a global Kubernetes deployment with 50+ regions? **A:** A regional mirror per cluster (or per cloud region), populated by a hub-and-spoke replication. Build once in CI, push to a primary registry (Harbor or your cloud's), let a replication policy mirror to each regional registry. Pull-through caches handle public images. Consumers in each region pull only from their nearest mirror, so a single registry outage does not stop deploys globally. The trade-off is operational complexity around image freshness and replication lag. ## Examples ### Authenticating against four different registries ```bash # Docker Hub $ docker login -u myuser # type personal access token # AWS ECR (token auto-rotates every 12h) $ aws ecr get-login-password --region us-east-1 \ | docker login --username AWS --password-stdin 123456789.dkr.ecr.us-east-1.amazonaws.com # GitHub Container Registry $ echo $GITHUB_PAT | docker login ghcr.io -u myuser --password-stdin # Self-hosted Harbor $ docker login harbor.example.com -u robot$ci -p <robot-token> ``` Four different auth flows, one CLI, four entries in `~/.docker/config.json` after. ### Replicating an image from Docker Hub to ECR ```bash # Pull from Hub $ docker pull nginx:1.27-alpine # Tag for ECR $ docker tag nginx:1.27-alpine \ 123456789.dkr.ecr.us-east-1.amazonaws.com/mirror/nginx:1.27-alpine # Push to ECR $ docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/mirror/nginx:1.27-alpine ``` Now your AWS workloads can pull `nginx` from ECR (in-region, free, fast) instead of Hub (cross-region, rate-limited). Production setups automate this with `crane copy` or a Harbor replication rule. ### Running a private registry locally for a quick test ```bash $ docker run -d -p 5000:5000 --name registry registry:2 $ docker tag myapp:1.0 localhost:5000/myapp:1.0 $ docker push localhost:5000/myapp:1.0 The push refers to repository [localhost:5000/myapp] ... 1.0: digest: sha256:9f8e7d6c... size: 1247 $ docker pull localhost:5000/myapp:1.0 ``` A fully working registry in one command. No auth, no TLS, only good for local testing - but it shows that Docker Hub is one specific server, and any OCI-spec-compliant alternative is interchangeable at the protocol level.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.