Push images to Docker Hub
Learn how to push Docker images to Docker Hub registry with practical steps. Understand tagging, authentication, and troubleshooting for reliable image distribution.
Focus: push images to docker hub registry
You've built a Docker image locally — maybe a Python web app, an API server, or a batch processor. But that image is trapped on your machine. When you deploy to a production server, a CI/CD pipeline, or share it with a colleague, you need a central place to store and retrieve it. Docker Hub is that registry — like GitHub for containers. Without pushing your image there, every deployment requires you to rebuild from scratch, breaking portability and consistency. This lesson teaches you how to push images to Docker Hub registry, turning your local build into a globally accessible artifact.
The problem this lesson solves
After building a Docker image, you have a self-contained runtime environment. But what happens when you want to run it on another machine? You cannot physically transfer the entire image over email or a thumb drive (though it's technically possible, it's impractical for images that are 500 MB to 2 GB).
Manual rebuilds break reproducibility. If you SSH into a server and rebuild the image from the same Dockerfile, subtle differences in system libraries, Docker versions, or base image tags can produce a different binary. This leads to the classic "it works on my machine" nightmare.
Team collaboration stalls. When a teammate needs your image for integration testing, they either need your source code and build context, or you need a shared artifact store. A registry solves both problems: you push once, and anyone with access can pull the exact same image.
CI/CD pipelines expect a registry. Modern deployment pipelines (GitHub Actions, GitLab CI, Jenkins) pull images from a registry to deploy. They don't SSH into build machines. If you can't push, you can't automate.
Core concept / mental model
Think of Docker Hub as a version-controlled filing cabinet for container images. Each image is stored in a repository (like a folder), and each version is tagged (like a file label). The push operation uploads your local image to the remote cabinet with its tags intact.
The flow is simple: 1. Tag your image with the registry URL (the address of your cabinet) 2. Authenticate (show the cabinet your credentials) 3. Push (upload the layers)
Key terminology:
- Registry: The server storing images. Docker Hub is the default public registry at
docker.io. - Repository: A collection of related images, often named after the project (e.g.,
nginx,your-username/your-app). - Tag: A label for a specific version of an image (e.g.,
v1.0,latest,production). - Image reference: The full name:
registry.example.com/username/repository:tag
Pro tip: Public repositories on Docker Hub are free, but anyone can pull your image. For private repositories, consider Docker Hub's paid plans or use alternative registries like AWS ECR or GitHub Container Registry.
How it works step by step
Pushing an image involves four discrete stages. Let's walk through them conceptually before the hands-on.
Step 1: Build a test image
You need an image to push. For this lesson, we'll create a minimal Nginx-based image as a demo.
Step 2: Sign in to Docker Hub
Before pushing, you must tell Docker your identity. You do this with docker login. This creates an entry in your local Docker config file (~/.docker/config.json) storing your authentication token.
Step 3: Tag your image correctly
Docker does not push an image unless its name matches the registry format. The required format is:
<registry-address>/<username>/<repository>:<tag>
For Docker Hub, the registry address is implicit (docker.io), so you can use:
<your-username>/<repository>:<tag>
If you omit the registry address, Docker assumes Docker Hub.
Step 4: Push the image
docker push <image-reference> triggers the upload. Docker compresses and uploads each image layer independently, so if you push again after a small change, only modified layers upload.
Hands-on walkthrough
Let's push a real image to Docker Hub. You'll need a free Docker Hub account. If you don't have one, sign up at hub.docker.com.
1. Create a minimal Dockerfile
Create a file called Dockerfile with:
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
Create a file index.html:
<!DOCTYPE html>
<html><body><h1>Hello from Docker Hub!</h1></body></html>
2. Build the image locally
docker build -t my-demo-image:latest .
Verify it works: docker run -d -p 8080:80 my-demo-image:latest and visit http://localhost:8080.
3. Authenticate with Docker Hub
docker login
You'll be prompted for your Docker Hub username and password (or access token). If successful, you see:
Login Succeeded
Pro tip: For automation (CI/CD), use a personal access token from Docker Hub settings instead of your password. Tokens can be scoped to read-only or limited repositories.
4. Tag the image with your registry username
Replace your-username with your actual Docker Hub username:
docker tag my-demo-image:latest your-username/my-demo-image:latest
Check the image list:
docker images
You should see both the original and the new tag — they share the same image ID.
5. Push to Docker Hub
docker push your-username/my-demo-image:latest
If everything works, you'll see layer upload progress:
The push refers to repository [docker.io/your-username/my-demo-image]
e23bd2f54b3c: Pushed
b8b9f3a4c7c2: Pushed
...
latest: digest: sha256:a1b2c3d4... size: 1234
6. Verify the push
Visit https://hub.docker.com/r/your-username/my-demo-image in your browser. You should see the repository with latest tag.
7. Pull your image on another machine (or clean local copy)
Remove the local image to simulate a fresh environment:
docker rmi your-username/my-demo-image:latest my-demo-image:latest
Now pull from Docker Hub:
docker pull your-username/my-demo-image:latest
Run it again: docker run -d -p 8081:80 your-username/my-demo-image:latest
Expected output: The same "Hello from Docker Hub!" page appears — identical bits, on any machine.
Compare options / when to choose what
Docker Hub is not the only registry. Here's how it compares to popular alternatives:
| Feature | Docker Hub (Public) | Docker Hub (Private) | GitHub Container Registry (ghcr.io) | AWS ECR |
|---|---|---|---|---|
| Cost | Free for public repos | Paid starting at $5/month | Free for public, free storage with GitHub Actions | Pay per GB stored + data transfer |
| Integration | Native docker client | Same | GitHub Actions via GITHUB_TOKEN |
IAM roles/keys |
| Security scanning | Basic (paid) | Basic (paid) | Basic (Dependabot integration) | Advanced (Inspector) |
| Rate limits | 100 pulls/6h (free) | More generous | By GitHub plan | No per-user limit |
| Best for | Open-source images, personal side projects | Small teams, quick prototypes | GitHub-centric CI/CD pipelines | AWS-native workloads, enterprise |
Pro tip: For production teams, consider using Docker Hub as a mirror but a cloud-specific registry (ECR, GCR, ACR) for primary storage — they offer better performance when pulling from the same cloud provider.
Troubleshooting & edge cases
Error: denied: requested access to the resource is denied
Cause: You're pushing to a repository name you don't own — probably a typo in the username or repository name, or you didn't login.
Fix: Verify docker login works, then check your image tag: docker image ls | grep your-username/.
Error: unauthorized: authentication required
Cause: Token expired or you're using a GitHub Container Registry without proper auth.
Fix: Run docker logout then docker login again, or generate a new personal access token.
Layer upload fails mid-push
Cause: Network interruption, or you hit Docker Hub's anonymous rate limit (if you didn't login before pushing).
Fix: Ensure you're logged in. For large pushes (multi-GB), use docker push with --all-tags or upload in smaller chunks.
Image is too large (values around 2GB+)
Regression: Docker Hub has a default file size limit of 10GB per layer (very rare). But network timeouts occur.
Workaround: Optimize image size using multi-stage builds or .dockerignore. For CI, push image to multiple registries.
Tag name isn't recognized
Problem: You tag an image as my-app:latest without the repo prefix.
Result: Docker pushes to library/my-app (official library) — which your account cannot access.
Fix: Always include your username: docker tag original-app your-username/my-app:latest.
What you learned & what's next
You now understand the core concept of pushing images to Docker Hub registry — from authentication and tagging to pushing and verifying. You can:
- Explain why a registry is essential for reproducible deployments
- Authenticate with
docker loginusing password or token - Tag images correctly:
username/repository:tag - Push and pull images to/from Docker Hub
- Choose between Docker Hub, GHCR, and ECR based on needs
- Troubleshoot authentication and rate-limit errors
Next lesson: Pull images from Docker Hub registry — learn best practices for docker pull, image digest pinning for reproducibility, and integrating with CI/CD pipelines.
Key takeaway: Once an image is pushed, it becomes immutable and shareable. Treat images as versioned artifacts, not rebuilds.
Practice recap
Practice exercise: Build a minimal Python app, push it to Docker Hub with a version tag (e.g., v1.0.0), then delete your local image and pull it back. Verify it runs identically. Next, try tagging another image with latest and push again — observe that the digest changes.
Common mistakes
- Forgetting to tag an image with your username before pushing — Docker tries to push to
library/which you don't own. - Using your Docker Hub password in CI scripts instead of a personal access token — tokens can be revoked and scoped per repository.
- Pushing an image with the
latesttag without versioning — subsequent pushes overwrite the tag, breaking reproducibility. - Not verifying the upload on hub.docker.com — a successful push message doesn't guarantee the image is accessible (check permissions).
Variations
- Push to Docker Hub using
docker push username/repo:tagversusdocker push registry-1.docker.io/username/repo:tag(full URL) — both work but the latter is explicit. - Use a personal access token from Docker Hub settings (recommended for automation) instead of a password for
docker login. - Consider pushing to alternative registries like GitHub Container Registry (
ghcr.io) or AWS ECR for tighter cloud integration.
Real-world use cases
- Push a Python web app image to Docker Hub so a CI/CD pipeline can deploy it to a production Kubernetes cluster.
- Share a GPU-optimized TensorFlow image with a remote team for reproducible ML experiments using the same library versions.
- Store a multi-GB game server image on Docker Hub for beta testers to pull and run locally without rebuilding from source.
Key takeaways
- Docker Hub is the default public registry —
docker pushassumes Docker Hub unless otherwise specified. - Authentication is mandatory:
docker loginbefore push; use personal access tokens for CI/CD. - Correct tagging format:
docker tag source-image username/repository:tag— always prefix with username. - Push layers incrementally: only modified layers upload on subsequent pushes, saving bandwidth.
- Verify after push:
docker pullon another machine to confirm the image is accessible and identical. - Different registries (Docker Hub, GHCR, ECR) offer trade-offs in cost, integration, and rate limits.
Keep learning
Related tutorials, quizzes, and articles for this topic.
Discussion
Questions, corrections, and tips help everyone reading this page.
0 comments
Add a comment
No comments yet — start the thread.