Container Registries and Tagging Strategies
A container registry is a storage and distribution service for Docker images, acting as the handoff point between the build stage of a pipeline and every downstream consumer — deployment jobs, other teams, orchestration platforms like Kubernetes. Popular registries include Docker Hub, GitHub Container Registry (GHCR), Amazon ECR, Google Artifact Registry, and self-hosted options like Harbor. Choosing a registry involves considerations of access control, geographic proximity to deployment targets, cost, vulnerability scanning integration, and how tightly it integrates with your CI platform's authentication model. But the registry itself is only half the story — how images are tagged determines whether a team can reliably answer 'which exact image is running in production right now, and how do I roll back to the previous one?'
Cricket analogy: A stadium's equipment store is the handoff point between the bat manufacturer and every team using it — choosing a supplier (SG, Kookaburra, or local) depends on price, quality checks, and proximity to the ground; but the label alone won't tell you which exact bat Kohli used in the World Cup final.
Why the 'latest' Tag Is Dangerous in Pipelines
The 'latest' tag is a mutable pointer, not a version — pushing a new image to the same tag overwrites what 'latest' refers to, meaning two different deployments referencing 'myapp:latest' at different times could run entirely different code with no record of what changed. This makes rollbacks unreliable, debugging painful, and reproducibility impossible, since you cannot pull the exact image that was running last Tuesday once 'latest' has moved on. Production pipelines should almost always deploy using immutable, uniquely identifying tags rather than 'latest', reserving 'latest' at most as a convenience alias for local development.
Cricket analogy: Calling a player 'the current opener' is a mutable label — today it might mean Rohit Sharma, next series someone else entirely after a reshuffle, so a scouting report written against 'the current opener' months ago is useless without knowing exactly who that was at the time.
Immutable Tagging Conventions
The most robust tagging strategy uses the git commit SHA (or a shortened form of it) as the primary tag, since a SHA uniquely and permanently identifies the exact source code that produced the image — there can never be ambiguity about what 'app:a3f9c21' contains. Many teams layer additional, more human-readable tags alongside the SHA: semantic version tags (v2.4.1) for released software, branch-based tags (main, staging) that do move but are understood to be mutable pointers used only for non-production environments, and date-based tags for audit trails. A common pattern pushes the same image under multiple tags simultaneously — the immutable SHA and one or more mutable convenience aliases — so different consumers can reference the level of stability they need.
Cricket analogy: A player's unique BCCI registration ID permanently and unambiguously identifies one specific player, no matter how many nicknames ('Hitman,' 'Captain') get layered on top — the ID never changes even as human-readable nicknames come and go with form and role.
name: push-with-tags
on:
push:
branches: [main]
tags: ['v*']
jobs:
push-image:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata for tags
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/acme/api
tags: |
type=sha,prefix=,format=short
type=ref,event=branch
type=semver,pattern={{version}}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}Think of an immutable SHA tag like a VIN number on a car: it uniquely and permanently identifies one specific build. A branch tag like 'staging' is more like a parking space — useful as a label for 'whatever is here right now,' but the occupant changes constantly.
Deploying directly from a mutable tag such as 'main' or 'latest' means your deployment manifest never actually changes, which breaks GitOps-style workflows that rely on detecting a diff to trigger a rollout — orchestrators may not notice a new image exists at all unless the pod is manually restarted or an imagePullPolicy of Always forces a re-pull.
Registry Cleanup and Retention Policies
Because every pipeline run can push a new uniquely tagged image, registries accumulate storage costs and clutter quickly. Most registries support retention policies that automatically expire untagged or old images after a configurable period, while explicitly protecting tags matching patterns like v* or images referenced by an active deployment. Designing this policy is a balance: too aggressive and you lose the ability to roll back to an older known-good build; too lax and storage costs and image sprawl grow unchecked.
Cricket analogy: A cricket board can't keep every training kit from every net session forever — old, unused gear gets cleared out periodically, but the exact match-day kits from World Cup finals (protected 'tagged' items) are preserved indefinitely regardless of age.
- A container registry stores and distributes images built by CI pipelines to downstream deployment consumers.
- The 'latest' tag is mutable and unreliable for production deployments since it can silently point to different code over time.
- Git commit SHA tags provide immutable, unambiguous traceability from image back to source.
- Semantic version and branch tags are useful human-readable aliases layered alongside SHA tags.
- Deploying from mutable tags breaks GitOps diff-detection workflows that expect manifest changes to trigger rollouts.
- Retention policies balance rollback capability against unbounded registry storage growth.
Practice what you learned
1. Why is deploying production workloads using the 'latest' tag considered risky?
2. What makes a git commit SHA an effective image tag?
3. Why can deploying from a mutable tag like 'main' break GitOps-style workflows?
4. What is a typical purpose of a registry retention policy?
5. In a combined tagging strategy, what role do branch-based tags like 'staging' typically serve?
Was this page helpful?
You May Also Like
Building Docker Images in Pipelines
Understand how CI/CD pipelines build, tag, and optimize Docker images, including layer caching, multi-stage builds, and build-time security checks.
Deploying Containers from CI/CD
Learn the common patterns pipelines use to deploy containerized applications, from direct kubectl updates to GitOps-driven reconciliation.
Rollback and Incident Response in CI/CD
Learn how to design pipelines that can quickly and safely revert a bad deployment, and how rollback strategy fits into a broader incident response process.
Blue-Green Deployments
Understand the blue-green deployment pattern for near-instant, low-risk releases and rollbacks by running two full environments and switching traffic between them.
Related Reading
Related Study Notes in DevOps
Browse all study notesNginx Study Notes
DevOps · 30 topics
DevOpsAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsAdvanced Bash Scripting Study Notes
Bash · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsDocker & Kubernetes Study Notes
YAML · 40 topics