Sharing Images: Docker Hub and Registries
Tag and push images to a registry, pull them on another machine, run your own private registry, and choose a sensible tagging strategy.
On this page 12 sections
Introduction
An image on your laptop is only half the story — to deploy it, a server has to get it. Registries are how images travel: you push an image from the machine that built it, and pull it wherever it needs to run.
- How image names tell Docker which registry to use.
- How to tag, push and pull images, using a private registry you run yourself.
- How to publish to Docker Hub and GitHub Container Registry.
- How to tag versions so deployments are predictable.
How Registries Work
A registry is a server that stores images, organised into repositories (one per image name) with many tags each. The first part of an image name says which registry it belongs to — if there is none, Docker assumes Docker Hub:
| Image name | Registry | Repository | Tag |
|---|---|---|---|
| nginx:alpine | docker.io (Docker Hub) | library/nginx | alpine |
| priya/quotes-api:1.0 | docker.io (Docker Hub) | priya/quotes-api | 1.0 |
| ghcr.io/priya/quotes-api:1.0 | ghcr.io (GitHub) | priya/quotes-api | 1.0 |
| localhost:5000/hello-go:1.0 | localhost:5000 (your own) | hello-go | 1.0 |
So pushing to a registry is a two-step job: give the image a name that includes the registry (docker tag), then docker push it.
Running a Private Registry
To practise without creating any accounts, Priya runs Docker’s own open-source registry in a container. It stores the pushed images in a volume:
docker run -d --name registry -p 5000:5000 -v registry-data:/var/lib/registry registry:3Click Run to see what this code prints.
Companies often run their own registry, or use a cloud one such as Amazon ECR, Google Artifact Registry or Azure Container Registry. They all work with the same docker push and docker pull commands.
Tagging and Pushing
Priya’s tiny Go service from lesson 11 is built locally as hello-go. She tags it for her registry as version 1.0:
docker tag hello-go localhost:5000/hello-go:1.0docker imagesClick Run to see what this code prints.
Both names share the same ID. Now she pushes it:
docker push localhost:5000/hello-go:1.0Click Run to see what this code prints.
Each layer was uploaded, and the registry confirmed the digest of the pushed image. Registries have an HTTP API, so you can see what they store:
curl -s http://localhost:5000/v2/_catalogcurl -s http://localhost:5000/v2/hello-go/tags/listClick Run to see what this code prints.
Pulling on Another Machine
To simulate a fresh server, Priya deletes both local names of the image, so it only exists in the registry:
docker rmi hello-go localhost:5000/hello-go:1.0Click Run to see what this code prints.
Then pulls it back, as any machine that can reach the registry would:
docker pull localhost:5000/hello-go:1.0Click Run to see what this code prints.
The digest is identical to the one printed by push — it is byte-for-byte the same image. And it runs:
docker run -d --name hello -p 8080:8080 localhost:5000/hello-go:1.0 > /dev/nullsleep 1curl -s http://localhost:8080/Click Run to see what this code prints.
Adding a second tag to the same image is cheap — the registry already has every layer, so nothing is uploaded again:
docker tag localhost:5000/hello-go:1.0 localhost:5000/hello-go:latestdocker push localhost:5000/hello-go:latestcurl -s http://localhost:5000/v2/hello-go/tags/listClick Run to see what this code prints.
You can only push tags that exist locally:
docker push localhost:5000/hello-go:2.0Click Run to see what this code prints.
Docker allows plain HTTP only for registries on localhost. A registry reachable over a network needs HTTPS and authentication — or use a hosted registry, which provides both.
Pushing to Docker Hub
Docker Hub works the same way. Create a free account at hub.docker.com, then log in from the terminal. For security, create a personal access token in your Docker Hub account settings and paste it when asked for the password:
docker login -u priyaThen tag the image with your Docker Hub username as the namespace, and push. The output looks just like the push above, with docker.io as the registry:
docker tag hello-go priya/hello-go:1.0docker push priya/hello-go:1.0New repositories are public by default, so anyone can docker pull priya/hello-go:1.0. Repositories can also be made private in their settings on Docker Hub; how many private repositories you get depends on your plan.
GitHub Container Registry
If your code is on GitHub, its container registry (ghcr.io) keeps images next to the repository. Create a personal access token with the write:packages scope, then:
docker login ghcr.io -u YOUR-GITHUB-USERNAMEdocker tag hello-go ghcr.io/YOUR-GITHUB-USERNAME/hello-go:1.0docker push ghcr.io/YOUR-GITHUB-USERNAME/hello-go:1.0In practice, most teams push from a CI/CD pipeline — for example GitHub Actions building and pushing a new image on every merge — rather than from a laptop.
Priya, whose Docker Hub username is priya, runs docker push quotes-api:1.0 and gets “denied: requested access to the resource is denied”. What should she do?
A Tagging Strategy
Tags are how you know what is running in production and how you roll back. A common approach is to push several tags for each release:
| Tag | Example | Use |
|---|---|---|
| Exact version | 1.4.2 | Deploy and roll back precisely; never reuse it for different code |
| Minor / major version | 1.4, 1 | Let users follow updates within a version line |
| Git commit | sha-3f9c2e1 | Trace an image back to the exact source code |
| latest | latest | Convenience for trying things out — never deploy it to production |
Put the steps to release version 1.1 of an image to production in order.
- On the server, pull priya/hello-go:1.1 and replace the old container
- Tag it for the registry: docker tag hello-go:1.1 priya/hello-go:1.1
- Build the image: docker build -t hello-go:1.1 .
- Push it: docker push priya/hello-go:1.1
- Test the new image locally
Common Mistakes
- Pushing images that contain secrets — anyone who can pull the image can read them (lesson 9).
- Overwriting an existing version tag with different code, so “1.0” means two different things.
- Deploying latest, then not knowing which version is running or how to roll back.
- Typing your real password into docker login on shared machines instead of using an access token.
Frequently Asked Questions
In ~/.docker/config.json — encoded, not encrypted — unless you configure a credential helper, which stores it in your operating system’s keychain. Docker Desktop sets one up automatically. Use docker logout on shared machines.
Yes. docker buildx build --platform linux/amd64,linux/arm64 --push builds a multi-platform image, and each machine pulls the variant it needs.
Not strictly — you can build on the server itself, or export with docker save and import with docker load. A registry becomes worthwhile as soon as you have CI/CD or more than one machine.
Key Takeaways
- The first part of an image name says which registry it belongs to; none means Docker Hub.
- Publish with docker tag (add the registry name) and docker push; get it with docker pull.
- The digest proves the pulled image is byte-for-byte what was pushed.
- Use access tokens with docker login, and keep secrets out of images.
- Deploy exact version tags, never latest.
Summary
Your images can now travel anywhere. In the final lesson you will bring everything together and make your containers ready for production.