content/manuals/dhi/migration/examples/python.md
This example shows how to migrate a Python application to Docker Hardened Images.
The following examples show Dockerfiles before and after migration to Docker Hardened Images. Each example includes five variations:
[!NOTE]
Multi-stage builds are recommended for most use cases. Single-stage builds are supported for simplicity, but come with tradeoffs in size and security.
You must authenticate to
dhi.iobefore you can pull Docker Hardened Images. Rundocker login dhi.ioto authenticate.
{{< tabs >}} {{< tab name="Before (Ubuntu)" >}}
#syntax=docker/dockerfile:1
FROM ubuntu/python:3.13-24.04_stable AS builder
ENV LANG=C.UTF-8
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PATH="/app/venv/bin:$PATH"
WORKDIR /app
RUN python -m venv /app/venv
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
FROM ubuntu/python:3.13-24.04_stable
WORKDIR /app
ENV PYTHONUNBUFFERED=1
ENV PATH="/app/venv/bin:$PATH"
COPY app.py ./
COPY --from=builder /app/venv /app/venv
ENTRYPOINT [ "python", "/app/app.py" ]
{{< /tab >}} {{< tab name="Before (Wolfi)" >}}
#syntax=docker/dockerfile:1
FROM cgr.dev/chainguard/python:latest-dev AS builder
ENV LANG=C.UTF-8
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PATH="/app/venv/bin:$PATH"
WORKDIR /app
RUN python -m venv /app/venv
COPY requirements.txt .
# Install any additional packages if needed using apk
# RUN apk add --no-cache gcc musl-dev
RUN pip install --no-cache-dir -r requirements.txt
FROM cgr.dev/chainguard/python:latest
WORKDIR /app
ENV PYTHONUNBUFFERED=1
ENV PATH="/app/venv/bin:$PATH"
COPY app.py ./
COPY --from=builder /app/venv /app/venv
ENTRYPOINT [ "python", "/app/app.py" ]
{{< /tab >}} {{< tab name="Before (DOI)" >}}
#syntax=docker/dockerfile:1
FROM python:latest AS builder
ENV LANG=C.UTF-8
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PATH="/app/venv/bin:$PATH"
WORKDIR /app
RUN python -m venv /app/venv
COPY requirements.txt .
# Install any additional packages if needed using apt
# RUN apt-get update && apt-get install -y gcc && rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir -r requirements.txt
FROM python:latest
WORKDIR /app
ENV PYTHONUNBUFFERED=1
ENV PATH="/app/venv/bin:$PATH"
COPY app.py ./
COPY --from=builder /app/venv /app/venv
ENTRYPOINT [ "python", "/app/app.py" ]
{{< /tab >}} {{< tab name="After (multi-stage)" >}}
#syntax=docker/dockerfile:1
# === Build stage: Install dependencies and create virtual environment ===
FROM dhi.io/python:3.13-alpine3.21-dev AS builder
ENV LANG=C.UTF-8
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PATH="/app/venv/bin:$PATH"
WORKDIR /app
RUN python -m venv /app/venv
COPY requirements.txt .
# Install any additional packages if needed using apk
# RUN apk add --no-cache gcc musl-dev
RUN pip install --no-cache-dir -r requirements.txt
# === Final stage: Create minimal runtime image ===
FROM dhi.io/python:3.13-alpine3.21
WORKDIR /app
ENV PYTHONUNBUFFERED=1
ENV PATH="/app/venv/bin:$PATH"
COPY app.py ./
COPY --from=builder /app/venv /app/venv
ENTRYPOINT [ "python", "/app/app.py" ]
{{< /tab >}} {{< tab name="After (single-stage)" >}}
#syntax=docker/dockerfile:1
FROM dhi.io/python:3.13-alpine3.21-dev
ENV LANG=C.UTF-8
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
ENV PATH="/app/venv/bin:$PATH"
WORKDIR /app
RUN python -m venv /app/venv
COPY requirements.txt .
# Install any additional packages if needed using apk
# RUN apk add --no-cache gcc musl-dev
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py ./
ENTRYPOINT [ "python", "/app/app.py" ]
{{< /tab >}} {{< /tabs >}}