Docker Compose Deployment auf dem VPS
Vollständige Anleitung zur Bereitstellung von Anwendungen mit Docker Compose auf einem VPS. Von der Installation bis hin zu Multi-Container-Setups mit Netzwerken und Volumes.
Docker Compose Deployment auf dem VPS
Docker Compose ist der einfachste Weg, Anwendungen auf deinem VPS bereitzustellen und zu verwalten. Wenn du nach dem besten VPS für Docker suchst, schau dir zuerst unseren Vergleich an. Dann definierst du deinen gesamten Stack in einer einzigen Datei, deployest mit einem Befehl und aktualisierst ohne Ausfallzeiten.
Warum das wichtig ist
Herkömmliche Deployments sind mühsam:
- Abhängigkeiten zwischen Anwendungen führen zu Konflikten
- Das „Bei mir funktioniert es”-Syndrom
- Aufwendige manuelle Einrichtung für jeden Server
- Rollbacks erfordern Glück und Beten
Docker Compose löst diese Probleme:
- Isolierte Umgebungen – Apps können sich gegenseitig nicht beeinträchtigen
- Reproduzierbare Deployments – Gleiche Konfiguration, gleiches Ergebnis, jedes Mal
- Versionskontrolle – Deine Infrastruktur ist Code
- Einfache Rollbacks – Die vorherige Version ist einen Befehl entfernt
Voraussetzungen
- Ein VPS mit Ubuntu 22.04+ (wir empfehlen Hostinger VPS wegen ihrer Docker-optimierten Images)
- Grundlegende Kenntnisse der Kommandozeile
- SSH-Zugang zu deinem Server
Schritt 1: Docker installieren
# Alte Versionen entfernen
sudo apt remove docker docker-engine docker.io containerd runc
# Abhängigkeiten installieren
sudo apt update
sudo apt install ca-certificates curl gnupg -y
# Docker-GPG-Schlüssel hinzufügen
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
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Repository hinzufügen
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
# Docker installieren
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
# Eigenen Benutzer zur Docker-Gruppe hinzufügen (Aus- und Einloggen erforderlich)
sudo usermod -aG docker $USER
# Installation überprüfen
docker --version
docker compose version
Melde dich ab und wieder an, damit die Gruppenänderungen wirksam werden.
Schritt 2: Projektstruktur anlegen
mkdir -p ~/apps/myapp
cd ~/apps/myapp
Empfohlene Struktur:
myapp/
├── docker-compose.yml # Haupt-Compose-Datei
├── docker-compose.prod.yml # Produktions-Overrides
├── .env # Umgebungsvariablen (niemals committen!)
├── .env.example # Vorlage für Umgebungsvariablen
├── nginx/
│ └── nginx.conf # Benutzerdefinierte Nginx-Konfiguration
├── data/ # Persistente Daten (in .gitignore)
└── logs/ # Anwendungslogs (in .gitignore)
Schritt 3: Erste docker-compose.yml schreiben
Lass uns einen vollständigen Web-Stack deployen:
# docker-compose.yml
services:
app:
image: node:20-alpine
working_dir: /app
volumes:
- ./src:/app
- /app/node_modules
command: npm start
environment:
- NODE_ENV=production
- DATABASE_URL=postgres://user:pass@db:5432/myapp
depends_on:
db:
condition: service_healthy
restart: unless-stopped
networks:
- internal
db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: myapp
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- internal
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./certbot/conf:/etc/letsencrypt:ro
- ./certbot/www:/var/www/certbot:ro
depends_on:
- app
restart: unless-stopped
networks:
- internal
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
command: redis-server --appendonly yes
restart: unless-stopped
networks:
- internal
volumes:
postgres_data:
redis_data:
networks:
internal:
driver: bridge
Schritt 4: Umgebungsvariablen richtig verwenden
Erstelle deine .env-Datei:
# .env
DB_PASSWORD=your-super-secret-password-here
REDIS_PASSWORD=another-secret
API_KEY=your-api-key
Erstelle .env.example zur Dokumentation:
# .env.example
DB_PASSWORD=
REDIS_PASSWORD=
API_KEY=
Committe .env niemals in git! Füge sie zur .gitignore hinzu:
echo ".env" >> .gitignore
echo "data/" >> .gitignore
echo "logs/" >> .gitignore
Schritt 5: Produktions-Overrides
Erstelle eine produktionsspezifische Datei:
# docker-compose.prod.yml
services:
app:
deploy:
resources:
limits:
cpus: '1'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
db:
deploy:
resources:
limits:
cpus: '2'
memory: 1G
Deployment mit:
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Schritt 6: Wichtige Docker Compose-Befehle
# Alle Services starten
docker compose up -d
# Logs anzeigen
docker compose logs -f
# Logs eines bestimmten Services anzeigen
docker compose logs -f app
# Alle Services stoppen
docker compose down
# Stoppen und Volumes entfernen (ACHTUNG – löscht Daten!)
docker compose down -v
# Neu bauen und starten
docker compose up -d --build
# Einen bestimmten Service neu starten
docker compose restart app
# Laufende Container anzeigen
docker compose ps
# Befehl in einem Container ausführen
docker compose exec app sh
# Ressourcennutzung anzeigen
docker stats
Schritt 7: Deployments ohne Ausfallzeiten
Für Updates ohne Downtime:
# Neue Images herunterladen
docker compose pull
# Nur geänderte Container neu erstellen
docker compose up -d --no-deps app
Oder Rolling Updates mit mehreren Replikas verwenden:
services:
app:
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
Schritt 8: Health Checks
Füge immer Health Checks hinzu:
services:
app:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Health-Status prüfen:
docker compose ps
docker inspect --format='{{json .State.Health}}' container_name
Schritt 9: Secrets verwalten
Für sensible Daten Docker Secrets oder externe Secret-Manager verwenden:
services:
app:
secrets:
- db_password
- api_key
environment:
- DB_PASSWORD_FILE=/run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txt
Schritt 10: Backup-Strategie
Erstelle ein Backup-Skript:
#!/bin/bash
# backup.sh
BACKUP_DIR="/backups/$(date +%Y-%m-%d)"
mkdir -p "$BACKUP_DIR"
# PostgreSQL sichern
docker compose exec -T db pg_dump -U user myapp > "$BACKUP_DIR/db.sql"
# Volumes sichern
docker run --rm \
-v myapp_postgres_data:/data:ro \
-v "$BACKUP_DIR":/backup \
alpine tar czf /backup/postgres_data.tar.gz /data
# Redis sichern
docker compose exec -T redis redis-cli BGSAVE
docker cp "$(docker compose ps -q redis)":/data/dump.rdb "$BACKUP_DIR/"
echo "Backup abgeschlossen: $BACKUP_DIR"
Praxisbeispiele
WordPress mit Datenbank
services:
wordpress:
image: wordpress:latest
ports:
- "8080:80"
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: ${WP_DB_PASSWORD}
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress_data:/var/www/html
depends_on:
- db
restart: unless-stopped
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: ${WP_DB_PASSWORD}
volumes:
- db_data:/var/lib/mysql
restart: unless-stopped
volumes:
wordpress_data:
db_data:
Full-Stack-JavaScript-App
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
environment:
- REACT_APP_API_URL=http://api:4000
depends_on:
- api
api:
build: ./api
ports:
- "4000:4000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/app
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
db:
image: postgres:16-alpine
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: pass
POSTGRES_USER: user
POSTGRES_DB: app
redis:
image: redis:7-alpine
volumes:
- redisdata:/data
volumes:
pgdata:
redisdata:
Self-Hosted Git mit Gitea
services:
gitea:
image: gitea/gitea:latest
ports:
- "3000:3000"
- "222:22"
volumes:
- gitea_data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__database__DB_TYPE=postgres
- GITEA__database__HOST=db:5432
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=${DB_PASSWORD}
depends_on:
- db
restart: unless-stopped
db:
image: postgres:16-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_USER: gitea
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: gitea
restart: unless-stopped
volumes:
gitea_data:
postgres_data:
Best Practices
- Image-Versionen festlegen – Verwende
postgres:16-alpine, nichtpostgres:latest - .env-Dateien nutzen – Halte Secrets aus Compose-Dateien heraus (siehe unsere VPS-Sicherheitsanleitung)
- Named Volumes für Daten – Keine Bind Mounts für Datenbanken verwenden
- Überall Health Checks – Wisse, wann Services wirklich bereit sind
- Ressourcenlimits – Verhindere, dass außer Kontrolle geratene Container deinen Server lahmlegen
- Logging-Limits – Setze max-size, damit die Festplatte nicht vollläuft
- Netzwerke verwenden – Isoliere Services, die nicht miteinander kommunizieren müssen
- depends_on mit Bedingungen – Warte, bis Services gesund sind, nicht nur gestartet
Häufige Fehler vermeiden
❌ latest-Tag verwenden – Builds werden nicht mehr reproduzierbar
❌ Daten in Containern speichern – Daten verschwinden, wenn der Container entfernt wird
❌ .env-Dateien committen – Secrets landen für immer in der Git-History
❌ Keine Health Checks – depends_on wartet nicht auf die Bereitschaft der App
❌ Logs ignorieren – Ohne Limits füllen sie deine Festplatte
❌ Datenbankports nach außen öffnen – Nur das freigeben, was externen Zugriff benötigt
❌ Als root ausführen – USER-Direktive in Dockerfiles verwenden
❌ Keine Restart-Policy – Container starten nach Abstürzen nicht automatisch neu
Debugging-Tipps
# Herausfinden, warum ein Container abstürzt
docker compose logs app --tail=100
# Shell in einem laufenden Container öffnen
docker compose exec app sh
# Shell in einem gestoppten Container öffnen
docker compose run app sh
# Container-Details inspizieren
docker inspect $(docker compose ps -q app)
# Netzwerkverbindung prüfen
docker compose exec app ping db
# Umgebungsvariablen anzeigen
docker compose exec app env
Häufige Fragen
Wie viel RAM brauche ich?
Für kleine Projekte reichen in der Regel 2 GB. Jeder Container hat einen gewissen Overhead – plane etwa 100 MB pro Container plus den tatsächlichen Bedarf der App ein. Hostinger VPS-Pläne beginnen bei 4 GB, was für die meisten Stacks komfortabel ausreicht.
Soll ich Docker Compose oder Kubernetes verwenden?
Docker Compose für Single-Server-Deployments (die meisten Anwendungsfälle). Kubernetes, wenn du Multi-Node-Cluster, Auto-Scaling oder ein dediziertes DevOps-Team benötigst. Nicht unnötig verkomplizieren.
Wie aktualisiere ich eine laufende Anwendung?
# Neueste Images herunterladen
docker compose pull
# Geänderte Container neu erstellen
docker compose up -d
Für eigene Builds: docker compose up -d --build
Kann ich Docker Compose mit dem Nginx Proxy Manager verwenden?
Ja! Ports nicht direkt freigeben – einfach die Container ins gleiche Netzwerk wie NPM legen. Siehe unsere Reverse-Proxy-Anleitung.
Wie persistiere ich Daten?
Named Volumes verwenden (Docker verwaltet den Speicherort) oder Bind Mounts (du gibst den Pfad an). Named Volumes sind für Datenbanken empfohlen.
Was ist der Unterschied zwischen up und start?
up erstellt und startet Container. start startet nur bereits vorhandene, gestoppte Container. Immer up -d verwenden.
Nächste Schritte: Richte automatische Backups ein, um deine Docker-Daten zu schützen, und füge Monitoring hinzu, um den Zustand deiner Container im Blick zu behalten.
Ready to get started?
Get the best VPS hosting deal today. Hostinger offers 4GB RAM VPS starting at just $4.99/mo.
Get Hostinger VPS — $4.99/mo// up to 75% off + free domain included
// related topics
// related guides
$1 VPS Hosting 2026: Cheapest VPS Servers Starting at $1/Month
Looking for $1 VPS hosting? Compare the cheapest VPS providers starting from $1-3/month. Real specs, no hidden fees, honest reviews of budget VPS options.
tutorialCaddy Reverse Proxy Guide 2026: Automatic HTTPS Made Easy
Set up Caddy as a reverse proxy with automatic HTTPS, zero-config SSL, and simple Caddyfile syntax. Complete VPS deployment guide.
tutorialCloudflare Tunnel VPS Guide 2026: Expose Services Without Opening Ports
Set up Cloudflare Tunnel on your VPS to expose web apps securely without opening ports or revealing your server IP. Complete guide with Docker and DNS config.
tutorialCoolify VPS Setup Guide 2026: Self-Hosted Vercel Alternative
Deploy Coolify on your VPS for a self-hosted Vercel/Netlify experience. Complete setup guide with Docker, SSL, and app deployments.
Andrius Putna
I am Andrius Putna. Geek. Since early 2000 in love tinkering with web technologies. Now AI. Bridging business and technology to drive meaningful impact. Combining expertise in customer experience, technology, and business strategy to deliver valuable insights. Father, open-source contributor, investor, 2xIronman, MBA graduate.
// last updated: February 6, 2026. Disclosure: This article may contain affiliate links.