Push and Pull From a Registry Without Surprises
Run a registry in a container, push and pull an image, read the common errors and learn what the Docker Hub steps look like. After this, you can move an image to another machine and pull it back by tag or by digest.
Course content
Sign in to save your progress.
-
Dockerfiles That Don't Rot
-
.dockerignore and What Belongs in the Build Context
-
ENTRYPOINT, CMD and When to Override Them
-
-
-
Data That Outlives Containers
-
Bind Mounts vs Named Volumes: Which One When
-
Permissions, Ownership and Why the Container Can't Write
-
-
-
Networks Beyond Defaults
-
Bridge Networks, Aliases and DNS
-
Publish Ports Without Exposing Everything
-
-
-
Compose for Real Apps
-
Services, depends_on and Healthchecks
-
Environment Files, Secrets and What Not to Commit
-
-
-
Images Across Machines
-
Tag Images So You Can Find Them Later
-
Push and Pull From a Registry Without Surprises
-
-
- Final Check: Docker Intermediate
Skip ahead: If you already push to a registry every day, skip to "The surprises", then go on to the next lesson.
An image on your computer helps nobody else. A registry is a server that stores images so other machines can pull them. Docker Hub is the best known one, and you can run your own. This lesson uses a registry that runs in a container on your computer, so you can push and pull for real without an account or a password. At the end I explain the Docker Hub version in text only, and I say plainly what I did not run.
Run your own registry
The registry is itself an image, registry:2. Publish its port on loopback only, so nothing outside your computer can reach it.
docker run -d --name shelf-registry -p 127.0.0.1:5055:5000 registry:2 | Out-Null
Start-Sleep -Seconds 3
curl.exe -s http://localhost:5055/v2/_catalog
{"repositories":[]}
The registry answers with an empty list of repositories. Now build something to put in it.
FROM alpine:3.24
CMD ["echo", "hello from the registry"]
Set-Location reg-demo
docker build -q -t shelf-app:1.0.0 . | Out-Null
docker image ls shelf-app --format "{{.Repository}}:{{.Tag}}"
shelf-app:1.0.0
The name decides where it goes
A push goes to the registry named in the image's name. Without one, Docker assumes Docker Hub. Push the image as it is, with no registry in the name.
docker push shelf-app:1.0.0 2>&1 | Select-String "push refers|denied" | ForEach-Object { $_.ToString().Trim() }
The push refers to repository [docker.io/library/shelf-app]
push access denied, repository does not exist or may require authorization: server message: insufficient_scope: authorization failed
Docker tried docker.io/library/shelf-app, which is Docker Hub's official-images area, and Hub refused because I am not logged in and that namespace is not mine. If you ever see "requested access to the resource is denied" or "insufficient_scope", the same thing is happening to you. You are pushing to a place you do not own, usually because the name is missing your namespace or the registry's address.
Give the image a name that starts with the registry's address and push again.
docker tag shelf-app:1.0.0 localhost:5055/shelf-app:1.0.0
docker push localhost:5055/shelf-app:1.0.0 2>&1 | Select-String "push refers|digest:" | ForEach-Object { $_.ToString().Trim() }
The push refers to repository [localhost:5055/shelf-app]
1.0.0: digest: sha256:d5250a8f920f56c037c1b27baaed3c8ddd10f28fdfb99350f8daa06c8f870e94 size: 855
The digest is the registry's identity for this exact image, and it ends the push. Ask the registry what it holds. It speaks plain HTTP, so curl works.
curl.exe -s http://localhost:5055/v2/_catalog
curl.exe -s http://localhost:5055/v2/shelf-app/tags/list
{"repositories":["shelf-app"]}
{"name":"shelf-app","tags":["1.0.0"]}
Pull it back
To prove the round trip, remove every local copy and pull from the registry. On a second computer the steps would be the same: it needs network access to the registry and the full name.
docker image rm localhost:5055/shelf-app:1.0.0 shelf-app:1.0.0 | Out-Null
docker pull localhost:5055/shelf-app:1.0.0 2>&1 | Select-String "Status:" | ForEach-Object { $_.ToString().Trim() }
docker run --rm localhost:5055/shelf-app:1.0.0
Status: Downloaded newer image for localhost:5055/shelf-app:1.0.0
hello from the registry
The image came from the registry, not from a local copy, and it ran. I did this on one computer, so the "other machine" here is a clean image store, not a second PC.
The surprises
The tag is not the identity. A tag can be moved to a different image later. The digest cannot. Pulling by digest gets exactly the image you tested.
$ref = docker image inspect localhost:5055/shelf-app:1.0.0 --format "{{index .RepoDigests 0}}"
$ref
docker image rm localhost:5055/shelf-app:1.0.0 | Out-Null
docker pull $ref 2>&1 | Select-String "Status:" | ForEach-Object { $_.ToString().Trim() }
localhost:5055/shelf-app@sha256:d5250a8f920f56c037c1b27baaed3c8ddd10f28fdfb99350f8daa06c8f870e94
Status: Downloaded newer image for localhost:5055/shelf-app@sha256:d5250a8f920f56c037c1b27baaed3c8ddd10f28fdfb99350f8daa06c8f870e94
The platform matters. An image is built for one CPU type, and a computer with another type cannot run it unless the image was built for both.
docker image inspect $ref --format "{{.Architecture}} {{.Os}}"
amd64 linux
This one is amd64 on Linux. A computer with an ARM processor, such as a recent Mac or a Raspberry Pi, needs an image built for arm64, or one built for several platforms with docker buildx build --platform. I did not run that here, so I am describing it from Docker's documentation.
Plain HTTP is allowed only for local registries. Docker treats localhost as safe. Any other registry address must use HTTPS, or you have to list it as an insecure registry in Docker's settings. Do not turn that on to make an error go away on a network you share.
The Docker Hub version
I did not run these steps. They need a Docker Hub account and an access token, and I did not use a real one. This is what they look like.
docker login --username YOUR-HUB-NAME
docker tag shelf-app:1.0.0 YOUR-HUB-NAME/shelf-app:1.0.0
docker push YOUR-HUB-NAME/shelf-app:1.0.0
docker login asks for a password. Use an access token that you create in your Hub account settings, with only the permission you need, and never put it in a file or a command line you keep. On Windows, Docker Desktop stores the login with the system credential store. docker logout removes it. A repository you push to becomes public if your Hub plan makes new repositories public by default, so check the setting before you push anything private.
docker rm -f shelf-registry
docker image rm shelf-app:1.0.0
Which images in your own projects exist only on one computer, and what would you lose if that computer died today?
About
Run a registry in a container, push and pull an image, read the common errors and learn what the Docker Hub steps look like. After this, you can move an image to another machine and pull it back by tag or by digest.
- Duration
- 5 min
- Last update
- 08/10/2026
Comments (0)
Sign in to commentNo comments yet.