Image Tags and Versioning
A Docker image tag is a readable label attached to image content, such as nginx:1.27-alpine or my-api:1.0.0. Tags matter because they are the names people type in Dockerfiles, Compose files, CI pipelines, and deployments. Used well, tags make releases easy to find and roll back; used carelessly, they make the same command pull different software on different days.
Overview: How Image Tags Work
A Docker image is read-only content described by metadata and stacked filesystem layers. A registry stores image manifests, configuration objects, and compressed layer blobs. A tag is not the image itself. It is a name in a repository that points to a manifest. When you run docker pull redis:7.2-alpine, Docker asks the registry what manifest the 7.2-alpine tag currently points to, then downloads any missing layers by digest.
Image references normally have the form [registry/][namespace/]repository[:tag]. The short reference nginx:1.27-alpine expands to Docker Hub’s official image namespace, effectively docker.io/library/nginx:1.27-alpine. A private or third-party registry is written explicitly, for example ghcr.io/acme/payments-api:1.4.2.
If you omit the tag, Docker uses latest. That does not mean newest, safest, or most stable. It is only a tag name. Image publishers decide what latest points to, and many serious projects avoid using it in production because it can move without warning.
Tags are mutable by design. The owner of my-api:staging can push a new image to the same tag every hour. That is useful for channels such as dev, staging, or stable, but it is weak for reproducibility. A digest, written like nginx@sha256:..., identifies exact content. If a tag is a nickname, a digest is the content address.
Modern images are often multi-platform. A single tag may point to an image index that contains separate manifests for linux/amd64, linux/arm64, and other platforms. Docker Desktop on macOS and Windows still pulls Linux container images into its Linux VM when Linux containers are enabled. That means the same tag may resolve to different platform-specific manifests while still representing the same logical release.
Syntax
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
docker build -t NAME:TAG [-t NAME:ANOTHER_TAG] PATH
docker pull NAME[:TAG|@DIGEST]
docker image inspect IMAGE[:TAG|@DIGEST]
docker push NAME:TAG
| Part | Meaning |
|---|---|
SOURCE_IMAGE |
An existing local image reference or image ID. |
TARGET_IMAGE |
The new repository name and tag you want to add locally. |
-t NAME:TAG |
Applies a name and tag while building. You may pass -t more than once. |
:latest |
The default tag when no tag is written. It is ordinary and mutable. |
@sha256:... |
A digest reference to exact image content. |
docker push |
Uploads the tag reference and any missing content to the target registry. |
docker tag does not copy layers. It adds another local reference to the same image ID. Only docker push publishes that reference to a registry. Locally, several tags can point to the same image; removing one tag with docker image rm may leave the image content available through another tag.
Examples
Example 1: Pull and Inspect a Specific Tag
docker pull nginx:1.27-alpine
docker image inspect --format '{{index .RepoDigests 0}}' nginx:1.27-alpine
Output:
1.27-alpine: Pulling from library/nginx
Digest: sha256:3f6fb2fb8c2c5e2a1f0c7f1bb4d38f0d5ddc5c4b0d9e7a3d6c1a2b3c4d5e6f70
Status: Downloaded newer image for nginx:1.27-alpine
docker.io/library/nginx:1.27-alpine
nginx@sha256:3f6fb2fb8c2c5e2a1f0c7f1bb4d38f0d5ddc5c4b0d9e7a3d6c1a2b3c4d5e6f70
The tag is the friendly input. The digest is the exact content Docker resolved from the registry. Your real digest may differ as maintainers publish patched images under the same tag. For production audit trails, record the digest that was actually deployed.
Example 2: Build One Image with Release and Channel Tags
FROM node:20-alpine
WORKDIR /app
RUN printf 'console.log("inventory api")\n' > server.js
CMD ["node", "server.js"]
docker build -t inventory-api:1.0.0 -t inventory-api:stable .
docker image ls inventory-api
Output:
[+] Building 2.1s (6/6) FINISHED
=> exporting to image
=> naming to docker.io/library/inventory-api:1.0.0
=> naming to docker.io/library/inventory-api:stable
REPOSITORY TAG IMAGE ID CREATED SIZE
inventory-api 1.0.0 2f5b4c6d7e8f 10 seconds ago 133MB
inventory-api stable 2f5b4c6d7e8f 10 seconds ago 133MB
Both tags point to the same local image ID. 1.0.0 is a release tag you can keep forever. stable is a channel tag you can move when a later release has been tested. The Dockerfile uses a specific base image tag, node:20-alpine, so the base runtime is more predictable than node or node:latest.
Example 3: Retag an Image for a Registry
docker tag inventory-api:1.0.0 ghcr.io/acme/inventory-api:1.0.0
docker tag inventory-api:1.0.0 ghcr.io/acme/inventory-api:stable
docker image ls ghcr.io/acme/inventory-api
Output:
REPOSITORY TAG IMAGE ID CREATED SIZE
ghcr.io/acme/inventory-api 1.0.0 2f5b4c6d7e8f 2 minutes ago 133MB
ghcr.io/acme/inventory-api stable 2f5b4c6d7e8f 2 minutes ago 133MB
Retagging prepares names that include the registry host and namespace. It still does not upload anything. After logging in, docker push ghcr.io/acme/inventory-api:1.0.0 would publish that tag and missing blobs. Pushing stable separately would move or create the channel tag in the registry.
Example 4: Pin a Deployment to a Digest
docker pull nginx@sha256:3f6fb2fb8c2c5e2a1f0c7f1bb4d38f0d5ddc5c4b0d9e7a3d6c1a2b3c4d5e6f70
Output:
docker.io/library/nginx@sha256:3f6fb2fb8c2c5e2a1f0c7f1bb4d38f0d5ddc5c4b0d9e7a3d6c1a2b3c4d5e6f70: Pulling from library/nginx
Status: Image is up to date for nginx@sha256:3f6fb2fb8c2c5e2a1f0c7f1bb4d38f0d5ddc5c4b0d9e7a3d6c1a2b3c4d5e6f70
Digest pinning is strongest when the digest comes from your own tested build output or trusted registry metadata. It makes rollback and incident investigation easier because every node asks for the same content. The tradeoff is update discipline: pinned digests do not automatically receive patched base images.
How It Works Step by Step
- The Docker CLI parses the image reference. If there is no tag, it assumes
latest. - For a pull, the daemon contacts the registry and resolves the tag to a manifest or image index.
- If the tag points to a multi-platform index, Docker selects the manifest matching the requested or default platform.
- The selected manifest lists the image config object and layer digests. Docker downloads only missing blobs and verifies them by content digest.
- The local image store records the tag, image ID, platform metadata, and layer chain.
- For
docker tag, Docker adds a new local name pointing at existing image content. No layer is rebuilt or copied. - For
docker push, Docker uploads missing blobs, uploads a manifest, and then sets the remote tag to point at that manifest. - When
docker runstarts a container, Docker mounts the image layers read-only, adds a thin writable container layer, and starts the configured process.
Versioning sits on top of these mechanics. A semantic version tag such as 1.4.2 communicates release meaning to humans, but Docker does not enforce semantic versioning. Docker only stores references. Your release process decides whether 1.4.2 is immutable, whether 1.4 moves to the newest patch in that minor line, and whether stable follows the newest approved production release.
Common Mistakes
Using latest in Production
docker run -d --name web nginx
This means nginx:latest. A later pull may fetch different content, so two servers can run different builds even though they used the same command. Use a tested tag or digest:
docker run -d --name web nginx:1.27-alpine
Reusing a Release Tag for Different Builds
docker build -t inventory-api:1.0.0 .
docker push inventory-api:1.0.0
This is fine the first time. It becomes dangerous if you rebuild changed source and push 1.0.0 again. Anyone who already pulled the old 1.0.0 may keep using it, while new machines get different content. Treat release tags as immutable and publish a new tag such as 1.0.1 for changed code.
Assuming Retagging Creates a New Image
docker tag inventory-api:1.0.0 inventory-api:backup
This adds another label to the same image ID. It is useful, but it is not a backup and it does not protect you from deleting the content if all tags and containers referencing it are removed. Real backups belong in registries, artifact storage, and source-controlled build inputs.
Baking Secrets into Versioned Images
FROM node:20-alpine
ENV API_TOKEN=changeme
CMD ["node", "server.js"]
Even if you later remove the variable, the old value can remain in image history or layers. Secrets should be provided at runtime by your orchestrator, Docker secrets, mounted files, or environment variables managed outside the image build.
Best Practices
- Use explicit image tags everywhere: Dockerfiles, Compose files, CI scripts, and deployment manifests.
- Treat release tags such as
1.0.0as immutable once pushed. - Use channel tags such as
stable,staging, orprodonly when you intentionally want a moving pointer. - Publish a unique tag for every build, often a semantic version, build number, or Git commit SHA.
- Record the digest deployed to each environment so rollback and audits are exact.
- Pin base image tags in Dockerfiles, for example
node:20-alpine, and rebuild regularly for security updates. - Use digests for maximum reproducibility, but pair them with an update process so fixes are not missed.
- Never store secrets in image layers with
ENV, copied files, or build-time scripts. - Keep tags meaningful.
payments-api:2026-08-03.4is easier to reason about thantest2.
Practice Exercises
- Pull
redis:7.2-alpineand print one repo digest withdocker image inspect. Expected end state: you can explain the difference between the tag you typed and the digest Docker resolved. - Build a small image with two tags:
notes-api:0.1.0andnotes-api:dev. Hint: pass-ttwice to onedocker buildcommand. - Design a tagging scheme for a service deployed to development, staging, and production. Include one immutable tag and one moving channel tag, and decide which one each environment should use.
Summary
- A Docker tag is a human-readable pointer to image content in a repository.
- Tags are mutable unless your registry policy or release process prevents movement.
latestis just the default tag name, not a guarantee of freshness or stability.- Digests identify exact content and are best for strict reproducibility.
docker tagadds another local reference;docker pushpublishes a tag to a registry.- Use immutable release tags, intentional channel tags, recorded digests, and pinned base image tags for predictable deployments.
