{"id":10390,"date":"2025-06-16T15:14:50","date_gmt":"2025-06-16T13:14:50","guid":{"rendered":"https:\/\/via-internet.de\/blog\/?p=10390"},"modified":"2025-06-16T15:15:48","modified_gmt":"2025-06-16T13:15:48","slug":"django-docker-builds-optimieren-mit-uv","status":"publish","type":"post","link":"https:\/\/via-internet.de\/blog\/2025\/06\/16\/django-docker-builds-optimieren-mit-uv\/","title":{"rendered":"Django-Docker-Builds optimieren mit uv"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\"><\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Wer in Docker-Containern Python-Abh\u00e4ngigkeiten mit <code>pip<\/code> verwaltet, sollte einen Blick auf <code>uv<\/code> von <a href=\"https:\/\/github.com\/astral-sh\/uv\">Astral<\/a> werfen. Dieses Tool bietet deutlich schnellere Installationen und robustere Verwaltung der Abh\u00e4ngigkeiten. Besonders f\u00fcr Django-Projekte in der CI oder Produktion kann das Build-Tempo sp\u00fcrbar verbessert werden. Auch die Pflege und das Aktualisieren der Abh\u00e4ngigkeiten werden einfacher.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><code>uv<\/code> im Dockerfile einbinden<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Die empfohlene Methode nutzt ein Multi-Stage-Build, um das aktuelle <code>uv<\/code>-Binary direkt ins Image zu kopieren:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">COPY --from=ghcr.io\/astral-sh\/uv:latest \/uv \/usr\/local\/bin\/uv<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So spart man sich die Installation via <code>pip<\/code> oder Paketmanager und bleibt immer aktuell.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Wichtige Umgebungsvariablen<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Im Dockerfile werden zentrale Umgebungsvariablen gesetzt:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ENV UV_PROJECT_ENVIRONMENT=\/venv \\\n    UV_NO_MANAGED_PYTHON=1 \\\n    UV_PYTHON_DOWNLOADS=never \\\n    VIRTUAL_ENV=\/venv<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>UV_NO_MANAGED_PYTHON=1<\/code>: <code>uv<\/code> verwaltet Python nicht selbst.<\/li>\n\n\n\n<li><code>UV_PYTHON_DOWNLOADS=never<\/code>: Downloads werden komplett deaktiviert.<\/li>\n\n\n\n<li><code>UV_PROJECT_ENVIRONMENT=\/venv<\/code>: Virtuelle Umgebung wird unter <code>\/venv<\/code> verwaltet.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Performance-Verbesserungen<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ENV UV_COMPILE_BYTECODE=1 \\\n    UV_LINK_MODE=copy \\\n    UV_CACHE_DIR=\/app\/.cache\/uv<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Bytecode wird beim Installieren erzeugt (<code>.pyc<\/code>), schnellere Startzeit.<\/li>\n\n\n\n<li>Hardlink-Probleme werden durch Kopieren vermieden.<\/li>\n\n\n\n<li>Caching via Docker-Mounts beschleunigt Builds enorm.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Sicherheit und Reproduzierbarkeit<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ENV UV_FROZEN=1 \\\n    UV_REQUIRE_HASHES=1 \\\n    UV_VERIFY_HASHES=1<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Nur exakt definierte Versionen werden installiert.<\/li>\n\n\n\n<li>Jeder Download wird \u00fcber einen kryptografischen Hash abgesichert.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Abh\u00e4ngigkeiten installieren<\/h3>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">RUN uv venv $VIRTUAL_ENV\n\nRUN --mount=type=cache,target=\/app\/.cache\/uv \\\n    --mount=type=bind,source=uv.lock,target=uv.lock \\\n    --mount=type=bind,source=pyproject.toml,target=pyproject.toml \\\n    uv sync --no-install-project --no-editable<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Befehle installieren exakt die in der <code>uv.lock<\/code> Datei festgelegten Pakete.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Dev- und Test-Abh\u00e4ngigkeiten verwalten<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Mit <code>dependency-groups<\/code> lassen sich verschiedene Umgebungen differenzieren:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">[project]\ndependencies = [\"django~=5.2\"]<\/pre>\n\n\n<p>[dependency-groups]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">dev = [&#8220;django-debug-toolbar&#8221;] test = [&#8220;pytest&#8221;]<\/p>\n\n\n<p>[tool.uv]<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">default-groups = []<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Mit Build-Argumenten lassen sich Gruppen gezielt installieren:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ARG BUILD_GROUPS=\"\"\n\nRUN --mount=type=cache,target=\/app\/.cache\/uv \\\n    --mount=type=bind,source=uv.lock,target=uv.lock \\\n    --mount=type=bind,source=pyproject.toml,target=pyproject.toml \\\n    uv venv $VIRTUAL_ENV &amp;&amp; \\\n    uv sync --no-install-project --no-editable $BUILD_GROUPS<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Beispielaufrufe:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Nur Produktivabh\u00e4ngigkeiten\n$ docker build .\n\n# Mit Entwicklungswerkzeugen\n$ docker build --build-arg BUILD_GROUPS=\"--group dev\"\n\n# Mit Testumgebung\n$ docker build --build-arg BUILD_GROUPS=\"--group test\"<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Migration von <code>pip<\/code><\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Bestehende Projekte lassen sich \u00fcberf\u00fchren mit:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">uv add --requirements requirements.txt\nuv add --group dev --requirements requirements-dev.txt\nuv lock<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Pflege und Updates<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Veraltete Pakete pr\u00fcfen:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">uv pip list --outdated<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Lock-Datei aktualisieren:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">uv lock --upgrade<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Damit bleibt die Abh\u00e4ngigkeitsverwaltung reproduzierbar, sicher und wartbar.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\">Diese Methode spart Zeit, Bandbreite und sorgt f\u00fcr eine klar strukturierte und abgesicherte Docker-Infrastruktur f\u00fcr Python-Projekte wie Django. Wer auf schnelle CI\/CD-Pipelines, reproduzierbare Builds und saubere Produktionsimages setzt, findet in <code>uv<\/code> eine moderne und effiziente L\u00f6sung.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Hier das finale Dockerfile<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">### BUILD IMAGE ###\n\nFROM python:3.13-slim AS builder\n\nENV DJANGO_SETTINGS_MODULE=myproj.settings \\\n    PATH=\"\/venv\/bin:$PATH\" \\\n    PYTHONDONTWRITEBYTECODE=1 \\\n    PYTHONUNBUFFERED=1 \\\n    UV_CACHE_DIR=\/root\/.cache\/uv \\\n    UV_COMPILE_BYTECODE=1 \\\n    UV_FROZEN=1 \\\n    UV_LINK_MODE=copy \\\n    UV_NO_MANAGED_PYTHON=1 \\\n    UV_PROJECT_ENVIRONMENT=\/venv \\\n    UV_PYTHON_DOWNLOADS=never \\\n    UV_REQUIRE_HASHES=1 \\\n    UV_VERIFY_HASHES=1 \\\n    VIRTUAL_ENV=\/venv\n\nCOPY --from=ghcr.io\/astral-sh\/uv:latest \/uv \/usr\/local\/bin\/uv\n\nRUN &lt;&lt;EOT\napt-get update -y &amp;&amp; \\\napt-get install -y --no-install-recommends \\\n    build-essential \\\n    # other build dependencies here\nEOT\n\nWORKDIR \/app\n\nARG BUILD_GROUPS=\"\"\n\nRUN --mount=type=cache,target=\/app\/.cache\/uv \\\n    --mount=type=bind,source=uv.lock,target=uv.lock \\\n    --mount=type=bind,source=pyproject.toml,target=pyproject.toml \\\n    uv venv $VIRTUAL_ENV &amp;&amp; \\\n    uv sync --no-install-project --no-editable $BUILD_GROUPS\n\n# Copy what's needed to run collectstatic.\nCOPY myproj \/app\/myproj\nCOPY media \/app\/media\nCOPY manage.py \/app\/\n\nRUN DEBUG=False .\/manage.py collectstatic --noinput\n\n### FINAL IMAGE ###\n\nFROM python:3.13-slim\n\nARG PORT=8000\nENV DJANGO_SETTINGS_MODULE=myproj.settings \\\n    PATH=\"\/venv\/bin:$PATH\" \\\n    PORT=${PORT} \\\n    PYTHONDONTWRITEBYTECODE=1 \\\n    PYTHONUNBUFFERED=1 \\\n    VIRTUAL_ENV=\/venv\n\nEXPOSE ${PORT}\nENTRYPOINT [\"\/bin\/bash\", \"\/app\/bin\/run\"]\nCMD [\"prod\"]\n\nWORKDIR \/app\n\nRUN &lt;&lt;EOT\napt-get clean -y &amp;&amp; \\\napt-get update -y &amp;&amp; \\\napt-get install -y --no-install-recommends \\\n\t# OS dependencies, e.g. bash, db clients, etc.\n    bash &amp;&amp; \\\napt-get autoremove -y &amp;&amp; \\\napt-get clean -y &amp;&amp; \\\nrm -rf \/var\/lib\/apt\/lists\/* \/tmp\/* \/var\/tmp\/*\nEOT\n\n# Copy selectively from builder to optimize final image.\n# --link enables better layer caching when base image changes\nCOPY --link --from=builder \/venv \/venv\nCOPY --link --from=builder \/app\/myproj \/app\/myproj\nCOPY --link --from=builder \/app\/static \/app\/static\nCOPY --link --from=builder \/app\/manage.py \/app\/manage.py<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Wer in Docker-Containern Python-Abh\u00e4ngigkeiten mit pip verwaltet, sollte einen Blick auf uv von Astral werfen. Dieses Tool bietet deutlich schnellere Installationen und robustere Verwaltung der Abh\u00e4ngigkeiten. Besonders f\u00fcr Django-Projekte in der CI oder Produktion kann das Build-Tempo sp\u00fcrbar verbessert werden. Auch die Pflege und das Aktualisieren der Abh\u00e4ngigkeiten werden einfacher. uv im Dockerfile einbinden Die empfohlene Methode nutzt ein Multi-Stage-Build, um das aktuelle uv-Binary direkt ins Image zu kopieren: So spart man sich die Installation via pip oder Paketmanager und bleibt immer aktuell. Wichtige Umgebungsvariablen Im Dockerfile werden zentrale Umgebungsvariablen gesetzt: Performance-Verbesserungen Sicherheit und Reproduzierbarkeit Abh\u00e4ngigkeiten installieren Diese Befehle installieren exakt [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-10390","post","type-post","status-publish","format-standard","hentry","category-allgemein"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/posts\/10390","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/comments?post=10390"}],"version-history":[{"count":3,"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/posts\/10390\/revisions"}],"predecessor-version":[{"id":10394,"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/posts\/10390\/revisions\/10394"}],"wp:attachment":[{"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/media?parent=10390"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/categories?post=10390"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/via-internet.de\/blog\/wp-json\/wp\/v2\/tags?post=10390"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}