Docker Compose auf dem VPS: Webserver, Datenbank & Reverse Proxy richtig betreiben

Warum Docker Compose auf einem VPS die beste Wahl ist

Wer heute einen Webserver, eine Datenbank und einen Reverse Proxy auf einem VPS betreiben möchte, kommt an Docker Compose kaum noch vorbei. Die Kombination aus Docker-Containern und einer deklarativen YAML-Konfiguration ermöglicht es, komplexe Stacks mit einem einzigen Befehl hochzufahren, zu skalieren oder zu aktualisieren.

Im Gegensatz zur klassischen LAMP-Installation, bei der Nginx, PHP-FPM, MySQL und Co. direkt auf dem Host-System installiert werden, isoliert Docker jede Komponente. Das hat mehrere Vorteile: Dienste lassen sich versionieren, portabel zwischen Servern verschieben und bei Bedarf sauber zurücksetzen, ohne das Host-System zu beeinträchtigen.

Besonders spannend wird es, wenn mehrere Domains oder Subdomains auf demselben VPS laufen sollen. Hier kommt der Reverse Proxy ins Spiel – typischerweise Traefik, Nginx Proxy Manager oder ein eigener Nginx-Container. Er verteilt eingehende Anfragen an die richtigen Container und kümmert sich gleichzeitig um SSL-Zertifikate.

hostazar.com setzt seit Jahren auf Docker-basierte Stacks, weil sich damit reproduzierbare Deployments realisieren lassen. Egal ob WordPress, Node.js, Python oder PHP – die zugrunde liegende Infrastruktur bleibt stets die gleiche.

Voraussetzungen für den Docker-Compose-Setup

Bevor du mit der eigentlichen Konfiguration beginnst, sollte dein VPS einige Grundvoraussetzungen erfüllen. Empfohlen wird mindestens 2 GB RAM, 2 vCPUs und 40 GB SSD-Speicher, damit mehrere Container komfortabel laufen können.

Als Betriebssystem empfiehlt sich eine aktuelle Ubuntu LTS- oder Debian-Distribution. Beide bieten aktuelle Docker-Pakete, eine große Community und langfristigen Support. Bei hostazar.com werden vorkonfigurierte VPS mit Docker-Vorinstallationen angeboten, was die Einrichtung deutlich beschleunigt.

Folgende Komponenten sollten installiert sein:

  • Docker Engine (ab Version 24.x)
  • Docker Compose Plugin (ab Version 2.20)
  • Git für Versionsverwaltung
  • UFW oder eine andere Firewall
  • Eine Domain, die per A-Record auf die VPS-IP zeigt

Die Installation von Docker und Docker Compose gestaltet sich auf Ubuntu denkbar einfach und ist in wenigen Minuten erledigt.

# Docker installieren
sudo apt update
sudo apt install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

# Benutzer zur Docker-Gruppe hinzufügen
sudo usermod -aG docker $USER
newgrp docker

# Installation prüfen
docker --version
docker compose version

Die richtige Projektstruktur für Docker Compose

Eine saubere Verzeichnisstruktur ist das A und O, wenn du langfristig mit Docker Compose arbeiten möchtest. Sie hilft dir, den Überblick zu behalten, Backups korrekt zu erstellen und Konfigurationen versionierbar zu machen.

Am besten legst du im Home-Verzeichnis des Server-Benutzers ein zentrales docker-Verzeichnis an, in dem jedes Projekt ein eigenes Unterverzeichnis bekommt. Jedes Projekt enthält seine eigene docker-compose.yml, einen .env-Datei für sensible Variablen sowie Volumes für persistente Daten.

Persistente Daten wie Datenbank-Inhalte, Uploads oder Konfigurationsdateien sollten niemals innerhalb des Containers gespeichert werden. Stattdessen werden sie über benannte Volumes oder Bind-Mounts auf dem Host gespeichert, damit sie Container-Updates überleben.

~/docker/
├── traefik/                  # Reverse Proxy
│   ├── docker-compose.yml
│   ├── traefik.yml
│   ├── dynamic.yml
│   └── letsencrypt/
├── app1/                     # Erstes Projekt (z.B. WordPress)
│   ├── docker-compose.yml
│   ├── .env
│   └── data/
├── app2/                     # Zweites Projekt
│   ├── docker-compose.yml
│   ├── .env
│   └── data/
└── backups/                  # Backup-Verzeichnis
    └── scripts/

Diese Trennung hat einen weiteren Vorteil: Jeder Container-Stack kann unabhängig gestartet, gestoppt und aktualisiert werden, ohne andere Projekte zu beeinflussen. Das ist besonders bei produktiven Setups essenziell.

Webserver-Container: Nginx oder Apache in Docker

Der Webserver bildet das Herzstück jeder Webanwendung. In Docker-Setups wird Nginx bevorzugt, da es ressourcenschonender arbeitet und sich hervorragend als Reverse Proxy eignet. Es gibt zwei grundlegende Ansätze: ein dedizierter Nginx-Container pro Projekt oder ein gemeinsamer Reverse Proxy wie Traefik.

Für klassische PHP-Anwendungen wie WordPress, Nextcloud oder Laravel empfiehlt sich das PHP-FPM-Modell: Nginx dient als Frontend, verarbeitet statische Dateien selbst und reicht dynamische Anfragen an einen separaten PHP-FPM-Container weiter.

Ein typischer Nginx-Container nutzt ein offizielles Image und wird über ein eigenes Dockerfile oder Inline-Konfiguration angepasst. Individuelle Nginx-Konfigurationen werden als Volume eingebunden, was Updates vereinfacht.

version: "3.9"

services:
  nginx:
    image: nginx:1.27-alpine
    container_name: project-nginx
    restart: unless-stopped
    ports:
      - "8080:80"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - ./html:/var/www/html:ro
    networks:
      - backend
    depends_on:
      - php

  php:
    image: php:8.3-fpm-alpine
    container_name: project-php
    restart: unless-stopped
    volumes:
      - ./html:/var/www/html
    networks:
      - backend

networks:
  backend:
    driver: bridge

Beachte, dass der Nginx-Container in diesem Setup den Port 8080 nutzt, da der Reverse Proxy (Traefik) später die Ports 80 und 443 auf dem Host belegt. Der depends_on-Parameter sorgt dafür, dass PHP vor Nginx startet.

Für Node.js-Anwendungen kannst du stattdessen den offiziellen node-Container verwenden und auf Nginx komplett verzichten, da der eingebettete Express-Server die HTTP-Anfragen direkt verarbeitet.

Datenbank-Container: MariaDB, MySQL oder PostgreSQL

Eine performante Datenbank gehört in jeden produktiven Webstack. In Docker werden Datenbanken als isolierte Container betrieben, was den Vorteil hat, dass Version-Updates oder Migrationen ohne Eingriff in das Host-System möglich sind.

Die Wahl der Datenbank hängt vom Anwendungsfall ab. MariaDB ist der Standard für die meisten PHP-Projekte, PostgreSQL überzeugt bei komplexen Datenstrukturen und JSON-Operationen, während MySQL von Oracle weiterhin in Enterprise-Umgebungen stark vertreten ist.

Bei der Konfiguration solltest du unbedingt auf folgende Punkte achten: Passwörter niemals in die docker-compose.yml schreiben, sondern in eine .env-Datei auslagern. Diese Datei wird zusätzlich in die .gitignore aufgenommen, damit keine sensiblen Daten ins Repository gelangen.

version: "3.9"

services:
  db:
    image: mariadb:11.4
    container_name: project-db
    restart: unless-stopped
    environment:
      MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
      MARIADB_DATABASE: ${DB_NAME}
      MARIADB_USER: ${DB_USER}
      MARIADB_PASSWORD: ${DB_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
      - ./init:/docker-entrypoint-initdb.d:ro
    networks:
      - backend
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      interval: 30s
      timeout: 10s
      retries: 5

volumes:
  db_data:
    driver: local

networks:
  backend:
    driver: bridge

Der healthcheck ist besonders wichtig, da andere Container dadurch den Status der Datenbank abfragen können. Compose kann so warten, bis die Datenbank wirklich bereit ist, bevor abhängige Services starten. Persistente Daten liegen im benannten Volume db_data und überleben Container-Neustarts.

Reverse Proxy mit Traefik – der Standard für Docker-Setups

Ein Reverse Proxy nimmt eingehende HTTP- und HTTPS-Anfragen entgegen und verteilt sie an die dahinterliegenden Container. Er ist außerdem für die Verwaltung von SSL-Zertifikaten, Load Balancing und das Routing zwischen mehreren Domains zuständig.

Traefik hat sich in den letzten Jahren als bevorzugter Reverse Proxy für Docker-Setups etabliert. Das liegt vor allem an der nativen Docker-Integration: Traefik erkennt neue Container automatisch und konfiguriert sich anhand von Labels selbst. Du musst keine Nginx-Konfigurationen manuell pflegen.

Traefik arbeitet mit einem Provider-Modell. Der Docker-Provider überwacht laufende Container und liest Labels wie traefik.http.routers.app.rule=Host(`example.com`). Daraus generiert Traefik automatisch die passenden Routing-Regeln.

Die Vorteile von Traefik gegenüber klassischem Nginx-Proxy:

  • Automatische SSL-Zertifikate über Let's Encrypt
  • Hot Reloading der Konfiguration
  • Integriertes Dashboard zur Überwachung
  • Native Unterstützung für Docker, Docker Swarm und Kubernetes
# docker-compose.yml für Traefik
version: "3.9"

services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    restart: unless-stopped
    command:
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
      - "[email protected]"
      - "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
      - "--accesslog=true"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    networks:
      - proxy

networks:
  proxy:
    name: proxy
    driver: bridge

Wichtig ist, dass Traefik den Docker-Socket als read-only gemountet bekommt. Das verhindert, dass kompromittierte Traefik-Container Änderungen am Docker-Daemon vornehmen können. Das Dashboard sollte zusätzlich durch eine Basic-Authentifizierung oder einen VPN-Zugang geschützt werden.

Das vollständige docker-compose.yml-Setup

Die Königsdisziplin ist das Zusammenführen aller Komponenten zu einem kohärenten Stack. Das folgende Beispiel zeigt einen realistischen Web-Stack mit Traefik, Nginx, PHP-FPM und MariaDB – bereit für den produktiven Einsatz.

Die wichtigsten Designprinzipien dabei: alle Services in einem Compose-File, gemeinsames Netzwerk, persistente Volumes, Labels für Traefik-Routing und kein direktes Port-Mapping auf den Datenbank-Container.

version: "3.9"

services:
  nginx:
    image: nginx:1.27-alpine
    container_name: app-nginx
    restart: unless-stopped
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
      - app_data:/var/www/html:ro
    networks:
      - proxy
      - backend
    depends_on:
      - php
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.app.rule=Host(`example.com`,`www.example.com`)"
      - "traefik.http.routers.app.entrypoints=websecure"
      - "traefik.http.routers.app.tls.certresolver
  
    
VPS absichern 2026: SSH, Firewall, Fail2ban & Updates
DevOps 02. June 2026 10 Min

VPS absichern 2026: SSH, Firewall, Fail2ban & Updates

VPS absichern 2026: Einsteiger-Guide für sichere SSH-Logins, UFW-Firewall, Fail2ban, automatische Updates, Backups und Monitoring auf deinem Linux-Server.