Skip to content

Lesson 2.5: Tagging and Pushing Images

Welcome to the final lesson of Phase 2! You've built custom images and optimized them with multi-stage builds. Now it's time to share your work. In this lesson, you'll learn how to tag images meaningfully, push them to a registry like Docker Hub, and pull them anywhere. By the end, you'll be able to distribute your containerized applications to the world (or your team).


Learning Objectives

TIP

By the end of this lesson, you will be able to:

  • Understand Docker image naming conventions (repository, tag, digest).
  • Tag local images with docker tag.
  • Log in to a container registry (Docker Hub) using docker login.
  • Push images to a registry with docker push.
  • Pull images from a registry using docker pull.
  • Choose a versioning strategy for tags (e.g., latest, semantic versioning).
  • Briefly describe private registries and their use cases.
  • Avoid common security pitfalls when pushing images.

1. Docker Image Naming

Before pushing, you need to understand how Docker images are named and identified.

1.1. Repository and Tag

A full image name has the format:

[registry/][namespace/]repository[:tag]
  • registry: The hostname of the registry (defaults to Docker Hub if omitted).
  • namespace: Often a username or organization (e.g., library for official images on Docker Hub). On Docker Hub, if you omit namespace, it implies library (official images). For your own images, you typically use your Docker Hub username.
  • repository: The name of the image (e.g., ubuntu, myapp).
  • tag: A label identifying a specific version or variant (e.g., latest, 1.0, slim). If omitted, defaults to latest.

Examples:

  • ubuntu → Docker Hub, library/ubuntu, tag latest.
  • yourusername/myapp:v1.0 → Docker Hub, yourusername/myapp, tag v1.0.
  • quay.io/prometheus/node-exporter:latest → Quay.io registry, prometheus/node-exporter, tag latest.

1.2. Digest

Every image also has a unique digest (a SHA256 hash) that identifies its exact content. You can refer to an image by digest to ensure you always get the same image, even if the tag changes.

yourusername/myapp@sha256:45b23dee08af5e43a7fea6c4cf9c25ccf269ee113168c19722f87876677c5cb2

Use docker images --digests to see digests of local images.


2. Tagging Images Locally

The docker tag command creates a new tag (reference) for an existing local image. It does not create a new image; it just adds another name.

Syntax:

bash
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

Examples:

  1. Tag an existing myapp image as yourusername/myapp:1.0:
    bash
    docker tag myapp:latest yourusername/myapp:1.0
  2. Tag an image by ID:
    bash
    docker tag a1b2c3d4 yourusername/myapp:1.0
  3. Add the latest tag to a specific version:
    bash
    docker tag yourusername/myapp:1.0 yourusername/myapp:latest

Now docker images will show multiple tags pointing to the same image ID.


3. Docker Hub and Other Registries

3.1. Docker Hub

Docker Hub is the default public registry. It hosts official images and millions of community images. To push your own images, you need a Docker Hub account.

  • Free account: unlimited public repositories, one private repository.
  • Paid plans: more private repos.

3.2. Other Registries

You can also use other registries:

INFO

  • GitHub Container Registry (ghcr.io) – integrated with GitHub.
  • Google Container Registry (gcr.io) – for Google Cloud.
  • Amazon Elastic Container Registry (ECR) – for AWS.
  • Azure Container Registry (ACR) – for Azure.
  • Quay.io – from Red Hat.
  • Private self-hosted registry (e.g., using the registry:2 image).

Pushing to these is similar but requires different login credentials.


4. Logging In to a Registry

Before pushing, you must authenticate with the registry. Use docker login.

Syntax:

bash
docker login [OPTIONS] [SERVER]
  • If SERVER is omitted, defaults to Docker Hub.

Example for Docker Hub:

bash
docker login

You'll be prompted for username and password (or personal access token). For security, consider using a personal access token instead of your password (Docker Hub supports tokens).

Example for another registry:

bash
docker login ghcr.io

Logout:

bash
docker logout

5. Pushing Images

Once you're logged in and your image is tagged appropriately, use docker push.

Syntax:

bash
docker push NAME[:TAG]

Example:

bash
docker push yourusername/myapp:1.0

Docker uploads the image layers to the registry. If the layers already exist (e.g., you pushed a different tag of the same image), only the manifest is updated.


6. Pulling Images

After pushing, you (or anyone) can pull the image from any machine with Docker installed.

bash
docker pull yourusername/myapp:1.0

If the tag is omitted, :latest is assumed.

You can also run directly without an explicit pull:

bash
docker run yourusername/myapp:1.0

If the image is not local, Docker automatically pulls it.


7. Versioning Strategies

Tagging consistently is crucial for maintainability. Common strategies:

TIP

  • latest tag: Always points to the most recent stable version. Useful for development, but can be ambiguous.
  • Semantic versioning (e.g., 1.2.3):
    • 1 – major version tag (points to latest 1.x.x).
    • 1.2 – minor version tag (points to latest 1.2.x).
    • 1.2.3 – exact patch.
  • Environment tags (e.g., staging, production) – but careful, these can be misleading if not immutable.
  • Git commit hash (e.g., git-abc1234) – for traceability.

Best practice: Tag with both a version and latest, e.g., push 1.0 and also update latest to point to the same image.


8. Private Registries

For proprietary code, use a private registry. You can:

  • Use Docker Hub's private repositories (one free private repo).
  • Use cloud provider registries (ECR, GCR, ACR) which are private by default.
  • Run your own registry with the official registry image.

Running a local registry for testing:

bash
docker run -d -p 5000:5000 --name registry registry:2

Then tag and push to localhost:5000/myapp:1.0.


9. Security Considerations

DANGER

Never push secrets (API keys, passwords) in your images. Even if you delete them in a later layer, they remain in the layer history. Use environment variables at runtime or secret management.

  • Use personal access tokens instead of your password for docker login.
  • Regularly scan your images for vulnerabilities (Docker Hub can do this automatically for private repos).
  • Sign your images with Docker Content Trust (advanced) to ensure integrity.

Hands-On Tasks

Task 1: Create a Docker Hub Account (if needed)

  1. Go to hub.docker.com and sign up for a free account.
  2. Verify your email.
  1. In Docker Hub, go to Account Settings → Security → New Access Token.
  2. Give it a name (e.g., "CLI access") and generate.
  3. Copy the token (you won't see it again).

Task 3: Login to Docker Hub

bash
docker login

Use your username and the token (or password).

Task 4: Tag an Existing Image

  1. If you don't have a custom image, build a simple one (e.g., the Go app from the previous lesson).
  2. Tag it with your Docker Hub username and a version:
    bash
    docker tag myapp:latest yourusername/myapp:1.0
    docker tag myapp:latest yourusername/myapp:latest
  3. Verify with docker images.

Task 5: Push the Image

bash
docker push yourusername/myapp:1.0
docker push yourusername/myapp:latest

Watch the layers upload. If you push the same image again, it will be instant (cached).

Task 6: Verify on Docker Hub

  1. Go to your Docker Hub repositories page. You should see myapp with tags 1.0 and latest.

Task 7: Pull and Run from Another Machine (or Simulate)

If you have another machine with Docker, pull and run:

bash
docker run yourusername/myapp:1.0

If not, you can simulate by removing the local image first:

bash
docker rmi yourusername/myapp:1.0
docker pull yourusername/myapp:1.0
docker run yourusername/myapp:1.0

Task 8: Explore an Image's Digest

bash
docker images --digests yourusername/myapp

Note the digest. Try pulling by digest:

bash
docker pull yourusername/myapp@sha256:<digest>

Task 9: Push to a Different Tag

Tag and push a new version, e.g., 1.1.

bash
docker tag yourusername/myapp:1.0 yourusername/myapp:1.1
docker push yourusername/myapp:1.1

Task 10: (Optional) Run a Local Registry

  1. Start a local registry container:
    bash
    docker run -d -p 5000:5000 --name registry registry:2
  2. Tag your image for localhost:
    bash
    docker tag yourusername/myapp:1.0 localhost:5000/myapp:1.0
  3. Push to local registry:
    bash
    docker push localhost:5000/myapp:1.0
  4. Remove local image and pull from local registry to test.

Summary

Key Takeaways

  • Images are named with [registry/][namespace/]repository[:tag]. Tag defaults to latest.
  • docker tag creates additional references to an existing local image.
  • Registries store and distribute images; Docker Hub is the default.
  • Use docker login to authenticate, then docker push to upload.
  • docker pull downloads images (often done automatically by docker run).
  • Choose a versioning strategy (semantic versioning + latest is common).
  • Private registries keep images secure; you can run your own.
  • Never embed secrets in images; use tokens for authentication.

Check Your Understanding

  1. What is the full format of a Docker image name? Give an example with a custom registry, namespace, and tag.
  2. How do you create a new tag myapp:prod for an existing image myapp:2.0?
  3. What command do you use to authenticate with a registry?
  4. After tagging an image with your Docker Hub username, what command pushes it?
  5. Why should you avoid using your Docker Hub password directly in docker login?
  6. What is an image digest, and why might you use it instead of a tag?
  7. How would you set up a private registry on your local machine for testing?
Click to see answers
  1. [registry/][namespace/]repository[:tag]. Example: gcr.io/myproject/myapp:v1.0 (gcr.io is the registry, myproject is the namespace/project, myapp is the repository, v1.0 is the tag).
  2. docker tag myapp:2.0 myapp:prod
  3. docker login (for Docker Hub) or docker login <registry-hostname> for other registries.
  4. docker push yourusername/myapp:tagname
  5. Because your password grants full account access. A personal access token can be scoped to specific permissions and revoked without affecting your account password.
  6. A digest is a SHA256 hash that uniquely identifies the exact image content. Using a digest ensures you always pull the exact same image, even if someone has retagged latest to point to a different version.
  7. Run docker run -d -p 5000:5000 --name registry registry:2. Then tag images as localhost:5000/myapp:tag and push to localhost:5000.

Additional Resources


Next Up

This concludes Phase 2: Building Custom Images. You are now equipped to create, optimize, and share your own images. In Phase 3, we'll dive into Data Persistence & Storage – a critical topic for stateful applications. See you there!