graph TD
A[Docker Tagging Strategies] --> B{Choose a Tagging Strategy};
B --> C[Unique / Immutable Tags];
C --> C1[Specific Version: my-app:1.2.3];
C --> C2[Git SHA: my-app:abc1234];
C --> C3[Build ID: my-app:build-42];
C --> C4[Digest: my-app@sha256:d1g3st];
C -- Recommended for Production --> D{Benefits};
D --> D1[Reproducibility];
D --> D2[Traceability];
D --> D3[Security];
B --> E[Rolling / Mutable Tags];
E --> E1[Major/Minor Version: my-app:2.1];
E1 -- Receives Patch Updates --> E2[my-app:2.1.3];
E --> E3[Major Version: my-app:2];
E3 -- Receives Minor & Patch Updates --> E1;
E -- Use with Caution --> F{Risks};
F --> F1[Inconsistent Builds];
F --> F2[Unexpected Updates];
B --> G[<font color=red>The latest Tag</font>];
G -- Avoid in Production --> H{Issues};
H --> H1[<font color=red>Not a 'Latest' Release</font>];
H --> H2[<font color=red>Unpredictable</font>];
H --> H3[<font color=red>Hard to Rollback</font>];Login to docker via command line
docker loginBash

Common tagging strategies
Semantic Versioning (SemVer)
This is a recommended and widely adopted approach for stable releases that uses a three-part system: MAJOR.MINOR.PATCH.
MAJORversion: For incompatible API changes.MINORversion: For new, backward-compatible features.PATCHversion: For backward-compatible bug fixes.- Example:
my-app:2.1.3points to a very specific image version, whilemy-app:2.1would automatically receive patch updates andmy-app:2would receive both minor and patch updates.
Unique/Immutable tags
These tags change with every new image pushed, ensuring that a specific tag always points to the exact same image content.
- Git Commit SHA: Tags the image with the short hash of the Git commit used to build it (e.g.,
my-app:abc1234). This provides strong traceability between source code and image. - Build ID: Uses an incremental ID from a CI/CD pipeline (e.g.,
my-app:build-42) to correlate the image with a specific build job. - Manifest Digest: The most reliable but least human-readable option is to use the unique, content-addressable SHA-256 digest of the image manifest (e.g.,
my-app@sha256:...). This guarantees you are using the exact same image regardless of tag changes.
Rolling/Non-unique tags
These are tags that are designed to be overwritten, or “floated,” to point to the newest stable version within a category.
latest: By default, Docker assigns this tag if none is specified. It is mutable, and its meaning depends entirely on the image publisher. This tag should be avoided in production deployments to prevent unexpected updates and ensure reproducible builds.- Major/Minor versions: A tag like
my-app:2ormy-app:2.1is “stable” for users who want to receive the latest updates within a specific version branch.
Best practices for tagging
- Be specific in production: Use immutable tags like SemVer with the full
MAJOR.MINOR.PATCHor a Git SHA for production deployments. This prevents unforeseen changes if a rolling tag is updated. - Automate your process: Integrate tagging into your CI/CD pipeline to automatically apply tags based on version numbers, Git commits, or build IDs. This ensures consistency and accuracy.
- Incorporate context: Add extra information to your tags to indicate the base image, environment, or architecture (e.g.,
my-app:1.2.3-alpine,my-app:1.2.3-prod). - Use multiple tags for flexibility: Assign multiple tags to an image. For instance, a single production image could have the tags
my-app:1.2.3,my-app:1.2, andmy-app:latest.
docker tag command syntax
To add a new tag to an existing image, use the following command:
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]BashExample:
To create a latest tag for a production build:
docker tag my-app:1.0.0 my-app:latestBashTo tag a Docker image with both a specific version and the latest tag, and then push it to Docker Hub, follow these steps:
- Build your Docker image (if you haven’t already):
docker build -t your_username/your_image_name:specific_version .BashReplace your_username, your_image_name, and specific_version with your actual details. The . indicates the current directory as the build context.
- Tag the image with the
latesttag:
docker tag your_username/your_image_name:specific_version your_username/your_image_name:latestBashThis command creates an additional tag (latest) for the same image ID as specific_version. Log in to Docker Hub.
docker loginBashEnter your Docker Hub username and password when prompted. Push both tags to Docker Hub.
docker push your_username/your_image_name:specific_version
docker push your_username/your_image_name:latestBashAlternatively, to push all tags associated with an image, you can use the --all-tags flag:
docker push --all-tags your_username/your_image_nameBashAfter these steps, your Docker image will be available on Docker Hub with both the specific_version tag and the latest tag. When a new version is released, you can repeat the process, tagging the new image with its new specific version and the latest tag, effectively updating the latest tag to point to the newest version.
working example
Create out sample FastAPI endpoint main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"message": "Hello World"}PythonCreate requirements.txt file with require python packages
fastapi
uvicornININow we will create our Docker file, typically named Dockerfile
But in our case, we will be creating two versions, i.e., dev and prod
which means “development” Docker image and a “production” Docker image
First Development Docker file creation Dockerfile.dev
# Use the official Python 3.11 Alpine base image.
# This keeps the image size very small.
FROM python:3.13-alpine
# Install dumb-init using the Alpine Package Manager.
RUN apk add --no-cache dumb-init
# Set the working directory inside the container.
WORKDIR /app
# Copy the requirements file and install dependencies first.
# This improves build speed by leveraging Docker's caching.
COPY requirements.txt main.py .
RUN pip install --no-cache-dir -r requirements.txt --break-system-packages
# Expose the port that Uvicorn will run on.
EXPOSE 8000
# Define the command to run your application with Uvicorn.
# The `host="0.0.0.0"` setting is essential for exposing the port
# to your local machine or other network services.
ENTRYPOINT ["dumb-init", "--"]
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]DockerfileNext we will create production Docker file named Dockerfile.prod
# syntax=docker/dockerfile:1.4
#
# ====================================================================
# STAGE 1: Build a virtual environment with production dependencies
# ====================================================================
FROM python:3.13-alpine as builder
# Set the working directory inside the container
WORKDIR /app
# Install build dependencies and create a dedicated non-root user
RUN apk add --no-cache dumb-init \
&& adduser --disabled-password --no-create-home appuser
# Copy requirements file and install dependencies into a virtual environment
COPY requirements.txt .
RUN python -m venv /venv && /venv/bin/pip install --no-cache-dir -r requirements.txt --break-system-packages
# ====================================================================
# STAGE 2: Create the minimal production image
# ====================================================================
FROM python:3.13-alpine
#FROM alpine
# Set the working directory inside the container
WORKDIR /app
# Add a dedicated non-root user for running the application
RUN adduser --disabled-password --no-create-home appuser
# Copy the virtual environment from the builder stage
COPY --from=builder --chown=appuser:appuser /venv /venv
# Copy the application source code from the host
COPY main.py .
# Copy dumb-init executable
COPY --from=builder --chown=appuser:appuser /usr/bin/dumb-init /usr/bin/dumb-init
# Use the non-root user for security
USER appuser
# Expose the application port
EXPOSE 8000
# Use dumb-init as the entrypoint for proper signal handling
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
# Set the command to run the Uvicorn server via the virtual environment
CMD ["/venv/bin/uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]DockerfileLets build our first docker development image
docker build -t moreskylab/todo-api:v1.0.0 -f Dockerfile.dev .Bash
now add latest tag
docker tag moreskylab/todo-api:v1.0.0 moreskylab/todo-api:latestBash
Push image to docker hub
docker push moreskylab/todo-api:v1.0.0
docker push moreskylab/todo-api:latestBashOR
docker push --all-tags moreskylab/todo-apiBash

Discover more from Altgr Blog
Subscribe to get the latest posts sent to your email.
