Skip to main content

Tagging, Versioning, and Registries

Every Docker image you build needs a name (tag) and a place to store it (registry). How you name your images determines whether you can trace a production issue back to a specific commit, roll back safely, or even tell which version is running. This lesson covers tagging strategies that make deployments predictable and auditable.

Image Naming Anatomy

A full image reference has this structure:

registry.example.com/team/app:1.4.2
└── registry ──────┘ └─ namespace ─┘ └ tag ┘
PartExamplePurpose
Registryregistry.example.comWhere the image is stored (Docker Hub if omitted)
Namespaceteam/appOrganization and project grouping
Tag1.4.2Human-readable version label
Digest@sha256:abc123...Immutable content hash (assigned by registry)

Tags vs Digests

This is the most important concept in image identification:

flowchart TD
A["Image Content<br/>(layers + config)"] -->|"content hash"| B["Digest: sha256:abc123..."]
B -->|"labeled as"| C["Tag: 1.4.2"]
B -->|"also labeled as"| D["Tag: stable"]
B -->|"also labeled as"| E["Tag: latest"]

F["New Image Content"] -->|"content hash"| G["Digest: sha256:def456..."]
G -->|"now labeled as"| H["Tag: latest ⚠️"]

style B fill:#e8f5e9,stroke:#2e7d32
style G fill:#fff3e0,stroke:#ef6c00
style H fill:#ffebee,stroke:#c62828
PropertyTagDigest
Formatapp:1.4.2app@sha256:abc123...
Mutable?Yes -- can be reassigned to different contentNo -- always points to same content
Use forHuman-readable versioningExact artifact identification

Key insight: Tags can lie. Someone can push a new image with the tag 1.4.2, and the tag now points to different content. Digests never lie -- the same digest always means the same image bytes.

Why latest Is Dangerous

The latest tag is not special or magical. It is just a default tag name that Docker uses when you do not specify one:

docker build -t app .          # Tags as app:latest
docker pull nginx # Pulls nginx:latest

The problem: latest changes silently every time someone pushes without a version tag. You have no way to know which version is running, and no way to roll back to a specific latest.

warning

Never use latest as a deployment target in production. Always use explicit version tags or digests.

For every release, create at least two tags:

# Version tag: human-readable release identifier
docker build -t registry.example.com/app:1.4.2 .

# Commit tag: exact source code traceability
docker tag registry.example.com/app:1.4.2 registry.example.com/app:sha-abc1234

# Push both
docker push registry.example.com/app:1.4.2
docker push registry.example.com/app:sha-abc1234
Tag TypeExamplePurpose
Semantic version1.4.2Human-readable release label
Commit SHAsha-abc1234Trace image back to exact source code
Channel (optional)stable, stagingConvenience pointer for environments

The Promotion Workflow

Build once, promote the same artifact through environments. This ensures staging and production run identical code:

flowchart LR
A["CI Build"] -->|"Build + Tag"| B["Registry"]
B -->|"app:1.4.2"| C["Staging"]
C -->|"Tests Pass"| D["Tag as stable"]
D -->|"Same digest"| E["Production"]

style A fill:#e3f2fd,stroke:#1565c0
style C fill:#fff3e0,stroke:#ef6c00
style E fill:#e8f5e9,stroke:#2e7d32
# CI builds and pushes
docker build -t $IMAGE:$VERSION -t $IMAGE:sha-$GIT_SHA .
docker push $IMAGE:$VERSION
docker push $IMAGE:sha-$GIT_SHA

# After staging validation, promote to stable
docker tag $IMAGE:$VERSION $IMAGE:stable
docker push $IMAGE:stable

The key principle: the image that ran in staging is the exact same image (same digest) that runs in production. You never rebuild for production.

Working with Registries

Pushing an Image

# Log in to your registry
docker login registry.example.com

# Tag the image with the registry path
docker tag my-app:1.4.2 registry.example.com/team/my-app:1.4.2

# Push
docker push registry.example.com/team/my-app:1.4.2

Pulling an Image

# Pull by tag
docker pull registry.example.com/team/my-app:1.4.2

# Pull by digest (guaranteed same content)
docker pull registry.example.com/team/my-app@sha256:abc123...

Inspecting Remote Images

# View image details without pulling
docker manifest inspect registry.example.com/team/my-app:1.4.2

Versioning Strategies

StrategyFormatBest For
Semantic versioning1.4.2APIs and libraries where compatibility matters
Date-based2026.02.13Rolling releases with frequent deploys
Commit-basedsha-abc1234Maximum traceability, every build is unique
Hybrid1.4.2-sha-abc1234Human-readable version with source traceability

Most teams should use semantic version + commit SHA as their standard tag set.

Rollback with Tags

When a deployment fails, you need to deploy the previous known-good version. This only works if:

  1. You have a tagged previous version (e.g., 1.4.1)
  2. That tag has not been overwritten with different content
# Roll back to previous version
docker pull registry.example.com/team/my-app:1.4.1
docker compose up -d

For maximum safety, record the digest of each deployment so you can pull by digest even if tags are moved.

Common Mistakes

MistakeConsequenceFix
Only using latestCannot identify what is running, cannot roll backAlways tag with version + commit SHA
No commit SHA tagCannot trace production issue to source codeAdd sha-<shortsha> tag to every build
Overwriting release tagsRollback goes to wrong versionTreat release tags as immutable
No registry retention policyRegistry storage grows foreverSet up automated cleanup for old CI tags
Not capturing digest after pushCannot prove what was deployedRecord digest in deployment logs

Key Takeaways

  • Tags are mutable (can be moved), digests are immutable (always same content). Use both.
  • Never deploy latest to production -- always use explicit version tags.
  • Build once, promote everywhere -- the same image artifact should flow from CI through staging to production.
  • Tag every build with both a version number and a commit SHA for traceability.
  • Record the digest of every deployment for guaranteed rollback safety.

What's Next