Fertige Images vom Docker Hub reichen oft nicht – sobald du eigenen Code oder eine spezielle Konfiguration mitliefern willst, brauchst du ein eigenes Image. Dafür gibt es das Dockerfile. In dieser Anleitung lernst du die Grundlagen: die wichtigsten Anweisungen, wie du ein eigenes Docker-Image baust und startest – und wie du es schlank hältst.
Ein eigenes Docker-Image zu bauen ist der Moment, in dem du von einem reinen Docker-Nutzer zum Entwickler eigener Container wirst. Statt fertige Container-Images vom Docker Hub zu übernehmen, beschreibst du im Dockerfile reproduzierbar, wie deine Anwendung samt Abhängigkeiten, Konfiguration und Startbefehl aussehen soll. Das Ergebnis läuft auf deinem Laptop genauso wie auf dem Server – ein großer Vorteil gegenüber manueller Installation. In diesem Guide gehen wir über die reinen Grundlagen hinaus und schauen uns auch Build-Kontext, Layer-Caching und Multi-Stage-Builds an, damit deine Images klein, sicher und schnell gebaut sind.
- Was ist ein Dockerfile?
- Die wichtigsten Anweisungen
- Ein einfaches Dockerfile
- Image bauen und starten
- Tipp: Images schlank halten
- Build-Kontext und .dockerignore verstehen
- Layer-Caching clever nutzen
- Multi-Stage-Builds für schlanke Images
- Häufige Fragen
- Was ist der Unterschied zwischen CMD und ENTRYPOINT?
- Warum ist mein Docker-Image so groß?
- Wie aktualisiere ich ein Image nach Codeänderungen?
- Fazit
Was ist ein Dockerfile?
Ein Dockerfile ist eine Textdatei mit einer Bauanleitung. Docker liest sie Zeile für Zeile und erzeugt daraus ein Image – eine unveränderliche Vorlage, aus der du beliebig viele Container startest. Wenn dir die Begriffe noch fremd sind, hilft Was ist Docker?.
Die wichtigsten Anweisungen
| Anweisung | Bedeutung |
|---|---|
FROM | Basis-Image, auf dem aufgebaut wird |
WORKDIR | Arbeitsverzeichnis im Image |
COPY | Dateien vom Host ins Image kopieren |
RUN | Befehl beim Bauen ausführen (z. B. Pakete installieren) |
EXPOSE | Dokumentiert den genutzten Port |
CMD | Standardbefehl beim Start des Containers |
Ein einfaches Dockerfile
Das folgende Beispiel baut ein Image für eine kleine Python-Anwendung:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "app.py"]Image bauen und starten
Im Verzeichnis mit dem Dockerfile baust du das Image mit docker build. Der Parameter -t vergibt einen Namen (Tag):
docker build -t meine-app:1.0 .Anschließend startest du einen Container daraus:
docker run -d -p 8000:8000 --name app meine-app:1.0Tipp: Images schlank halten
- Schlanke Basis wählen: Varianten wie
-slimoderalpinesparen viel Platz. - .dockerignore nutzen: Schließe unnötige Dateien (z. B.
.git) vom Build aus. - Layer zusammenfassen: Fasse zusammengehörige
RUN-Befehle mit&&zusammen.
Große, ungenutzte Build-Reste entfernst du später beim Aufräumen.
Build-Kontext und .dockerignore verstehen
Wenn du docker build -t meine-app:1.0 . ausführst, ist der Punkt am Ende der sogenannte Build-Kontext: das gesamte Verzeichnis, das Docker an den Daemon überträgt. Alles, was dort liegt, kann per COPY ins Image gelangen – aber alles wird auch erst einmal übertragen. Bei großen Projektordnern verlangsamt das den Build spürbar und schleust im schlimmsten Fall Geheimnisse ein.
Die Lösung ist eine .dockerignore-Datei neben dem Dockerfile. Sie funktioniert wie eine .gitignore und hält unnötige oder sensible Dateien aus dem Kontext heraus:
- .git – die komplette Versionshistorie gehört nicht ins Image.
- node_modules oder venv – Abhängigkeiten installierst du im Build ohnehin neu.
- .env und Schlüsseldateien – Secrets haben in einem Image nichts verloren.
- *.log, tmp/, Testdaten – aufblähen ohne Nutzen.
Layer-Caching clever nutzen
Jede Anweisung im Dockerfile erzeugt einen Layer. Docker speichert diese Layer und verwendet sie beim nächsten Build wieder, solange sich Eingaben nicht geändert haben. Genau deshalb steht im Beispiel oben COPY requirements.txt . und der RUN pip install vor dem COPY . . des restlichen Codes: Solange sich deine Abhängigkeiten nicht ändern, bleibt der teure Installationsschritt im Cache und der Build dauert Sekunden statt Minuten.
Als Faustregel gilt: Ordne die Anweisungen von selten geändert (Basis-Image, System-Pakete, Abhängigkeiten) nach häufig geändert (dein Quellcode). Sobald ein Layer neu gebaut werden muss, werden auch alle nachfolgenden Layer verworfen – deshalb kostet eine ungünstige Reihenfolge bei jedem kleinen Codeänderung unnötig Zeit.
Multi-Stage-Builds für schlanke Images
Compiler, Build-Tools und Entwicklungs-Bibliotheken brauchst du zum Bauen, aber nicht zur Laufzeit. Mit einem Multi-Stage-Build nutzt du eine erste Stufe zum Kompilieren und kopierst nur das fertige Artefakt in ein schlankes finales Image. Dazu definierst du mehrere FROM-Anweisungen und kopierst mit COPY --from=builder gezielt Dateien aus der Build-Stufe.
Das Ergebnis ist ein Image, das oft nur einen Bruchteil der ursprünglichen Größe hat – ein Go- oder Node-Build schrumpft so schnell von mehreren Hundert MB auf wenige Dutzend. Kleinere Images bedeuten schnelleres Deployment, weniger Angriffsfläche und geringeren Speicherverbrauch in deiner Docker-Compose-Umgebung.
Bevor du dein Image auf einen Server bringst, lohnt sich ein kurzer Blick auf ein paar bewährte Praktiken, die Sicherheit und Wartbarkeit deutlich verbessern:
- Feste Versionen taggen: Nutze
python:3.12-slimstattlatest, damit Builds reproduzierbar bleiben. - Nicht als root laufen: Lege mit
RUN useraddeinen eigenen Benutzer an und wechsle perUSER-Anweisung dorthin. - Nur nötige Ports öffnen:
EXPOSEdokumentiert klar, worüber dein Dienst erreichbar ist. - Regelmäßig neu bauen: So landen Sicherheitsupdates des Basis-Images automatisch in deinem Container-Image.
Häufige Fragen
Rund um das Bauen eigener Images tauchen immer wieder dieselben Fragen auf. Hier die wichtigsten kurz beantwortet.
Was ist der Unterschied zwischen CMD und ENTRYPOINT?
CMD legt den Standardbefehl fest, den du beim docker run leicht überschreiben kannst. ENTRYPOINT definiert einen festen Startbefehl, an den zusätzliche Argumente angehängt werden. Für einfache Anwendungen reicht CMD; brauchst du ein Image, das sich wie ein einzelnes Kommando verhält, kombinierst du beide.
Warum ist mein Docker-Image so groß?
Meist liegt es am Basis-Image oder an zwischengespeicherten Paketlisten. Wähle eine -slim– oder alpine-Variante, räume im selben RUN-Layer auf (etwa rm -rf /var/lib/apt/lists/*) und prüfe mit docker image ls sowie docker history meine-app:1.0, welche Layer den Platz belegen.
Wie aktualisiere ich ein Image nach Codeänderungen?
Du baust es einfach neu mit docker build -t meine-app:1.1 .. Dank Layer-Caching werden nur die geänderten Schritte neu ausgeführt. Anschließend startest du einen frischen Container aus dem neuen Tag – der alte Container übernimmt Änderungen nicht automatisch.
Fazit
Mit FROM, COPY, RUN und CMD hast du die vier wichtigsten Bausteine eines Dockerfiles beisammen. docker build macht daraus ein Image, aus dem du Container startest. Achte auf eine schlanke Basis, dann bleiben deine Images klein und schnell. Im nächsten Schritt kombinierst du dein Image mit Docker Compose und einem Healthcheck.

