Docker-Update-Strategie: Container-Updates ohne Ausfallzeit auf einem VPS
Vier aufeinander aufbauende Methoden zum Aktualisieren von Docker-Containern auf einem VPS, vom einfachen Pull-and-Replace bis zum Blue-Green-Deployment ohne Ausfallzeit mit Traefik. Image-Pinning, Rollback, Diun-Benachrichtigungen und docker-rollout.
Docker-Container auf einem VPS zu aktualisieren muss keine Ausfallzeit bedeuten. Der richtige Ansatz hängt davon ab, was Sie betreiben und wie viel Unterbrechung Sie tolerieren können. Ein persönlicher Blog verträgt ein paar Sekunden Ausfallzeit während eines docker compose up -d. Ein SaaS-Produkt mit zahlenden Kunden nicht.
Diese Anleitung behandelt vier Methoden, von der einfachsten bis zur widerstandsfähigsten. Jede baut auf der vorherigen auf. Beginnen Sie mit der Methode, die zu Ihrer Situation passt, und steigen Sie auf die nächste Stufe um, wenn es nötig wird.
Voraussetzungen: Ein VPS mit Debian 12 oder Ubuntu 24.04 mit Docker Engine 27+ und Docker Compose v2. Alle Befehle verwenden die docker compose-Plugin-Syntax (nicht das veraltete docker-compose-v1-Binary). Docker in Produktion auf einem VPS: Was schiefgeht und wie Sie es beheben
Wie pinne ich Docker-Images auf eine bestimmte Version?
Pinnen Sie Ihre Images auf eine bestimmte Minor- oder Patch-Version in Ihrer Compose-Datei. Der latest-Tag ist ein bewegliches Ziel, das ohne Vorwarnung Breaking Changes einführen kann. Pinning gibt Ihnen Kontrolle darüber, wann Updates stattfinden, und macht Rollbacks möglich, indem das vorherige Image lokal erhalten bleibt.
Verschiedene Tag-Strategien bergen unterschiedliche Risiken:
| Tag-Format | Beispiel | Risikoniveau | Update-Verhalten |
|---|---|---|---|
latest |
nginx:latest |
Hoch | Beliebige Version, jederzeit. Sie können nicht erkennen, was sich geändert hat. |
| Nur Major | nginx:1 |
Mittel-hoch | Kann von 1.25 auf 1.27 springen. Minor-Versionen können das Verhalten ändern. |
| Minor | nginx:1.27 |
Niedrig | Erhält Patch-Updates (1.27.0 auf 1.27.3). Sicher für die meisten Workloads. |
| Patch | nginx:1.27.3 |
Sehr niedrig | Exakte Version. Keine überraschenden Updates. Sie aktualisieren manuell. |
| Digest | nginx:1.27.3@sha256:6f12... |
Minimal | Byte-identisches Image jedes Mal. Immun gegen Tag-Mutation. |
Für die meisten Produktionsdienste pinnen Sie auf die Minor-Version (image: postgres:16.6). Das ist die richtige Balance zwischen Sicherheitspatches und Stabilität. Für Dienste, bei denen Reproduzierbarkeit zählt (CI, regulierte Umgebungen), pinnen Sie auf den vollständigen Digest.
services:
app:
image: myapp:2.4.1
# Not: image: myapp:latest
db:
image: postgres:16.6
Notieren Sie die aktuellen Image-Digests vor dem Update. Sie brauchen sie für ein Rollback:
docker image inspect --format='{{index .RepoDigests 0}}' $(docker compose images app -q)
myapp@sha256:a1b2c3d4e5f6...
Wie richte ich Health Checks in Docker Compose ein?
Health Checks teilen Docker mit, ob Ihr Container tatsächlich funktioniert, nicht nur ob er läuft. Alle Zero-Downtime-Muster hängen davon ab. Ohne Health Check hat Docker keine Möglichkeit zu wissen, ob der neue Container bereit ist, bevor der alte entfernt wird.
Fügen Sie einen healthcheck-Block zu jedem Service in Ihrer Compose-Datei hinzu. Der test-Befehl wird im angegebenen Intervall innerhalb des Containers ausgeführt. Docker markiert den Container erst als healthy, wenn der Test bestanden ist.
services:
app:
image: myapp:2.4.1
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
Was jedes Feld bewirkt:
- test: Der auszuführende Befehl.
CMDführt ihn direkt aus. Verwenden SieCMD-SHELL, wenn Sie Shell-Funktionen wie Pipes benötigen. - interval: Zeit zwischen den Prüfungen. 15s ist angemessen für Webdienste.
- timeout: Wie lange auf den Abschluss des Befehls gewartet wird, bevor er als fehlgeschlagen gilt.
- retries: Anzahl aufeinanderfolgender Fehler, bevor Docker den Container als
unhealthymarkiert. - start_period: Karenzzeit nach dem Container-Start. Health Checks in diesem Zeitfenster zählen nicht zum Fehlerschwellenwert. Setzen Sie den Wert lang genug für den Start Ihrer Anwendung.
Für Dienste, die curl nicht installiert haben, verwenden Sie den vom Dienst angebotenen eingebauten Check:
db:
image: postgres:16.6
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myuser -d mydb"]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
cache:
image: redis:7.4
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
Prüfen Sie nach dem Start Ihrer Dienste, ob die Health Checks bestanden werden:
docker compose ps
NAME IMAGE STATUS PORTS
app myapp:2.4.1 Up 2 minutes (healthy) 0.0.0.0:8080->8080/tcp
db postgres:16.6 Up 2 minutes (healthy) 5432/tcp
Der Status (healthy) bedeutet, dass Ihr Health Check konfiguriert ist und besteht. Wenn Sie (health: starting) sehen, befindet sich der Container noch in seiner start_period. Bei (unhealthy) prüfen Sie die Health-Check-Logs:
docker inspect --format='{{json .State.Health}}' $(docker compose ps -q app) | python3 -m json.tool
Wie aktualisiere ich einen Docker-Container auf meinem VPS?
Führen Sie docker compose pull aus, um das neue Image zu holen, dann docker compose up -d, um den Container zu ersetzen. Docker Compose stoppt den alten Container, entfernt ihn und startet einen neuen aus dem aktualisierten Image. Das verursacht eine kurze Unterbrechung (typischerweise 2-10 Sekunden), während der neue Container startet und seinen Health Check besteht.
Schritt für Schritt: das einfache Update
Sichern Sie vor jedem Update Ihre Volumes. Ein fehlgeschlagenes Update mit beschädigten Daten ist weitaus schlimmer als ein paar Sekunden Ausfallzeit.
Lesen Sie das Changelog der neuen Version. Prüfen Sie auf Breaking Changes, veraltete Konfigurationsoptionen und erforderliche Migrationsschritte. Das dauert fünf Minuten und spart Stunden beim Debugging.
# Pull the new image
docker compose pull app
# Check what changed
docker compose up -d --dry-run
Das --dry-run-Flag (Docker Compose v2.20+) zeigt, was Compose tun wird, ohne es tatsächlich auszuführen. Sie sehen, welche Container neu erstellt werden:
DRY RUN MODE - service "app" - Pull
DRY RUN MODE - Container app-1 - Recreate
DRY RUN MODE - Container app-1 - Started
Wenden Sie das Update an:
docker compose up -d app
[+] Running 1/1
✔ Container app-1 Started 0.8s
Prüfen Sie, ob der neue Container healthy ist:
docker compose ps app
NAME IMAGE STATUS PORTS
app myapp:2.5.0 Up 15 seconds (healthy) 0.0.0.0:8080->8080/tcp
Testen Sie dann von außerhalb des Servers, ob der Dienst erreichbar ist:
curl -s -o /dev/null -w "%{http_code}" https://app.example.com/health
200
Wann sollten Sie Ihre Docker-Container aktualisieren?
Nicht alle Updates haben die gleiche Dringlichkeit. Ein einheitlicher Update-Zeitplan führt entweder zu unnötigem Risiko oder zu verpassten Sicherheitspatches.
- Sicherheitspatches (CVEs): Sofort anwenden. Abonnieren Sie die Sicherheitshinweise Ihrer Images. Ein bekannter CVE in einem öffentlich zugänglichen Container wird innerhalb von Stunden nach der Veröffentlichung ausgenutzt, nicht Tagen.
- Patch-Versionen (z. B. 2.4.1 auf 2.4.2): Wöchentlich oder zweiwöchentlich einplanen. Das sind Bugfixes. Changelog lesen, aktualisieren, prüfen.
- Minor-Versionen (z. B. 2.4 auf 2.5): Monatlich einplanen. Zuerst in einer Staging-Umgebung testen, falls vorhanden. Changelog auf Verhaltensänderungen prüfen.
- Major-Versionen (z. B. 2.x auf 3.x): Planen und testen. Major-Versionen verursachen Breaking Changes. Migrationsanleitung lesen. Auf einem separaten VPS oder lokal testen, bevor Sie die Produktion anfassen.
Wie mache ich ein Rollback eines Docker-Containers auf ein vorheriges Image?
Docker Compose hat keinen eingebauten Rollback-Befehl. Zum Zurücksetzen: Bearbeiten Sie Ihre Compose-Datei, um den vorherigen Image-Tag oder Digest zu pinnen, und führen Sie dann docker compose up -d aus. Der Container startet mit dem alten Image neu. Das funktioniert nur, wenn Sie das alte Image lokal behalten haben (führen Sie nicht docker image prune direkt nach dem Update aus).
Rollback Schritt für Schritt
Angenommen, Sie haben myapp von 2.4.1 auf 2.5.0 aktualisiert und die neue Version ist fehlerhaft.
- Prüfen Sie, ob das alte Image noch lokal verfügbar ist:
docker images myapp
REPOSITORY TAG IMAGE ID CREATED SIZE
myapp 2.5.0 abc123def456 2 hours ago 185MB
myapp 2.4.1 789fed654cba 2 weeks ago 182MB
- Bearbeiten Sie Ihre Compose-Datei, um die vorherige Version zu pinnen:
services:
app:
image: myapp:2.4.1
- Führen Sie das Rollback durch:
docker compose up -d app
[+] Running 1/1
✔ Container app-1 Started 0.7s
- Prüfen Sie, ob das Rollback funktioniert hat:
docker compose ps app
NAME IMAGE STATUS PORTS
app myapp:2.4.1 Up 10 seconds (healthy) 0.0.0.0:8080->8080/tcp
Wenn Sie das alte Image bereits bereinigt haben, lädt Docker es erneut aus der Registry herunter (vorausgesetzt, der Tag existiert noch). Für maximale Sicherheit notieren Sie den vollständigen Digest (sha256:...) vor dem Update. Digests sind unveränderlich. Tags können überschrieben werden.
Automatisierung mit einem Pre-Update-Skript
Speichern Sie den aktuellen Zustand vor jedem Update, damit ein Rollback immer nur einen Befehl entfernt ist:
#!/bin/bash
# save-state.sh - Run before every update
COMPOSE_FILE="${1:-docker-compose.yml}"
DATE=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR="./rollback/${DATE}"
mkdir -p "${BACKUP_DIR}"
cp "${COMPOSE_FILE}" "${BACKUP_DIR}/"
docker compose ps --format json > "${BACKUP_DIR}/containers.json"
docker compose images --format json > "${BACKUP_DIR}/images.json"
echo "State saved to ${BACKUP_DIR}"
chmod 700 save-state.sh
Wird Watchtower 2026 noch gepflegt?
Watchtower wurde am 17. Dezember 2025 archiviert. Die Maintainer verwenden Docker nicht mehr und haben die Entwicklung eingestellt. Die letzte Version ist v1.7.1. Noch wichtiger: Watchtowers Docker SDK verwendet API v1.25, aber Docker Engine 29 hat die minimale API-Version auf v1.44 angehoben. Watchtower ist mit aktuellen Docker-Versionen inkompatibel, es sei denn, Sie senken das API-Minimum manuell mit DOCKER_MIN_API_VERSION=1.25 in Ihrer Daemon-Konfiguration. Das ist ein Workaround, keine Lösung.
Wenn Sie Watchtower heute nutzen, planen Sie die Migration. Für automatisierte Update-Benachrichtigungen ohne automatische Neustarts verwenden Sie Diun. Für automatisierte Updates ohne Ausfallzeit verwenden Sie docker-rollout hinter einem Reverse Proxy.
Wie benachrichtigt Diun Sie über Docker-Image-Updates?
Diun (Docker Image Update Notifier) überwacht Ihre Docker-Registries und sendet Benachrichtigungen, wenn neue Image-Versionen verfügbar sind. Es aktualisiert keine Container. Es informiert Sie, dass ein Update existiert, damit Sie das Changelog lesen und nach eigenem Ermessen aktualisieren können. Das ist der Ansatz „erst informieren, dann handeln".
Fügen Sie Diun zu Ihrer bestehenden Compose-Datei hinzu oder erstellen Sie eine eigene:
services:
diun:
image: crazymax/diun:4
command: serve
volumes:
- "diun-data:/data"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
environment:
TZ: "Europe/Berlin"
DIUN_WATCH_WORKERS: "10"
DIUN_WATCH_SCHEDULE: "0 6 * * *"
DIUN_PROVIDERS_DOCKER: "true"
DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT: "true"
DIUN_NOTIF_SLACK_WEBHOOKURL_FILE: "/run/secrets/slack_webhook"
secrets:
- slack_webhook
restart: unless-stopped
secrets:
slack_webhook:
file: ./secrets/slack_webhook.txt
volumes:
diun-data:
Die Slack-Webhook-URL gehört in eine Secrets-Datei, nicht in eine Umgebungsvariable, da Docker Secrets sie aus der docker inspect-Ausgabe und Prozesslistings heraushält. Erstellen Sie die Secrets-Datei mit eingeschränkten Berechtigungen:
mkdir -p secrets
echo "https://hooks.slack.com/services/YOUR/WEBHOOK/URL" > secrets/slack_webhook.txt
chmod 600 secrets/slack_webhook.txt
Die wichtigsten Einstellungen:
- DIUN_WATCH_SCHEDULE: Cron-Ausdruck.
0 6 * * *prüft täglich um 06:00 Uhr. Passen Sie dies an Ihr Wartungsfenster an. - DIUN_PROVIDERS_DOCKER_WATCHBYDEFAULT: Bei
trueüberwacht Diun alle laufenden Container. Setzen Sie es auffalseund verwenden Sie Labels für selektive Überwachung. - Docker-Socket-Mount: Nur lesend (
:ro), da Diun nur Container-Metadaten liest. Es startet oder stoppt niemals Container.
Für selektive Überwachung (empfohlen für größere Stacks) setzen Sie WATCHBYDEFAULT auf false und fügen Sie Labels zu den Containern hinzu, die Sie überwachen möchten:
services:
app:
image: myapp:2.4.1
labels:
- "diun.enable=true"
- "diun.watch_repo=true"
Starten Sie Diun und prüfen Sie die Logs:
docker compose up -d diun
docker compose logs diun --tail 20
diun | Thu, 19 Mar 2026 06:00:01 CET INF Starting Diun version=v4.31.0
diun | Thu, 19 Mar 2026 06:00:01 CET INF Configuration loaded from 5 environment variable(s)
diun | Thu, 19 Mar 2026 06:00:02 CET INF Cron triggered
diun | Thu, 19 Mar 2026 06:00:03 CET INF New image found image=docker.io/myapp:2.5.0 provider=docker
Wenn Diun ein neues Image findet, sendet es eine Slack-Nachricht mit dem Image-Namen, dem aktuellen Tag und dem neuen Tag. Sie entscheiden, ob und wann Sie aktualisieren.
Wie erreicht docker-rollout Updates ohne Ausfallzeit?
docker-rollout ist ein Docker-CLI-Plugin, das Blue-Green-Deployments für Compose-Services durchführt. Es startet einen neuen Container aus dem aktualisierten Image, wartet auf das Bestehen des Health Checks und entfernt dann den alten Container. Der Traffic erreicht nie einen nicht funktionsfähigen Container, da der Reverse Proxy nur zu gesunden Containern routet.
Voraussetzungen:
- Ein Reverse Proxy (Traefik, Caddy oder nginx-proxy), der den Traffic zu Ihrem Service routet
- Health Checks in Ihrer Compose-Datei definiert
- Keine
container_name-Direktive am Service (docker-rollout verwaltet Container-Namen) - Kein direktes
ports-Mapping am Service (der Reverse Proxy übernimmt die Port-Exposition)
docker-rollout installieren
mkdir -p /usr/local/lib/docker/cli-plugins
curl -fsSL https://raw.githubusercontent.com/wowu/docker-rollout/main/docker-rollout \
-o /usr/local/lib/docker/cli-plugins/docker-rollout
chmod +x /usr/local/lib/docker/cli-plugins/docker-rollout
Nach der Installation sollte die Version angezeigt werden:
docker rollout --version
docker-rollout version v0.13
Beispiel: Traefik + docker-rollout
Eine minimale Compose-Datei für eine Webanwendung hinter Traefik mit Health Checks. Die App hat weder ports noch container_name, da docker-rollout die Skalierung verwalten muss.
services:
traefik:
image: traefik:3.3
command:
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
restart: unless-stopped
app:
image: myapp:2.4.1
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`app.example.com`)"
- "traefik.http.routers.app.entrypoints=web"
- "traefik.http.services.app.loadbalancer.server.port=8080"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
restart: unless-stopped
Deployment ohne Ausfallzeit
Holen Sie das neue Image und verwenden Sie docker rollout statt docker compose up -d:
docker compose pull app
docker rollout app
==> Scaling 'app' to '2' instances
Container myproject-app-2 Creating
Container myproject-app-2 Created
Container myproject-app-2 Starting
Container myproject-app-2 Started
==> Waiting for new containers to be healthy (timeout: 60 seconds)
==> Stopping and removing old containers
Während dieses Vorgangs erkennt Traefik den neuen Container über den Docker-Socket, routet den Traffic zu ihm, sobald er healthy ist, und stoppt das Routing zum alten Container, bevor dieser entfernt wird. Ihre Benutzer bemerken keine Unterbrechung.
Wenn der neue Container seinen Health Check nicht besteht, bricht docker-rollout ab und der alte Container läuft weiter. Kein manuelles Eingreifen nötig.
Was ist Blue-Green-Deployment mit Docker und Traefik?
Beim Blue-Green-Deployment laufen zwei Kopien Ihres Services (Blue und Green). Eine bedient den Live-Traffic, während die andere inaktiv bleibt. Zum Deployen aktualisieren Sie die inaktive Kopie, überprüfen ihre Funktion und leiten dann den Traffic um. Das gibt Ihnen sofortiges Rollback durch Zurückschalten auf die vorherige Kopie.
Das ist das Konzept hinter docker-rollout, aber Sie können es für mehr Kontrolle manuell implementieren. Ein minimales Beispiel mit dynamischer Traefik-Konfiguration:
services:
traefik:
image: traefik:3.3
command:
- "--providers.file.directory=/etc/traefik/dynamic"
- "--providers.file.watch=true"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
volumes:
- "./traefik/dynamic:/etc/traefik/dynamic:ro"
restart: unless-stopped
app-blue:
image: myapp:2.4.1
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
app-green:
image: myapp:2.4.1
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 15s
timeout: 5s
retries: 3
start_period: 30s
Eine dynamische Traefik-Konfigurationsdatei steuert, welche Kopie den Traffic erhält:
# traefik/dynamic/app.yml
http:
routers:
app:
rule: "Host(`app.example.com`)"
service: app
entryPoints:
- web
services:
app:
loadBalancer:
servers:
- url: "http://app-blue:8080"
Zum Deployen: Aktualisieren Sie das Image von app-green, starten Sie es, warten Sie bis es healthy ist, und bearbeiten Sie dann app.yml, um auf app-green zu zeigen. Traefik übernimmt die Änderung automatisch, da watch=true. Für ein Rollback bearbeiten Sie die Datei so, dass sie wieder auf app-blue zeigt.
Dieser Ansatz bedeutet mehr Aufwand als docker-rollout. Verwenden Sie ihn, wenn Sie explizite Kontrolle über den Wechsel benötigen, Smoke Tests gegen die neue Version durchführen möchten, bevor Sie den Traffic umleiten, oder wenn mehrere Services gleichzeitig wechseln müssen.
Welche Update-Methode sollte ich verwenden?
Wählen Sie die Methode, die zu Ihrer Ausfallzeittoleranz und Infrastrukturkomplexität passt.
| Methode | Ausfallzeit | Komplexität | Rollback | Reverse Proxy erforderlich | Geeignet für |
|---|---|---|---|---|---|
docker compose pull + up |
2-10 Sekunden | Niedrig | Manuell (Compose-Datei bearbeiten) | Nein | Persönliche Projekte, interne Tools |
| Diun + manuelles Update | Wie oben | Niedrig | Wie oben | Nein | Teams, die vor dem Update informiert sein wollen |
| docker-rollout | Keine | Mittel | Automatisch (bricht bei Fehler ab) | Ja | Produktionsdienste auf einem einzelnen VPS |
| Blue-Green (manuell) | Keine | Hoch | Sofort (Konfigurationsdatei umschalten) | Ja | Multi-Service-Stacks, regulierte Umgebungen |
Entscheidungsbaum:
- Sind 2-10 Sekunden Ausfallzeit akzeptabel? Verwenden Sie
docker compose pull && docker compose up -d. - Möchten Sie über Updates informiert werden, bevor Sie sie anwenden? Fügen Sie Diun hinzu.
- Ist Zero Downtime erforderlich? Haben Sie einen Reverse Proxy? Verwenden Sie docker-rollout.
- Brauchen Sie explizite Kontrolle über den Traffic-Wechsel? Implementieren Sie Blue-Green manuell.
Etwas funktioniert nicht?
Container startet, zeigt aber (unhealthy)
Prüfen Sie den Health-Check-Befehl. Führen Sie ihn manuell im Container aus:
docker compose exec app curl -f http://localhost:8080/health
Wenn das fehlschlägt, liegt das Problem in Ihrer Anwendung, nicht in Docker. Prüfen Sie die Anwendungslogs:
docker compose logs app --tail 50
Altes Image wurde bereinigt, Rollback nicht möglich
Wenn der Tag in der Registry noch existiert, lädt docker compose pull es erneut herunter. Wenn Sie per Digest gepinnt haben, lädt Docker das exakte Image unabhängig von Tag-Änderungen:
image: myapp:2.4.1@sha256:789fed654cba...
docker-rollout hängt beim Deployment
Der Health Check wird innerhalb des Timeouts nicht bestanden. Prüfen Sie Intervall und Retries des Health Checks. Erhöhen Sie das Timeout:
docker rollout -t 120 app
Watchtower funktioniert nach Docker-Update nicht mehr
Docker Engine 29 erfordert mindestens API v1.44. Watchtower verwendet API v1.25. Migrieren Sie zu Diun für Benachrichtigungen oder docker-rollout für automatisierte Updates ohne Ausfallzeit.
Diun erkennt keine neuen Images
Prüfen Sie den Cron-Zeitplan in DIUN_WATCH_SCHEDULE. Lösen Sie einen manuellen Scan aus:
docker compose exec diun diun image list
Prüfen Sie die Diun-Logs auf Registry-Authentifizierungsfehler:
docker compose logs diun --tail 30
Bereit, es selbst auszuprobieren?
Stellen Sie Ihren eigenen Server in Sekunden bereit. Linux, Windows oder FreeBSD. →