Welcome back, intrepid Docker explorer! So far, you’ve mastered building custom Docker images, running them as containers, and even making them talk to each other. That’s fantastic! But what good are your brilliant creations if they’re stuck on your machine? It’s like baking the most delicious cake but never letting anyone taste it!

In this chapter, we’re going to unlock the power of sharing your Docker images with the world (or at least your team!). We’ll dive into the world of container registries, focusing on the most popular one: Docker Hub. You’ll learn how to properly prepare your images for sharing, push them to a public registry, and pull them down from anywhere. We’ll also touch upon the concept of private registries for when you need a bit more exclusivity.

By the end of this chapter, you’ll not only be able to show off your Docker images but also collaborate more effectively and streamline your deployment pipelines. Ready to become a Docker image distributor? Let’s go!


Core Concepts: The Image Library

Imagine Docker images are like books you’ve written. A container registry is like a massive library where you can store your books for others to find, read, and even check out. It’s a centralized repository for Docker images.

There are two main types of registries:

  1. Public Registries: Accessible to everyone (like a public library). Docker Hub is the most famous example.
  2. Private Registries: Restricted access, usually for organizations or specific teams (like a private collection or a company’s internal library).

Let’s start with the most common one: Docker Hub.

Docker Hub: The World’s Largest Image Library

Docker Hub is Docker’s official cloud-based registry service. It’s where most public Docker images (like ubuntu, nginx, node, python) are stored and made available. It also allows individuals and organizations to host their own public or private image repositories.

Why is Docker Hub important?

  • Collaboration: Easily share images with teammates.
  • Deployment: Your production servers can pull images directly from Docker Hub.
  • Version Control: Store different versions of your application images.
  • Accessibility: Your images are available from any machine with Docker installed.

Image Tagging: Giving Your Creations a Proper Name

Before you can push an image to a registry, it needs a proper name and tag that tells the registry where it belongs. Think of it like putting your name and a version number on your book cover.

A fully qualified image name for a registry looks like this: [REGISTRY_HOST/][USERNAME/]IMAGE_NAME[:TAG]

  • REGISTRY_HOST/: This is optional. If omitted, Docker defaults to Docker Hub (docker.io).
  • USERNAME/: This is your Docker Hub username (or organization name). It’s crucial for identifying your repositories on Docker Hub.
  • IMAGE_NAME: The name of your repository/image.
  • :TAG: The version or identifier for your specific image. Common tags include latest, 1.0, dev, production. If you don’t specify a tag, Docker often defaults to latest.

For example, if my Docker Hub username is awesomeuser and I have an image named my-web-app at version 2.1, its full name would be awesomeuser/my-web-app:2.1.

We use the docker tag command to apply these names.

Authenticating with docker login

Just like you need a library card to check out books or add your own, you need to authenticate with Docker Hub to push images. This is done securely through the command line.

Pushing Your Image: docker push

Once your image is properly tagged and you’re logged in, the docker push command sends your local image to the specified registry. Docker will upload only the layers that don’t already exist in the registry, making subsequent pushes faster.

Pulling Images: docker pull

This is the command you’ve probably used many times already! docker pull retrieves an image from a registry. If you don’t specify a registry or username, it defaults to pulling from Docker Hub.

Private Registries: When Secrecy Matters

While Docker Hub is great for public images, what if your images contain proprietary code or sensitive information? That’s where private registries come in. They offer:

  • Enhanced Security: Control who has access to your images.
  • Compliance: Meet specific regulatory requirements.
  • Internal Distribution: Keep images within your company’s network.

Popular private registry options include:

  • Cloud Provider Services: AWS Elastic Container Registry (ECR), Google Container Registry (GCR)/Artifact Registry, Azure Container Registry (ACR).
  • Self-Hosted Solutions: You can even run your own Docker Registry server!
  • Integrated with CI/CD: Many CI/CD platforms (like GitLab, GitHub Container Registry) offer built-in private registries.

For this chapter, we’ll focus on Docker Hub, but understand that the core concepts of tagging, logging in, pushing, and pulling apply to private registries as well.


Step-by-Step Implementation: Publishing Your First Image

Let’s get hands-on and publish one of your very own Docker images to Docker Hub!

For this exercise, we’ll assume you have a simple Docker image built from a previous chapter. If not, let’s quickly create a dummy one. We’ll use a basic Nginx image that serves a simple “Hello Docker!” page.

Prerequisites:

  • Docker Desktop 4.27.0 (or later stable version as of December 2025) installed and running. You can check your version with docker --version and docker compose version.
  • A Docker Hub account. If you don’t have one, head over to hub.docker.com/signup and create a free account. It’s quick and easy!

First, let’s make sure we have an image to work with. If you already have a custom image, feel free to use that. Otherwise, follow these quick steps:

Step 1: Create a Simple Nginx Image (If you don’t have one)

Create a new directory called my-nginx-app and navigate into it:

mkdir my-nginx-app
cd my-nginx-app

Now, create a file named index.html inside my-nginx-app:

<!-- my-nginx-app/index.html -->
<!DOCTYPE html>
<html>
<head>
    <title>Hello Docker!</title>
    <style>
        body { font-family: sans-serif; text-align: center; margin-top: 50px; }
    </style>
</head>
<body>
    <h1>Hello Docker from your Custom Image!</h1>
    <p>You're about to share this with the world!</p>
</body>
</html>

Next, create a Dockerfile in the same directory:

# my-nginx-app/Dockerfile
# Use a lightweight Nginx base image
FROM nginx:alpine

# Copy our custom index.html into the Nginx web root
COPY index.html /usr/share/nginx/html/index.html

# Expose port 80 (Nginx's default)
EXPOSE 80

# The default command for nginx:alpine runs Nginx
CMD ["nginx", "-g", "daemon off;"]

Explanation:

  • FROM nginx:alpine: We start with the official nginx:alpine image. alpine is a very small Linux distribution, making our image lightweight.
  • COPY index.html /usr/share/nginx/html/index.html: This copies our index.html file into the default Nginx web server directory inside the container.
  • EXPOSE 80: Informs Docker that the container listens on port 80 at runtime. It’s documentation, not a firewall rule.
  • CMD ["nginx", "-g", "daemon off;"]: This is the command Nginx runs when the container starts. daemon off; ensures Nginx stays in the foreground, which is crucial for Docker containers.

Now, let’s build this image:

docker build -t my-nginx-app:1.0 .

Explanation:

  • docker build: The command to build a Docker image.
  • -t my-nginx-app:1.0: This tags our image with the name my-nginx-app and the version 1.0.
  • .: This tells Docker to look for the Dockerfile in the current directory.

You should now see my-nginx-app:1.0 when you run docker images.

Let’s quickly test it locally:

docker run -d -p 8080:80 --name my-test-server my-nginx-app:1.0

Open your browser to http://localhost:8080. You should see your “Hello Docker from your Custom Image!” message. Don’t forget to stop and remove the test container:

docker stop my-test-server
docker rm my-test-server

Step 2: Log in to Docker Hub

Open your terminal or command prompt and use the docker login command.

docker login

Docker will prompt you for your Docker Hub username and password.

Authenticating with your existing credentials...
Login Succeeded

If you encounter issues, ensure your username and password are correct. You can also generate an access token on Docker Hub for programmatic logins, which is a best practice for automation. For manual CLI login, your password is fine.

Step 3: Tag Your Image for Docker Hub

Now, we need to re-tag our my-nginx-app:1.0 image with your Docker Hub username. This tells Docker that this image belongs to your account on Docker Hub.

Important: Replace YOUR_DOCKERHUB_USERNAME with your actual Docker Hub username!

docker tag my-nginx-app:1.0 YOUR_DOCKERHUB_USERNAME/my-nginx-app:1.0

Explanation:

  • docker tag: The command to add an additional tag to an existing image.
  • my-nginx-app:1.0: This is the source image we built locally.
  • YOUR_DOCKERHUB_USERNAME/my-nginx-app:1.0: This is the target tag, formatted for Docker Hub. It includes your username as a prefix, followed by the repository name and version tag.

Verify the new tag exists:

docker images

You should now see two entries for essentially the same image ID, but with different names and tags.

REPOSITORY                            TAG       IMAGE ID       CREATED         SIZE
my-nginx-app                          1.0       a1b2c3d4e5f6   2 minutes ago   23.4MB
YOUR_DOCKERHUB_USERNAME/my-nginx-app  1.0       a1b2c3d4e5f6   2 minutes ago   23.4MB
nginx                                 alpine    f0e1d2c3b4a5   2 weeks ago     23.4MB

(Your IMAGE ID and CREATED time will differ.)

Step 4: Push Your Tagged Image to Docker Hub

With the image correctly tagged and you logged in, you can now push it!

docker push YOUR_DOCKERHUB_USERNAME/my-nginx-app:1.0

Explanation:

  • docker push: This command uploads the specified image to the registry.
  • YOUR_DOCKERHUB_USERNAME/my-nginx-app:1.0: This is the fully qualified image name we just created. Docker knows to push it to Docker Hub because we didn’t specify another registry host.

You’ll see output indicating the layers being pushed. If any layers already exist on Docker Hub (e.g., the nginx:alpine base image layers), they won’t be re-uploaded, which is efficient!

The push refers to repository [docker.io/YOUR_DOCKERHUB_USERNAME/my-nginx-app]
a1b2c3d4e5f6: Pushed
... (other layers) ...
1.0: digest: sha256:abcdef1234567890... size: 734

Step 5: Verify on Docker Hub

Open your web browser and navigate to hub.docker.com. Log in if prompted. Go to your profile (usually by clicking your username in the top right). You should see a new repository named my-nginx-app listed under your repositories! Click on it to see its details, including the 1.0 tag you just pushed.

Congratulations, your image is now publicly available (by default) on Docker Hub! Anyone can now pull and run your image.

Step 6: Pull Your Image (Simulating Another Machine)

To truly appreciate what you’ve done, let’s simulate pulling your image on a different machine. We’ll start by removing the local image so we can pull it fresh.

First, make sure no containers are using the image. If you still have my-test-server running, stop and remove it.

Now, remove the local image:

docker rmi YOUR_DOCKERHUB_USERNAME/my-nginx-app:1.0
docker rmi my-nginx-app:1.0 # Remove the original tag too if you wish

You might need to remove my-nginx-app:1.0 first, then YOUR_DOCKERHUB_USERNAME/my-nginx-app:1.0 as they point to the same image ID. Or simply remove by IMAGE ID.

Once removed, verify with docker images. Your custom image should be gone.

Now, pull your image from Docker Hub:

docker pull YOUR_DOCKERHUB_USERNAME/my-nginx-app:1.0

Explanation:

  • docker pull: Fetches the image from the specified registry.
  • YOUR_DOCKERHUB_USERNAME/my-nginx-app:1.0: The fully qualified name of the image on Docker Hub.

You’ll see Docker downloading the layers. Once complete, run it to confirm it works:

docker run -d -p 8081:80 --name my-pulled-server YOUR_DOCKERHUB_USERNAME/my-nginx-app:1.0

Open your browser to http://localhost:8081. Success! You’ve pulled your own image and run it.

Clean up:

docker stop my-pulled-server
docker rm my-pulled-server

Mini-Challenge: Versioning Your Application

You’ve successfully pushed 1.0 of your my-nginx-app. Now, let’s practice good versioning. Imagine you made a small update to your index.html.

Challenge:

  1. Modify your index.html file in the my-nginx-app directory to say “Hello Docker from Version 2.0!”.
  2. Re-build your image, but this time tag it as my-nginx-app:2.0.
  3. Tag this new 2.0 image with your Docker Hub username (e.g., YOUR_DOCKERHUB_USERNAME/my-nginx-app:2.0).
  4. Push the 2.0 image to Docker Hub.
  5. Pull both 1.0 and 2.0 versions of your image (using their full Docker Hub names) and run them simultaneously on different local ports (e.g., 8080 for 1.0 and 8081 for 2.0). Verify both are running correctly.

Hint:

  • Remember docker build -t ... . to build with a new tag.
  • Remember docker tag SOURCE TARGET to prepare for pushing.
  • Remember docker push ... to send it up.
  • For running simultaneously, use different -p port mappings and --name for each container.

What to observe/learn: You’ll see how easy it is to manage different versions of your application using Docker image tags. This is incredibly powerful for rollbacks, A/B testing, and managing development vs. production builds.


Common Pitfalls & Troubleshooting

Even seasoned Docker users can stumble occasionally. Here are a few common issues you might encounter:

  1. “denied: requested access to the resource is denied” or “unauthorized: authentication required” during docker push:

    • Cause: You’re not logged in, or your Docker Hub username prefix in the image tag is incorrect.
    • Solution:
      • Run docker login again to ensure you’re authenticated.
      • Double-check that your image tag starts with your exact Docker Hub username, like YOUR_DOCKERHUB_USERNAME/my-nginx-app:1.0. A common mistake is forgetting the username part or having a typo.
  2. “No such image: my-nginx-app:1.0” during docker tag or docker push:

    • Cause: The image you’re trying to tag or push doesn’t exist locally with that exact name and tag.
    • Solution:
      • Run docker images to list all local images and verify the correct name and tag. There might be a typo in your command.
      • Ensure you’ve built the image successfully before trying to tag or push it.
  3. docker pull fails with “manifest unknown” or “repository does not exist”:

    • Cause: The image name or tag you’re trying to pull is incorrect, or the image hasn’t been successfully pushed to the registry yet.
    • Solution:
      • Verify the exact image name and tag on Docker Hub (check your browser).
      • Confirm the image was pushed successfully (Step 4 and 5).
      • If it’s a private repository, ensure you’ve run docker login for the correct registry.
  4. docker login fails with “Error response from daemon: Get “https://registry-1.docker.io/v2/": dial tcp: lookup registry-1.docker.io on [::1]:53: read udp [::1]:53->[::1]:53: read: connection refused”:

    • Cause: Often a network connectivity issue or a DNS problem on your local machine.
    • Solution:
      • Check your internet connection.
      • Restart your Docker Desktop application.
      • Sometimes, changing your local DNS settings or restarting your router can resolve this.

Remember, the Docker community and official documentation are always your friends! For more detailed troubleshooting, refer to the official Docker documentation on Docker Hub.


Summary: Your Images, Now Shareable!

Phew! You’ve just taken a massive leap in your Docker journey. Let’s recap what you’ve learned:

  • Container Registries are centralized storage locations for Docker images, like a library for your code.
  • Docker Hub is the most popular public registry, essential for sharing and accessing community images.
  • You use docker login to authenticate with Docker Hub from your terminal.
  • Image Tagging is crucial: docker tag SOURCE_IMAGE[:TAG] YOUR_DOCKERHUB_USERNAME/IMAGE_NAME[:TAG] prepares your image for pushing to your repository.
  • docker push uploads your tagged image to Docker Hub.
  • docker pull retrieves images from Docker Hub (or any other configured registry).
  • Private Registries offer secure, restricted storage for proprietary images, often provided by cloud vendors or self-hosted.
  • Versioning images with different tags (e.g., 1.0, 2.0, latest) is a best practice for managing application updates and rollbacks.

You’re no longer just building images; you’re publishing them! This skill is fundamental for collaborative development, CI/CD pipelines, and deploying applications to production environments.

In the next chapter, we’ll explore how to manage multi-container applications more easily using Docker Compose. Get ready to orchestrate your services like a pro!