Eigenen Docker Container erstellen: Schritt-für-Schritt Anleitung
In diesem Artikel zeige ich dir, wie du deinen eigenen Docker Container als Base-Image für deine Entwicklungsumgebung erstellen kannst. Egal ob du schon Erfahrung mit Docker hast oder gerade erst einsteigst - ich erkläre dir Schritt für Schritt den Weg vom Dockerfile über Docker Compose bis hin zu Volumes für persistente Daten.
DarkWolfCave.de
Update März 2026: Dieser Artikel wurde grundlegend überarbeitet. Die wichtigsten Änderungen:
- Python Base-Image von
python:3.11(900 MB) aufpython:3.13-slim-bookworm(45 MB) aktualisiert - Alle Python-Pakete auf aktuelle Versionen gebracht (Flask 3.1, requests 2.32, tabulate 0.10)
- Docker Compose als empfohlenen Weg zum Starten hinzugefügt
- Volumes (Bind Mounts und Named Volumes) ausführlich erklärt
- Security Best Practices: Non-Root User,
.dockerignore,--no-cache-dir - Neuer Abschnitt: Aktuelle Versionen selbst prüfen, damit die Anleitung nicht veraltet
Eigenen Docker Container erstellen - Was brauchen wir?
Auf deinem Server oder Raspberry Pi muss der Docker-Dienst installiert sein. Portainer ist hilfreich, aber optional. Solltest du bisher noch kein Docker nutzen, empfehle ich dir erst einmal folgenden Artikel von mir durchzugehen:
Raspberry Pi Docker ohne Probleme installieren
Eigene Entwicklungsumgebung in einem Docker Container
Bevor wir starten und einen eigenen Container (Image) erstellen, mit dem wir dann weiter experimentieren und ihn anpassen können, hier ein kleiner Hinweis zur Begrifflichkeit:
Wenn ich im Folgenden von “dev-env” spreche, meine ich damit “development environment” - also eine Entwicklungsumgebung.
Wozu du letztendlich deinen eigenen Docker Container erstellen willst, ist erst einmal nicht wichtig. Ich möchte dir aufzeigen, wie du einen solchen Base-Container generell erstellen und an deine Bedürfnisse anpassen kannst.
Unsere Ausgangslage: Wir erstellen einen Docker Container als Basis-Image zum Entwickeln von Python-Applikationen. Dieser Container dient als Grundlage, um direkt in einer sauberen, isolierten Umgebung arbeiten zu können.
Das Ganze läuft bei mir auf folgenden Komponenten:
Das komplette Setup für den Raspberry Pi 5 mit NVMe Boot
Mein Raspberry Pi 5 Setting
| Bild | Produkt | Preis | |
|---|---|---|---|
| Produktdaten werden geladen... | |||
Wir nutzen wie immer einen Raspberry Pi und erstellen uns erst einmal eine Ordnerstruktur. In meinem Fall speichere ich alles unter dem angemeldeten User im Verzeichnis tutorials/eigene_docker_container/dev-env.
Verbinde dich auf deinem Raspberry per Terminal und erstelle die Ordner:
mkdir -p ~/tutorials/eigene_docker_container/dev-env
cd ~/tutorials/eigene_docker_container/dev-env
Aktuelle Versionen prüfen - bevor du loslegst
Software-Versionen ändern sich ständig. Bevor du dein Dockerfile schreibst, solltest du kurz prüfen, welche Versionen aktuell stabil sind. So stellst du sicher, dass dein Container von Anfang an auf dem neuesten Stand ist.
Python Base-Image auf Docker Hub prüfen:
# Zeigt dir die verfügbaren Tags für das offizielle Python-Image
docker search python --limit 5
# Oder direkt auf Docker Hub nachschauen:
# https://hub.docker.com/_/python/tags
# Filtere dort nach "slim-bookworm" für schlanke Debian-basierte Images
Zum Zeitpunkt der letzten Überarbeitung (März 2026) ist python:3.13-slim-bookworm eine gute Wahl - schlank (~45 MB), stabil und auf dem aktuellen Debian Bookworm basierend. Das volle Image (python:3.13) wäre über 900 MB groß und enthält viele Pakete, die du im Container nicht brauchst.
Python-Pakete auf PyPI prüfen:
Die aktuellen stabilen Versionen findest du direkt auf pypi.org - einfach nach dem Paketnamen suchen (z.B. pypi.org/project/Flask). Dort steht immer die neueste Version ganz oben.
Alternativ kannst du auch im Terminal prüfen:
pip index versions flask
Projektdateien erstellen
In unserem Projektverzeichnis brauchen wir drei Dateien. Beachte dabei, dass Dockerfile mit großem D geschrieben wird:
touch Dockerfile requirements.txt .dockerignore
requirements.txt
Hier tragen wir unsere Python-Abhängigkeiten mit festen Versionsnummern ein. Feste Versionen sind wichtig, damit dein Build jederzeit reproduzierbar ist - also heute und in drei Monaten das gleiche Ergebnis liefert.
Bearbeite die requirements.txt und füge folgendes hinzu:
Flask==3.1.3
requests==2.32.5
tabulate==0.10.0
Solltest du im Laufe der Entwicklung weitere Bibliotheken benötigen, trägst du sie hier ein und baust das Image einfach neu.
.dockerignore
Die .dockerignore-Datei funktioniert wie eine .gitignore - sie verhindert, dass unnötige oder sensible Dateien ins Image kopiert werden:
__pycache__/
*.py[cod]
.venv/
venv/
.git/
.gitignore
.env
.env.*
*.egg-info/
dist/
build/
Dockerfile
Jetzt das Herzstück - unser Dockerfile. Es beschreibt Schritt für Schritt, wie Docker das Image zusammenbauen soll.
Für mehr Details zu den einzelnen Befehlen schaue in die offizielle Dockerfile-Referenz.
# Schlankes Python-Image als Basis (~45 MB statt ~900 MB)
FROM python:3.13-slim-bookworm
# Arbeitsverzeichnis im Container festlegen
WORKDIR /app
# Zuerst nur requirements.txt kopieren (Docker-Cache nutzen!)
# Solange sich requirements.txt nicht ändert, wird dieser Layer gecacht
COPY requirements.txt requirements.txt
# Python-Abhängigkeiten installieren
# --no-cache-dir spart Speicher, da pip keinen Download-Cache anlegt
RUN pip install --no-cache-dir -r requirements.txt
# Entwicklertools installieren
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
curl \
git \
&& rm -rf /var/lib/apt/lists/*
# Eigenen App-Code in den Container kopieren
COPY . .
# Nicht-Root User erstellen (Security Best Practice)
RUN groupadd -r -g 1001 appgroup \
&& useradd -r -u 1001 -g appgroup -d /app -s /sbin/nologin appuser \
&& chown -R appuser:appgroup /app
# Als normaler User statt root ausführen
USER appuser
# Standard-Befehl beim Containerstart
CMD ["python"]
Warum die Reihenfolge wichtig ist: Docker arbeitet mit Layer-Caching. Dateien die sich selten ändern (requirements.txt) sollten vor Dateien kopiert werden, die sich oft ändern (dein Code). So muss Docker bei einer Code-Änderung nicht jedes Mal alle Abhängigkeiten neu installieren.
Warum ein eigener User? Container laufen standardmäßig als root. Das ist ein Sicherheitsrisiko - wenn jemand aus dem Container ausbricht, hat er root-Rechte auf dem Host. Mit einem eigenen User ohne root-Rechte begrenzt du den möglichen Schaden.
Hinweis: Als appuser kannst du im Container keine Systempakete mit apt-get nachinstallieren. Wenn du im Container experimentieren und Pakete testen willst, verbinde dich einfach als root:
docker compose exec -u root dev-env bash
Sobald du weißt, welche Pakete du brauchst, trägst du sie ins Dockerfile ein und baust das Image neu. So bleibt dein Image sauber dokumentiert.
Image bauen und Container starten
Image bauen
docker build -t dev-env:v1 .
Der Punkt am Ende ist wichtig - er sagt Docker, dass das Dockerfile im aktuellen Verzeichnis liegt.
Container starten (Terminal)
docker run --name dev-env -it dev-env:v1 bash
Der Befehl startet den Container und verbindet dich direkt mit einer Bash-Shell.
Du solltest dann im Prompt appuser@<container-id>:/app$ sehen - dein festgelegtes Arbeitsverzeichnis.
Mit exit verlässt und beendest du den Container wieder.
Docker Compose - der bessere Weg
Den Container jedes Mal mit einem langen docker run-Befehl zu starten, ist umständlich und fehleranfällig. Mit Docker Compose definierst du alles in einer Datei - Ports, Volumes, Restart-Verhalten - und startest mit einem einzigen Befehl.
Erstelle eine docker-compose.yml im selben Verzeichnis:
services:
dev-env:
build: .
container_name: dev-env
ports:
- "5666:5000"
volumes:
- ./src:/app/src
stdin_open: true
tty: true
restart: unless-stopped
Was passiert hier?
build: .- baut das Image aus dem Dockerfile im aktuellen Verzeichnisports- leitet Port 5666 auf dem Host zu Port 5000 im Container weiter (z.B. für Flask)volumes- verbindet dein lokalessrc/-Verzeichnis mit/app/srcim Container (dazu gleich mehr)stdin_openundtty- entspricht-itbeidocker run, damit du dich per Terminal verbinden kannstrestart: unless-stopped- der Container startet nach einem Reboot automatisch neu, es sei denn du hast ihn bewusst gestoppt
Starten und stoppen:
# Container bauen und im Hintergrund starten
docker compose up -d --build
# Logs anzeigen
docker compose logs -f dev-env
# Container stoppen
docker compose down
Volumes - deine Daten überleben den Container
Ein wichtiger Punkt, der gerade am Anfang für Verwirrung sorgt: Container sind flüchtig. Alles was du innerhalb eines Containers erstellst oder veränderst, ist weg, sobald du den Container löschst und neu erstellst. Ein einfacher Stopp und Neustart behält die Daten - aber ein docker compose down gefolgt von up nicht.
Die Lösung sind Volumes - sie verbinden ein Verzeichnis auf deinem Host mit einem Verzeichnis im Container.
Bind Mounts für Entwicklung
In unserer docker-compose.yml haben wir bereits einen Bind Mount definiert:
volumes:
- ./src:/app/src
Das bedeutet: Dein lokales src/-Verzeichnis wird direkt in den Container unter /app/src eingebunden. Änderungen auf beiden Seiten sind sofort sichtbar - du kannst auf dem Host programmieren und im Container ausführen.
Erstelle das Verzeichnis und teste es:
# Lokales src-Verzeichnis erstellen
mkdir -p src
# Eine Test-Datei anlegen
echo 'print("Hallo aus dem Container!")' > src/hello.py
# Container starten und Script ausführen
docker compose up -d --build
docker compose exec dev-env python src/hello.py
Named Volumes für persistente Daten
Für Daten die unabhängig vom Host-Dateisystem existieren sollen (z.B. Datenbanken), nutzt du Named Volumes:
services:
dev-env:
build: .
volumes:
- ./src:/app/src
- app-data:/app/data
volumes:
app-data:
Named Volumes werden von Docker verwaltet und überleben auch ein docker compose down. Erst docker compose down -v löscht sie explizit.
Du willst Docker nicht selbst einrichten? Ich übernehme das für dich — schau dir meine Services an
Container über Portainer bereitstellen
Alternativ kannst du deinen Container auch über Portainer verwalten. Trage den Namen des Images bei “Create Container” unter “Image” ein.
Lass dich nicht durch das docker.io irritieren - da Portainer lokal auf deine Images zugreift, findet er dein selbst erstelltes Image auch ohne Registry.

Gib den Port an (in unserem Beispiel 5666) und wähle unter “Advanced container settings” → “Console” die Option “Interactive & TTY” aus.

Mit Container verbinden
Deinen laufenden Container erreichst du über das Terminal oder über Portainer.
Per Terminal:
docker compose exec dev-env bash
# oder ohne Docker Compose:
docker exec -it dev-env /bin/bash
# mit exit verlässt du den Container wieder
Per Portainer:
In der Container-Übersicht den Console-Button klicken und auf Connect. Damit das funktioniert, muss die Option “Interactive & TTY” beim Deployment aktiviert sein.

Wenn du meiner Anleitung gefolgt bist, befindest du dich jetzt innerhalb deines Containers im Pfad /app. Python startest du einfach mit python und verlässt die Python-Umgebung wieder mit exit().
Wie geht es jetzt weiter?
Du hast jetzt ein eigenes Base-Image, das du als Grundlage für weitere Projekte nutzen kannst. Probier es direkt aus - erstelle eine kleine Flask-App in deinem src/-Verzeichnis:
# src/app.py
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "<h1>Mein erster Docker Container!</h1>"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
Starte den Container und rufe die App auf:
docker compose up -d --build
docker compose exec dev-env python src/app.py
Öffne jetzt im Browser http://<deine-raspberry-ip>:5666 - du solltest die Ausgabe deiner Flask-App sehen.
Weitere Ideen:
- Anderes Base-Image: Statt Python kannst du z.B.
node:22-slim-bookwormoderubuntu:24.04als Basis nehmen - Eigene Tools: Erweitere das Dockerfile mit allem, was du brauchst - Datenbanken, Build-Tools, CLI-Werkzeuge
Alles was du an dein Dockerfile anpasst, baust du einfach neu mit docker compose up -d --build.
Ich hoffe ich konnte dir helfen. Bei Fragen schreibe in den Kommentaren oder schau auf meinem Discord-Kanal vorbei.
Kurze Zusammenfassung
- Versionen prüfen: Vor dem Start auf Docker Hub und PyPI die aktuellen stabilen Versionen nachschauen
- Projektdateien erstellen:
Dockerfile,requirements.txt,.dockerignoreunddocker-compose.yml - Schlankes Base-Image nutzen:
python:3.13-slim-bookwormstatt des vollen Images - Feste Versionen pinnen: In der
requirements.txtimmer mit==arbeiten - Docker Compose nutzen: Ports, Volumes und Restart-Verhalten deklarativ in einer Datei
- Volumes für Daten: Bind Mounts für deinen Code, Named Volumes für persistente Daten
- Image bauen:
docker compose up -d --build - Container verbinden:
docker compose exec dev-env bash - Iterativ erweitern: Neue Pakete in
requirements.txtoderDockerfileeintragen und neu bauen
Du hast weitere Fragen oder benötigst Hilfe? Nutze die Kommentar-Sektion oder schau auf meinem Discord-Kanal vorbei.
Du wirst hier einen groben Überblick finden.
Allerdings biete ich dir auch noch etwas mehr Support an:
- Du benötigst persönlichen Support
- Du möchtest von Beginn an Unterstützung bei deinem Projekt
- Du möchtest ein hier vorgestelltes Plugin durch mich installieren und einrichten lassen
- Du würdest gerne ein von mir erstelltes Script etwas mehr an deine Bedürfnisse anpassen
Für diese Punkte und noch einiges mehr habe ich einen limitierten VIP-Tarif eingerichtet.
Falls der Tarif gerade nicht verfügbar ist, kontaktiere mich auf Discord!
Kommentare