Files
docker-docs/content/manuals/dhi/migration/examples/python.md
Craig Osterhout d1c77771d5 dhi: add ubuntu migration (#23963)
<!--Delete sections as needed -->

## Description

Added general Ubuntu to Debian DHI migration.

-
https://deploy-preview-23963--docsdocker.netlify.app/dhi/migration/migrate-from-ubuntu/
-
https://deploy-preview-23963--docsdocker.netlify.app/dhi/migration/examples/go/
-
https://deploy-preview-23963--docsdocker.netlify.app/dhi/migration/examples/python/
-
https://deploy-preview-23963--docsdocker.netlify.app/dhi/migration/examples/node/

The tabbed comparison in the language examples is getting a bit wonky,
although still correct. A lot of tabs, and it randomly picks
alpine/debian as the final dhi image, whereas wolfi is probably going to
alpine and ubuntu is probably going to debian. Will look at this in a
followup.

## Related issues or tickets

ENGDOCS-3142

## Reviews

<!-- Notes for reviewers here -->
<!-- List applicable reviews (optionally @tag reviewers) -->

- [ ] Technical review
- [ ] Editorial review
- [ ] Product review

---------

Signed-off-by: Craig Osterhout <craig.osterhout@docker.com>
2026-01-13 11:41:39 -08:00

4.3 KiB

title, description, weight, keywords
title description weight keywords
Python Migrate a Python application to Docker Hardened Images 20 python, migration, dhi

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:

  • Before (Ubuntu): A sample Dockerfile using Ubuntu-based images, before migrating to DHI
  • Before (Wolfi): A sample Dockerfile using Wolfi distribution images, before migrating to DHI
  • Before (DOI): A sample Dockerfile using Docker Official Images, before migrating to DHI
  • After (multi-stage): A sample Dockerfile after migrating to DHI with multi-stage builds (recommended for minimal, secure images)
  • After (single-stage): A sample Dockerfile after migrating to DHI with single-stage builds (simpler but results in a larger image with a broader attack surface)

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.io before you can pull Docker Hardened Images. Run docker login dhi.io to 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 >}}