Image Tagging & Registries

Docker image tagging and registries are essential for distributing and versioning container images. This guide covers Docker Hub, private registries (AWS ECR, Azure ACR, Google GCR), semantic versioning strategies, and best practices for pushing and pulling images.

Image Tagging Docker Hub Private Registries Versioning
What Are Image Tags?

An image tag is a human-readable label attached to a specific image ID. Tags allow you to version and identify your images. The full tag format is: [registry/][namespace/]repository:tag. For example, docker.io/library/nginx:1.25-alpine means: registry = docker.io, namespace = library, repository = nginx, tag = 1.25-alpine.

Tags are mutable—you can change which image a tag points to. The latest tag is special but dangerous because it changes. Best practice is to use specific, immutable tags like version numbers or git commit SHAs.

# Tag format examples nginx:latest # Shortest form (defaults to Docker Hub) myapp:v1.0.0 # Custom tag myregistry.azurecr.io/myapp:2.1.0 # Private registry ghcr.io/myorg/backend:prod # GitHub Container Registry 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:latest # AWS ECR
Docker Hub: The Default Public Registry

Docker Hub is the default public registry where Docker pulls images when no registry is specified. It hosts millions of images, including official images from nginx, postgres, node, python, ubuntu, and many more. Official images are maintained by Docker in collaboration with software vendors and are considered secure and well-documented.

You can create a free Docker Hub account to push your own images. Free accounts have unlimited public repositories and one private repository. Paid plans offer more private repositories and team management features.

# Login to Docker Hub docker login # Pull an official image docker pull nginx:alpine # Tag your local image for Docker Hub docker tag myapp:latest myusername/myapp:v1.0.0 # Push to Docker Hub docker push myusername/myapp:v1.0.0 # Pull your image from anywhere docker pull myusername/myapp:v1.0.0 # Search Docker Hub from CLI docker search nginx
Private Registries: AWS ECR, Azure ACR, GCR, GHCR

Private registries store images in your cloud account with IAM-based authentication. They are essential for production environments where you can't push images to public Docker Hub. Major cloud providers offer fully managed registries:

AWS ECR (Elastic Container Registry) - Integrated with ECS and EKS. Uses AWS IAM for authentication.

Azure ACR (Azure Container Registry) - Integrated with Azure Kubernetes Service. Supports geo-replication.

Google GCR (Google Container Registry) / Artifact Registry - Integrated with GKE and Cloud Run.

GitHub Container Registry (GHCR) - Integrated with GitHub packages, uses GitHub tokens.

# AWS ECR # Get login token aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com # Tag and push docker tag myapp:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:latest docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/myapp:latest # Azure ACR az acr login --name myregistry docker tag myapp:latest myregistry.azurecr.io/myapp:v1.0.0 docker push myregistry.azurecr.io/myapp:v1.0.0 # GitHub Container Registry (GHCR) echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin docker tag myapp:latest ghcr.io/username/myapp:latest docker push ghcr.io/username/myapp:latest
Versioning Strategies: How to Tag Your Images

A good tagging strategy makes it easy to identify which version of your application is running, roll back to previous versions, and trace deployments back to source code commits. Here are the most common strategies:

Semantic Versioning (SemVer): Use v1.2.3 format. Increment major for breaking changes, minor for new features, patch for bug fixes. This is the industry standard.

Git Commit SHA: Use the full or short git commit hash (e.g., a1b2c3d). This creates immutable, traceable images—every commit produces a unique tag.

Environment + Version: Combine environment with version: prod-v1.2.3, staging-latest, dev-a1b2c3d.

Multiple tags per image: A single image can have multiple tags. Common pattern: tag with both version and git SHA, and also update the environment tag.

# Semantic versioning docker build -t myapp:1.0.0 . docker tag myapp:1.0.0 myapp:1.0 docker tag myapp:1.0.0 myapp:1 docker tag myapp:1.0.0 myapp:latest # Git commit SHA (from CI pipeline) GIT_SHA=$(git rev-parse --short HEAD) docker build -t myapp:$GIT_SHA . # Multiple tags for same image docker tag myapp:1.0.0 myregistry/myapp:1.0.0 docker tag myapp:1.0.0 myregistry/myapp:prod docker tag myapp:1.0.0 myregistry/myapp:a1b2c3d # Push all tags docker push myregistry/myapp:1.0.0 docker push myregistry/myapp:prod docker push myregistry/myapp:a1b2c3d
Best practice: Use multiple tags per image. Tag with semantic version for humans, tag with git commit SHA for traceability, and tag with environment (like "prod") for deployment.
The Problem with the 'latest' Tag

The latest tag is a mutable pointer. If you rebuild an image and push it with the same latest tag, the tag moves to the new image. This is dangerous because:

  • You can't tell which version is actually running
  • Rolling back becomes ambiguous
  • Multiple environments may get different "latest" images at different times
  • Reproducibility is lost—pulling the same tag later gives a different image

Best practice: Use latest only for local development, never in production. Always use specific version tags in production. If you need a mutable "latest" for convenience, also keep immutable version tags.

Never use :latest in production manifests. Always pin to a specific version tag like :v1.2.3 or :a1b2c3d. This ensures reproducibility and safe rollbacks.
Registry Comparison: Which One Should You Choose?
Registry Best For Authentication Cost
Docker Hub Public images, open source Username/password or token Free for public, paid for private
AWS ECR AWS users, ECS/EKS integration AWS IAM Storage + data transfer
Azure ACR Azure users, AKS integration Azure AD or admin keys Storage + data transfer
Google GCR/AR GCP users, GKE integration Google Cloud IAM Storage + data transfer
GHCR (GitHub) GitHub users, Actions integration GitHub token Free for public, included in GitHub plans
Self-hosted Registry Air-gapped environments, full control Custom (HTTPS basic auth) Your infrastructure costs
Pushing and Pulling: Best Practices
  • Use specific tags in production - Never use :latest. Pin to semantic versions or commit SHAs.
  • Authenticate before pushing - Use docker login or cloud CLI commands before push/pull.
  • Use short-lived credentials - For CI/CD, use OIDC or short-lived tokens instead of long-lived passwords.
  • Pull by digest for absolute immutability - Each image has a content-addressable digest (SHA256). Pulling by digest guarantees the exact image.
  • Use image scanning - Most registries offer vulnerability scanning. Scan images before deploying.
  • Implement retention policies - Old images accumulate storage costs. Set up lifecycle policies to delete untagged or old images.
# Pull by digest (guarantees exact image) docker pull nginx@sha256:abc123def456... # Check image digest docker inspect nginx:latest | grep Digest # Retag from digest docker tag nginx@sha256:abc123... myregistry/nginx:replica # Set up lifecycle policy (AWS ECR CLI example) aws ecr put-lifecycle-policy --repository-name myapp --lifecycle-policy-text '{ "rules": [{ "rulePriority": 1, "description": "Keep only 10 images", "selection": { "tagStatus": "any", "countType": "imageCountMoreThan", "countNumber": 10 }, "action": {"type": "expire"} }] }'
CI/CD Integration: Automating Tagging and Pushing

In CI/CD pipelines, you should automatically tag images and push them to registries. Common patterns include using the git commit SHA as a tag, plus a version tag from your package.json or version file. GitHub Actions, GitLab CI, and Jenkins all have built-in support for pushing to registries.

# GitHub Actions workflow example name: Build and Push on: push: branches: [main] tags: ['v*.*.*'] jobs: build: runs-on: ubuntu-latest permissions: contents: read packages: write steps: - uses: actions/checkout@v4 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Log in to GitHub Container Registry uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Extract metadata id: meta uses: docker/metadata-action@v5 with: images: ghcr.io/${{ github.repository }} tags: | type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} type=sha,format=short - name: Build and push uses: docker/build-push-action@v5 with: context: . push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }}
Frequently Asked Questions
What's the difference between a registry and a repository?
A registry is the server that stores images (like Docker Hub). A repository is a collection of related images within a registry (like myapp). A repository contains multiple tags (versions) of the same image.
Can I have multiple tags pointing to the same image?
Yes! A single image ID can have multiple tags. For example, when you build v1.0.0, you can tag it as both myapp:1.0.0 and myapp:latest. Both tags point to the same image.
How do I remove tags or images from a registry?
Docker Hub: Go to the repository in browser, click "Tags", delete the tag. AWS ECR: Use aws ecr batch-delete-image. Azure ACR: Use az acr repository delete. Most registries have CLI or UI deletion options.
What is a Docker image digest?
A digest is a SHA256 hash of the image's content. It uniquely identifies an image and is immutable. Pulling by digest (image@sha256:...) guarantees you get exactly the same image every time, even if tags move.
How do I authenticate to multiple registries?
You can run docker login multiple times for different registries. Credentials are stored in ~/.docker/config.json. For cloud registries, use their CLI tools (aws ecr get-login-password, az acr login, gcloud auth configure-docker).
Can I run my own private registry?
Yes! Use the official Docker Registry image: docker run -d -p 5000:5000 --name registry registry:2. For production, add TLS certificates and authentication. This is useful for air-gapped environments or CI/CD internal use.
What's the difference between Docker Hub and GHCR?
Docker Hub is Docker's public registry, the default for docker pull. GHCR (GitHub Container Registry) is GitHub's registry, integrated with GitHub Packages. GHCR uses GitHub tokens for authentication and integrates with GitHub Actions. Both work with standard Docker commands.
How do I migrate images between registries?
Pull the image, tag it for the new registry, then push: docker pull source-registry/image:tag, docker tag source-registry/image:tag target-registry/image:tag, docker push target-registry/image:tag.
Previous: Multi-Stage Builds Next: Docker Compose Basics

A consistent tagging strategy is essential for maintainable container deployments. Use semantic versioning, git SHAs, and multiple tags to ensure traceability and reproducibility.