Django-Docker-Builds optimieren mit uv

Inhaltsverzeichnis

Wer in Docker-Containern Python-Abhängigkeiten mit pip verwaltet, sollte einen Blick auf uv von Astral werfen. Dieses Tool bietet deutlich schnellere Installationen und robustere Verwaltung der Abhängigkeiten. Besonders für Django-Projekte in der CI oder Produktion kann das Build-Tempo spürbar verbessert werden. Auch die Pflege und das Aktualisieren der Abhängigkeiten werden einfacher.

uv im Dockerfile einbinden

Die empfohlene Methode nutzt ein Multi-Stage-Build, um das aktuelle uv-Binary direkt ins Image zu kopieren:

COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

So spart man sich die Installation via pip oder Paketmanager und bleibt immer aktuell.

Wichtige Umgebungsvariablen

Im Dockerfile werden zentrale Umgebungsvariablen gesetzt:

ENV UV_PROJECT_ENVIRONMENT=/venv \
    UV_NO_MANAGED_PYTHON=1 \
    UV_PYTHON_DOWNLOADS=never \
    VIRTUAL_ENV=/venv
  • UV_NO_MANAGED_PYTHON=1: uv verwaltet Python nicht selbst.
  • UV_PYTHON_DOWNLOADS=never: Downloads werden komplett deaktiviert.
  • UV_PROJECT_ENVIRONMENT=/venv: Virtuelle Umgebung wird unter /venv verwaltet.

Performance-Verbesserungen

ENV UV_COMPILE_BYTECODE=1 \
    UV_LINK_MODE=copy \
    UV_CACHE_DIR=/app/.cache/uv
  • Bytecode wird beim Installieren erzeugt (.pyc), schnellere Startzeit.
  • Hardlink-Probleme werden durch Kopieren vermieden.
  • Caching via Docker-Mounts beschleunigt Builds enorm.

Sicherheit und Reproduzierbarkeit

ENV UV_FROZEN=1 \
    UV_REQUIRE_HASHES=1 \
    UV_VERIFY_HASHES=1
  • Nur exakt definierte Versionen werden installiert.
  • Jeder Download wird über einen kryptografischen Hash abgesichert.

Abhängigkeiten installieren

RUN uv venv $VIRTUAL_ENV

RUN --mount=type=cache,target=/app/.cache/uv \
    --mount=type=bind,source=uv.lock,target=uv.lock \
    --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
    uv sync --no-install-project --no-editable

Diese Befehle installieren exakt die in der uv.lock Datei festgelegten Pakete.

Dev- und Test-Abhängigkeiten verwalten

Mit dependency-groups lassen sich verschiedene Umgebungen differenzieren:

[project]
dependencies = ["django~=5.2"]

[dependency-groups]

dev = [“django-debug-toolbar”] test = [“pytest”]

[tool.uv]

default-groups = []

Mit Build-Argumenten lassen sich Gruppen gezielt installieren:

ARG BUILD_GROUPS=""

RUN --mount=type=cache,target=/app/.cache/uv \
    --mount=type=bind,source=uv.lock,target=uv.lock \
    --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
    uv venv $VIRTUAL_ENV && \
    uv sync --no-install-project --no-editable $BUILD_GROUPS

Beispielaufrufe:

# Nur Produktivabhängigkeiten
$ docker build .

# Mit Entwicklungswerkzeugen
$ docker build --build-arg BUILD_GROUPS="--group dev"

# Mit Testumgebung
$ docker build --build-arg BUILD_GROUPS="--group test"

Migration von pip

Bestehende Projekte lassen sich überführen mit:

uv add --requirements requirements.txt
uv add --group dev --requirements requirements-dev.txt
uv lock

Pflege und Updates

Veraltete Pakete prüfen:

uv pip list --outdated

Lock-Datei aktualisieren:

uv lock --upgrade

Damit bleibt die Abhängigkeitsverwaltung reproduzierbar, sicher und wartbar.


Diese Methode spart Zeit, Bandbreite und sorgt für eine klar strukturierte und abgesicherte Docker-Infrastruktur für Python-Projekte wie Django. Wer auf schnelle CI/CD-Pipelines, reproduzierbare Builds und saubere Produktionsimages setzt, findet in uv eine moderne und effiziente Lösung.

Hier das finale Dockerfile

### BUILD IMAGE ###

FROM python:3.13-slim AS builder

ENV DJANGO_SETTINGS_MODULE=myproj.settings \
    PATH="/venv/bin:$PATH" \
    PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    UV_CACHE_DIR=/root/.cache/uv \
    UV_COMPILE_BYTECODE=1 \
    UV_FROZEN=1 \
    UV_LINK_MODE=copy \
    UV_NO_MANAGED_PYTHON=1 \
    UV_PROJECT_ENVIRONMENT=/venv \
    UV_PYTHON_DOWNLOADS=never \
    UV_REQUIRE_HASHES=1 \
    UV_VERIFY_HASHES=1 \
    VIRTUAL_ENV=/venv

COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv

RUN <<EOT
apt-get update -y && \
apt-get install -y --no-install-recommends \
    build-essential \
    # other build dependencies here
EOT

WORKDIR /app

ARG BUILD_GROUPS=""

RUN --mount=type=cache,target=/app/.cache/uv \
    --mount=type=bind,source=uv.lock,target=uv.lock \
    --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
    uv venv $VIRTUAL_ENV && \
    uv sync --no-install-project --no-editable $BUILD_GROUPS

# Copy what's needed to run collectstatic.
COPY myproj /app/myproj
COPY media /app/media
COPY manage.py /app/

RUN DEBUG=False ./manage.py collectstatic --noinput

### FINAL IMAGE ###

FROM python:3.13-slim

ARG PORT=8000
ENV DJANGO_SETTINGS_MODULE=myproj.settings \
    PATH="/venv/bin:$PATH" \
    PORT=${PORT} \
    PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1 \
    VIRTUAL_ENV=/venv

EXPOSE ${PORT}
ENTRYPOINT ["/bin/bash", "/app/bin/run"]
CMD ["prod"]

WORKDIR /app

RUN <<EOT
apt-get clean -y && \
apt-get update -y && \
apt-get install -y --no-install-recommends \
	# OS dependencies, e.g. bash, db clients, etc.
    bash && \
apt-get autoremove -y && \
apt-get clean -y && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
EOT

# Copy selectively from builder to optimize final image.
# --link enables better layer caching when base image changes
COPY --link --from=builder /venv /venv
COPY --link --from=builder /app/myproj /app/myproj
COPY --link --from=builder /app/static /app/static
COPY --link --from=builder /app/manage.py /app/manage.py