Warum Docker im Homelab?

Stell dir vor, du installierst Nextcloud, Gitea und ein Wiki direkt auf dem gleichen Server. Nextcloud will PHP 8.2, dein Wiki besteht auf PHP 7.4, und beide wollen ihren eigenen Apache. Gratulation, du hast Dependency Hell erreicht — und dein Samstag ist gelaufen.

Docker löst das Problem radikal: Jeder Service läuft in seinem eigenen Container mit exakt den Abhängigkeiten, die er braucht. Die Container wissen nichts voneinander, teilen sich keine Libraries, und können sich nicht gegenseitig kaputt machen. Ein docker compose up -d und der ganze Stack läuft. Ein docker compose down und er ist weg, ohne Spuren zu hinterlassen.

Das ist keine Bequemlichkeit — das ist der Unterschied zwischen einem Homelab, das du pflegst, und einem, das dich pflegt.

Konkret: Was Docker dir bringt

  • Isolation: Service A kann Service B nicht zerschießen. Auch wenn Service A PHP von 2019 braucht.
  • Reproduzierbarkeit: docker-compose.yml ist deine Infrastruktur als Code. Server neu aufsetzen? docker compose up -d. Fertig.
  • Updates ohne Risiko: Image ziehen, Container neu starten. Geht was schief? Altes Image, alter Container. Rollback in 30 Sekunden.
  • Community-Images: Für fast jede Open-Source-Software gibt es ein offizielles Docker-Image. Du konfigurierst, statt zu kompilieren.

Was du nach diesem Tutorial hast

  • ✅ Docker läuft stabil auf Proxmox (richtig konfiguriert, nicht irgendwie)
  • ✅ Docker Compose für Multi-Container-Stacks
  • ✅ Portainer als Web-GUI für den Überblick
  • ✅ Verständnis von Volumes, Netzwerken und Compose-Dateien
  • ✅ Deine ersten Self-Hosted Services

Docker auf Proxmox: LXC oder VM?

Die Frage, die in jedem Proxmox-Forum für Endlos-Diskussionen sorgt. Hier die ehrliche Antwort:

Option 1: Docker in einem LXC-Container

ProContra
Minimaler Overhead (~50 MB RAM)Teilt den Host-Kernel — wenn der Container den Kernel crasht, stirbt alles
Start in SekundenManche Docker-Features funktionieren nicht (AppArmor, bestimmte Netzwerk-Modi)
ZFS-Snapshots direkt vom HostBraucht oft privilegierten Modus → schlechtere Isolation
Perfekt für RAM-knappe SetupsDebugging bei Problemen ist nervenaufreibend

Geeignet für: Erfahrene Admins, die genau wissen was sie tun und jeden MB RAM zählen.

Option 2: Docker in einer VM (Empfohlen)

ProContra
Eigener Kernel — volle Docker-Kompatibilität~512 MB - 1 GB mehr RAM-Verbrauch
Alle Features funktionieren. Immer.Boot dauert 10-30 Sekunden statt 2
Volle Isolation vom HostEtwas mehr Storage-Overhead
Kein Gefummel mit LXC-Konfiguration
Snapshots und Backups sauber integriert

Geeignet für: Alle. Besonders wenn du keine Lust auf nächtliche Debugging-Sessions hast.

Meine klare Empfehlung: Nimm die VM. Der Extra-RAM-Verbrauch von ~1 GB ist es nicht wert, sich mit LXC-Docker-Inkompatibilitäten herumzuschlagen. In einer VM funktioniert Docker exakt so, wie die Dokumentation es verspricht. In LXC funktioniert Docker meistens so — und das “meistens” wird dich um 2 Uhr nachts auf einem Freitag einholen. Wenn du wirklich jeden MB RAM brauchst, hast du ohnehin ein Hardware-Problem, kein LXC-vs-VM-Problem.


Variante A: Docker in einer Debian-VM (Empfohlen)

Schritt 1: VM in Proxmox erstellen

Im Proxmox Web-Interface:

  1. “Create VM” klicken
  2. General:
    • Name: docker-host
    • Start at boot: ✅ (sonst stehen nach einem Proxmox-Reboot alle deine Services still)
  3. OS:
    • ISO: Debian 12 Netinstall
  4. System:
    • BIOS: Default (SeaBIOS)
    • Machine: q35 (moderner Chipsatz, bessere PCIe-Emulation)
    • SCSI Controller: VirtIO SCSI single
  5. Disks:
    • Bus: VirtIO Block
    • Size: 64 GB (Docker-Images fressen Platz — lieber zu viel als zu wenig)
    • SSD emulation: ✅
    • Discard: ✅ (TRIM-Support, damit ZFS/LVM den freien Platz zurückbekommt)
  6. CPU:
    • Cores: 4 (mehr ist besser, Docker-Builds können hungrig sein)
    • Type: host (gibt der VM Zugriff auf alle CPU-Features — messbar schneller als kvm64)
  7. Memory:
    • RAM: 8192 MB (8 GB — damit hast du Luft für 15-20 Container)
    • Ballooning: Kannst du aktivieren, braucht aber QEMU Guest Agent
  8. Network:
    • Model: VirtIO (10x schneller als emulierte Intel E1000, weil paravirtualisiert)

Schritt 2: Debian installieren

VM starten, Debian durchinstallieren:

  • Minimal Installation (kein Desktop, kein Print-Server — wir sind hier nicht bei Windows)
  • SSH Server: ✅ (das einzige was du anhakst)
  • Partitionierung: Guided – use entire disk

Nach dem Reboot per SSH verbinden:

# Als root: Grundpakete installieren
apt update && apt install -y sudo curl wget gnupg ca-certificates

# Deinen User zur sudo-Gruppe hinzufügen
usermod -aG sudo dein-username

# QEMU Guest Agent installieren — damit Proxmox die VM-IP anzeigt,
# saubere Shutdowns machen kann, und Freeze/Thaw für Backups funktioniert
apt install -y qemu-guest-agent
systemctl enable --now qemu-guest-agent

Schritt 3: Docker installieren

Wichtig: Nimm das offizielle Docker-Repository, nicht die Debian-Pakete. Die Debian-Version hinkt mehrere Major-Releases hinterher und hat bekannte Bugs. Das ist wie Windows XP im Jahr 2026 zu fahren:

# Docker GPG Key hinzufügen
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

# Docker Repository hinzufügen
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  tee /etc/apt/sources.list.d/docker.list > /dev/null

# Docker installieren
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Docker-Daemon starten und dauerhaft aktivieren
systemctl enable --now docker

# Deinen User zur docker-Gruppe hinzufügen
# (damit du nicht bei jedem Befehl sudo brauchst)
usermod -aG docker dein-username

# Einmal ausloggen und wieder einloggen, damit die Gruppenmitgliedschaft greift!

# Testen:
docker --version
docker compose version

Erwartete Ausgabe:

Docker version 27.x.x, build xxxxxxx
Docker Compose version v2.x.x

Funktioniert? Glückwunsch. Du hast gerade die aufwändigste Docker-Installation hinter dir. Ab jetzt wird’s nur noch einfacher.

Schritt 4: Docker sinnvoll konfigurieren

Die Standard-Docker-Konfiguration ist okay — aber “okay” reicht nicht, wenn 20 Container gleichzeitig Logs schreiben und irgendwann deine Platte voll ist:

cat > /etc/docker/daemon.json << 'EOF'
{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  },
  "storage-driver": "overlay2",
  "default-address-pools": [
    {
      "base": "172.17.0.0/12",
      "size": 24
    }
  ]
}
EOF

systemctl restart docker

Was das tut und warum:

  • Log-Rotation (max 10 MB, 3 Dateien): Ohne das wachsen Docker-Logs unbegrenzt. Ich habe schon Server gesehen, wo ein einzelner Container 40 GB Logs angehäuft hat. An einem Wochenende.
  • Overlay2: Der effizienteste Storage-Driver. Schneller und platzsparender als die Alternativen.
  • Address Pools: Gibt Docker genug IP-Raum für viele Container-Netzwerke, ohne mit deinem LAN zu kollidieren.

Variante B: Docker in einem LXC-Container (Für Fortgeschrittene)

⚠️ Klare Warnung: Das hier ist für Leute, die Spaß an Troubleshooting haben. Wenn du Docker schnell und stabil willst, nimm Variante A. Ernst gemeint.

LXC-Container erstellen

  1. “Create CT” im Proxmox Web-Interface
  2. Template: Debian 12 Standard
  3. Disk: 32 GB
  4. CPU: 4 Cores
  5. RAM: 8 GB
  6. Swap: 2 GB
  7. Network: Statische IP

LXC-Konfiguration anpassen

Auf dem Proxmox-Host (nicht im Container!):

CT_ID=100  # Deine Container-ID

# Nesting aktivieren (damit Docker Container in einem Container starten kann)
# Keyctl aktivieren (braucht Docker für Overlay-Filesystem)
pct set $CT_ID --features nesting=1,keyctl=1

# AppArmor lockern — sonst blockiert es Docker-Operationen
echo "lxc.apparmor.profile: unconfined" >> /etc/pve/lxc/$CT_ID.conf
echo "lxc.cap.drop:" >> /etc/pve/lxc/$CT_ID.conf

Dann im Container Docker installieren wie in Variante A (Schritt 3). Und beten.


Docker Compose: Das Werkzeug, das alles zusammenhält

Docker Compose ist nicht optional — es ist der Grund, warum Selfhosting mit Docker überhaupt Spaß macht. Statt Container einzeln mit ellenlangen docker run-Befehlen zu starten, beschreibst du deinen gesamten Stack in einer YAML-Datei. Reproduzierbar, versionierbar, verständlich.

Ordnerstruktur für dein Homelab

So organisiere ich meine Docker-Services — jeder in seinem eigenen Verzeichnis:

/opt/docker/
├── portainer/
│   └── docker-compose.yml
├── nextcloud/
│   ├── docker-compose.yml
│   └── .env
├── pihole/
│   └── docker-compose.yml
├── monitoring/
│   └── docker-compose.yml
└── reverse-proxy/
    └── docker-compose.yml

Warum so? Jeder Service ist eigenständig. Du kannst einen Service updaten, stoppen oder löschen, ohne andere zu beeinflussen. Du kannst das ganze Verzeichnis in Git versionieren. Und wenn du den Server neu aufsetzt, kopierst du /opt/docker/ rüber, machst docker compose up -d in jedem Ordner — fertig.

mkdir -p /opt/docker

Docker Compose Grundlagen

Eine typische docker-compose.yml:

# Beispiel: Uptime Kuma (Monitoring)
services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: unless-stopped
    ports:
      - "3001:3001"
    volumes:
      - ./data:/app/data
    environment:
      - TZ=Europe/Berlin

Die wichtigsten Felder erklärt:

FeldWas es tutWarum es wichtig ist
imageDocker-Image von Docker HubBestimmt welche Software läuft
container_nameFester Name statt ZufallsnameDamit du in docker ps nicht den Überblick verlierst
restart: unless-stoppedStartet automatisch nach Crashes/RebootsContainer überlebt Neustarts, außer du stoppst ihn bewusst
portsHost-Port:Container-PortSo erreichst du den Service von außen
volumesHost-Pfad:Container-PfadPersistente Daten überleben Container-Neuerstellung
environmentKonfiguration per UmgebungsvariablenKein Config-File-Fummeln nötig

Die Compose-Befehle, die du täglich brauchst

cd /opt/docker/mein-service/

# Stack starten (im Hintergrund)
docker compose up -d

# Live-Logs — wenn du sehen willst was passiert
docker compose logs -f

# Stack stoppen und aufräumen
docker compose down

# Update: neues Image ziehen + Container neu erstellen
docker compose pull && docker compose up -d

# Status aller Container im Stack
docker compose ps

Portainer: Docker-Web-GUI

Portainer gibt dir eine Web-Oberfläche für Docker. Container starten, stoppen, Logs lesen, Compose-Stacks verwalten — alles im Browser. Manche Puristen sagen “CLI reicht”. Die verwalten auch keine 30 Container parallel.

Installation

mkdir -p /opt/docker/portainer
cat > /opt/docker/portainer/docker-compose.yml << 'EOF'
services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: unless-stopped
    ports:
      - "9443:9443"
      - "9000:9000"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    environment:
      - TZ=Europe/Berlin

volumes:
  portainer_data:
EOF

cd /opt/docker/portainer
docker compose up -d

https://DEINE-IP:9443 öffnen → Admin-Konto erstellen → “Get Started” → fertig.

Sicherheits-Hinweis: Portainer hat über den Docker Socket Root-Zugriff auf den gesamten Host. Das ist by design, aber es heißt auch: starkes Passwort, nicht ins Internet exponieren, und am besten hinter einem Reverse Proxy mit 2FA.


Deine ersten Self-Hosted Services

Docker läuft, Compose funktioniert, Portainer zeigt dir eine leere Seite. Zeit, das zu ändern.

1. Uptime Kuma — Weißt du ob alles läuft?

Monitoring für alle deine Services — HTTP-Checks, Ping, DNS, TCP-Ports, Docker-Container-Status. Benachrichtigung per Telegram, E-Mail, Discord. Setup in 2 Minuten.

mkdir -p /opt/docker/uptime-kuma
cat > /opt/docker/uptime-kuma/docker-compose.yml << 'EOF'
services:
  uptime-kuma:
    image: louislam/uptime-kuma:1
    container_name: uptime-kuma
    restart: unless-stopped
    ports:
      - "3001:3001"
    volumes:
      - ./data:/app/data
    environment:
      - TZ=Europe/Berlin
EOF

cd /opt/docker/uptime-kuma && docker compose up -d

http://DEINE-IP:3001 — Status-Page in 5 Minuten konfiguriert. Danach weißt du sofort, wenn ein Service stirbt statt es zufällig beim Abendessen zu merken.

2. Homepage — Dein Homelab-Dashboard

Eine zentrale Startseite mit Links zu allen Services, System-Status, und Docker-Integration:

mkdir -p /opt/docker/homepage
cat > /opt/docker/homepage/docker-compose.yml << 'EOF'
services:
  homepage:
    image: ghcr.io/gethomepage/homepage:latest
    container_name: homepage
    restart: unless-stopped
    ports:
      - "3000:3000"
    volumes:
      - ./config:/app/config
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      - TZ=Europe/Berlin
      - PUID=1000
      - PGID=1000
EOF

cd /opt/docker/homepage && docker compose up -d

3. Nginx Proxy Manager — Reverse Proxy für Menschen

Statt http://192.168.1.100:8096 tippst du https://jellyfin.deine-domain.de. SSL-Zertifikate per Let’s Encrypt auf Knopfdruck. Kein Nginx-Config editieren, kein Certbot-Scripting:

mkdir -p /opt/docker/nginx-proxy-manager
cat > /opt/docker/nginx-proxy-manager/docker-compose.yml << 'EOF'
services:
  npm:
    image: jc21/nginx-proxy-manager:latest
    container_name: nginx-proxy-manager
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "81:81"
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    environment:
      - TZ=Europe/Berlin
EOF

cd /opt/docker/nginx-proxy-manager && docker compose up -d

Standard-Login: admin@example.com / changeme → sofort ändern!


Docker Volumes: Wo sind meine Daten?

Das hier ist entscheidend, und viele machen es falsch:

Bind Mounts vs. Named Volumes

Bind Mounts (./data:/app/data): Daten liegen genau dort, wo du es hinschreibst. Backup = Verzeichnis kopieren. Einfach, transparent, kontrollierbar. Das willst du für alles, was dir wichtig ist.

Named Volumes (portainer_data:/data): Docker verwaltet den Speicherort irgendwo unter /var/lib/docker/volumes/. Funktioniert, aber du musst erst suchen wo die Daten liegen, und docker volume Befehle benutzen. Gut für temporäre Daten, schlecht für Sachen die du sichern willst.

Meine Regel: Bind Mounts für alles. Named Volumes nur wenn das Image es erzwingt oder die Daten egal sind.

Backup-Script für Docker-Daten

#!/bin/bash
# /opt/docker/backup-docker.sh
# Sichert alle Docker-Compose-Daten konsistent

BACKUP_DIR="/opt/backups/docker"
DOCKER_DIR="/opt/docker"
DATE=$(date +%Y-%m-%d)

mkdir -p "$BACKUP_DIR"

for service in "$DOCKER_DIR"/*/; do
    name=$(basename "$service")
    [ "$name" = "backups" ] && continue  # Meta-Verzeichnis skippen
    
    echo "📦 Backup: $name"
    
    # Container stoppen für konsistentes Backup
    cd "$service"
    docker compose stop 2>/dev/null
    
    # Archiv erstellen
    tar czf "$BACKUP_DIR/${name}_${DATE}.tar.gz" -C "$DOCKER_DIR" "$name"
    
    # Container wieder starten
    docker compose start 2>/dev/null
done

# Alte Backups aufräumen (>30 Tage)
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +30 -delete

echo "✅ Backup abgeschlossen: $BACKUP_DIR"
chmod +x /opt/docker/backup-docker.sh

# Cronjob: Täglich um 3 Uhr
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/docker/backup-docker.sh >> /var/log/docker-backup.log 2>&1") | crontab -

Docker-Netzwerke: Wer redet mit wem?

Innerhalb eines Compose-Stacks

Container im gleichen Stack können sich über ihren Servicenamen erreichen. Docker macht das DNS automatisch:

services:
  app:
    image: nextcloud
    environment:
      - MYSQL_HOST=db    # ← einfach den Servicenamen, Docker löst das auf
  
  db:
    image: mariadb

Keine IPs, keine Port-Mappings nötig. Das passiert intern im Stack-Netzwerk.

Zwischen verschiedenen Stacks

Wenn dein Reverse Proxy (eigener Stack) mit Nextcloud (anderer Stack) reden soll, brauchst du ein gemeinsames Netzwerk:

# Einmal erstellen
docker network create proxy-network
# In BEIDEN Compose-Dateien:
services:
  mein-service:
    networks:
      - proxy-network
      - default    # Behält auch das Stack-interne Netzwerk

networks:
  proxy-network:
    external: true

Warum ein eigenes Netzwerk statt alles in host-Mode? Isolation. Dein Datenbank-Container muss nicht vom ganzen LAN erreichbar sein — nur vom App-Container. Weniger Angriffsfläche.


Performance-Tipps aus der Praxis

1. Ressourcen-Limits setzen

Ein Container ohne Limits kann den ganzen Host lahmlegen. Passiert schneller als du denkst — ein Bug im Container, eine Endlosschleife, und plötzlich hat der Host 0% freie CPU:

services:
  mein-service:
    deploy:
      resources:
        limits:
          cpus: "2.0"
          memory: 2G
        reservations:
          cpus: "0.5"
          memory: 512M

2. Docker-Müll regelmäßig aufräumen

Docker hortet alte Images wie ein digitaler Messie. Nach ein paar Monaten hast du Gigabytes an ungenutzten Images, gestoppten Containern und verwaisten Volumes:

# Alles Ungenutzte entfernen (Images, Container, Volumes, Netzwerke)
docker system prune -a --volumes

# Nur sehen wie viel Platz Docker frisst
docker system df

3. Log-Rotation (nochmal, weil es wirklich wichtig ist)

Ohne die daemon.json-Konfiguration von oben wachsen Docker-Logs unbegrenzt. Ein chatty Container kann dir die Platte in Tagen vollschreiben. Die 30 Sekunden Konfigurationszeit ersparen dir Stunden Notfall-Debugging.


Häufige Probleme und Lösungen

“Permission denied” bei Volumes

Das Problem Nummer 1 bei Docker. Container laufen intern oft als User 1000:1000, aber das Verzeichnis auf dem Host gehört root:

# Rechte anpassen
chown -R 1000:1000 ./data

# Oder bei LinuxServer.io Images: PUID/PGID setzen
environment:
  - PUID=1000
  - PGID=1000

Container kann keine DNS-Anfragen machen

Passiert, wenn Docker’s eingebauter DNS nicht richtig funktioniert:

# In /etc/docker/daemon.json hinzufügen:
{
  "dns": ["1.1.1.1", "9.9.9.9"]
}
# Dann: systemctl restart docker

Port bereits belegt

# Wer belegt den Port?
ss -tlnp | grep :80

# Lösung: Anderen Host-Port nutzen
ports:
  - "8080:80"  # Host-Port 8080, Container-Port bleibt 80

Nächste Schritte

Dein Docker-Host auf Proxmox steht. Du hast Compose verstanden, Portainer läuft, die ersten Services sind online. Ab hier wird’s richtig gut:

  1. Die 15 besten Self-Hosted Dienste — Nextcloud, Vaultwarden, Immich und mehr
  2. Watchtower für automatische Container-Updates (mit Benachrichtigung!)
  3. Traefik als Alternative zu Nginx Proxy Manager (wenn du Labels statt GUI willst)
  4. Automatische Backups auf ein NAS oder in die Cloud

Auf awesome-selfhosted findest du Hunderte von Services. Die Rabbit Hole ist tief — und macht süchtig. Du bist gewarnt.


Fazit

Docker auf Proxmox ist das Rückgrat jedes Homelabs. VM statt LXC nehmen (ernst gemeint), Docker Compose für alles verwenden, Bind Mounts für Daten die dir wichtig sind, Log-Rotation nicht vergessen. Das sind die vier Regeln, die dir 90% aller Probleme ersparen.

Starte mit einem einfachen Service — Uptime Kuma oder Pi-hole. Wenn der sauber läuft, baust du weiter. In ein paar Wochen hast du ein Setup, um das dich Kollegen beneiden. Und das Beste: Du verstehst jede einzelne Zeile davon, weil du es selbst gebaut hast.


Zuletzt aktualisiert: Februar 2026 | Docker 27.x | Proxmox VE 8.x