Docker Compose beschreibt in einer YAML-Datei (compose.yaml), aus welchen Containern eine Anwendung besteht, und startet, aktualisiert oder beendet diese mit einem einzigen Befehl. Für eine WordPress-Seite sind beispielsweise zwei Container erforderlich (der Webserver mit PHP und die Datenbank), zusammen mit den dazugehörigen Volumes, Passwörtern und Netzwerkkonfigurationen. Anstelle langer docker run-Befehle ist all dies in einer einzigen Datei zusammengefasst, die Sie auch in einem Versionskontrollsystem speichern können; wenn Sie diese auf einen anderen Server kopieren, erhalten Sie dieselbe Umgebung.
Compose ist ein typisches Tool für einen eigenen KVM-VPS: Auf diese Weise können mehrere eigenständige Anwendungen (beispielsweise WordPress, n8n oder eine eigene Webanwendung) nebeneinander ausgeführt werden, wobei jede in ihrem eigenen Verzeichnis mit ihren eigenen Einstellungen läuft.
Wir haben die Schritte im November 2026 auf einem Ubuntu-24.04-System getestet, wobei wir die Docker Engine 29.9.3 und Docker Compose 5.7.0 aus dem offiziellen Docker-Repository installiert haben. Die Befehle funktionieren auf AlmaLinux und Debian auf dieselbe Weise.
Installation: docker compose oder docker-compose?
Compose ist heute ein Plugin für das Docker-Befehlszeilentool; der Befehl lautet daher docker compose mit einem Leerzeichen. Das alte, in Python geschriebene docker-compose (mit Bindestrich) war die Version 1.x, die im Jahr 2023 eingestellt wurde; in älteren Anleitungen können Sie noch darauf stoßen. Die Befehle und das Dateiformat sind nahezu identisch; lediglich der Bindestrich muss durch ein Leerzeichen ersetzt werden.
Das Compose-Plugin wird bei der Installation von Docker zusammen mit dem Paket docker-compose-plugin installiert (siehe: Installation von Docker unter Ubuntu, Debian und AlmaLinux). Überprüfung:
docker compose version
Docker Compose version v5.7.0
Nach der 2.x-Serie sprang die Versionsnummer auf 5; die Befehle und das Format der Datei compose.yaml blieben unverändert. Wenn der Befehl docker compose die Antwort „unknown command“ zurückgibt, fehlt das Plugin: Installieren Sie das Paket docker-compose-plugin aus dem Docker-Repository. Unter Ubuntu schlägt das System bei Eingabe des Befehls docker-compose das Paket apt install docker-compose vor: Dabei handelt es sich um die veraltete Version 1.29 – installieren Sie diese bitte nicht.
In den Beispielen dieses Artikels ist der Benutzer Mitglied der Gruppe docker, weshalb die Befehle ohne sudo angegeben sind. Falls Sie kein Mitglied dieser Gruppe sind, fügen Sie bitte sudo vor den Befehlen ein.
Aufbau des Projekts
Jede Anwendung sollte einen eigenen Ordner erhalten, der die Dateien compose.yaml und .env enthält:
~/wordpress/
├── compose.yaml # Beschreibung der Dienste
└── .env # Passwörter und Einstellungen
Compose verwendet den Namen des Ordners als Projektnamen: Die Namen der Container, Volumes und Netzwerke beginnen damit (wordpress-db-1, wordpress_db_data). Wenn Sie einen anderen Namen wünschen, fügen Sie am Anfang der Datei die Zeile name: sajatnev ein.
Der Dateiname lautet gemäß der aktuellen Empfehlung compose.yaml (auch compose.yml ist zulässig). Der alte Name docker-compose.yml funktioniert weiterhin. Befinden sich beide Dateien im selben Ordner, gibt Compose eine Warnung aus („Found multiple config files“) und verwendet die Datei compose.yaml; benennen Sie daher die alte Datei um oder löschen Sie sie.
Beispiel: WordPress und MariaDB
Die folgende compose.yaml-Datei startet eine WordPress-Seite und eine MariaDB-Datenbank:
services:
db:
image: mariadb:11.8
restart: unless-stopped
environment:
MARIADB_DATABASE: wordpress
MARIADB_USER: wordpress
MARIADB_PASSWORD: ${DB_PASSWORD:?DB_PASSWORD in der .env-Datei angeben}
MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:?Add meg a DB_ROOT_PASSWORD-ot}
volumes:
- db_data:/var/lib/mysql
networks:
- backend
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
wordpress:
image: wordpress:${WP_VERSION:-latest}
restart: unless-stopped
depends_on:
db:
condition: service_healthy
environment:
WORDPRESS_DB_HOST: db
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
ports:
- "127.0.0.1:${WP_PORT:-8080}:80"
volumes:
- wp_data:/var/www/html
networks:
- backend
volumes:
db_data:
wp_data:
networks:
backend:
Die dazugehörige .env-Datei:
DB_PASSWORD=Valtoztassa-meg-1
DB_ROOT_PASSWORD=Valtoztassa-meg-2
WP_PORT=8080
Ändern Sie die Passwörter in lange, zufällige Zeichenfolgen (z. B. openssl rand -base64 24) und stellen Sie sicher, dass nur Sie die Datei lesen können:
chmod 600 .env
Am Anfang der Datei fehlt die Zeile version:. Der in den alten Anleitungen enthaltene Schlüssel version: "3.8" und ähnliche Angaben werden von Compose nicht mehr verwendet; wenn er in der Datei verbleibt, gab der Test folgende Warnung aus: „the attribute version is obsolete, it will be ignored“. Sie können ihn getrost löschen.
Was bedeuten die einzelnen Teile?
services: die Container. Der Name des Dienstes (db,wordpress) ist gleichzeitig auch der Netzwerkname: WordPress greift unter dem Namendbauf die Datenbank zu (WORDPRESS_DB_HOST: db), ohne IP-Adresse.image: das verwendete Image und sein Tag. Legen Sie bei der Datenbank die Hauptversion fest (mariadb:11.8), damit ein Update nicht unerwartet eine neue Hauptversion mit sich bringt.restart: unless-stopped: Der Container startet nach einem Stopp oder einem Neustart des Servers automatisch neu, es sei denn, Sie haben ihn manuell angehalten. Im Test starteten WordPress und MariaDB nach einem Neustart automatisch neu, während der zuvor mitdocker compose stopangehaltene Container jedoch weiterhin angehalten blieb. Weitere Optionen:no(Standard),always,on-failure.volumes: Der Speicherort der Daten. Die benannten Volumesdb_dataundwp_datawerden von Docker verwaltet (unter/var/lib/docker/volumes/). Sie bleiben auch nach dem Löschen des Containers erhalten, sodass die Daten bei einer Aktualisierung nicht verloren gehen.ports:127.0.0.1:8080:80verbindet den Port 80 des Containers mit dem Port 8080 des Servers, jedoch nur unter der lokalen Adresse. Von außen ist der Container somit nicht erreichbar, sondern nur über einen vorgeschalteten Reverse-Proxy, siehe unten.networks: Dasbackend-Netzwerk ist ein eigenes Netzwerk für die beiden Container. Ohne Angabe eines Netzwerks legt Compose automatisch eindefault-Netzwerk für das Projekt an; ein benanntes Netzwerk ist dann sinnvoll, wenn Sie mehrere Netzwerke verwenden möchten (siehe Abschnitt über den Proxy).healthcheckunddepends_on: WordPress startet erst, wenn die Datenbank bereits reagiert (condition: service_healthy). Das Skripthealthcheck.shist im offiziellen Image von MariaDB enthalten. Beim Test wurde die Datenbank beim ersten Start nach etwa 40 Sekunden alshealthyeingestuft; erst danach startete WordPress.
Die Reihenfolge der depends_on-Angaben gilt nur für den Befehl docker compose up. Beim Neustart des Servers startet Docker die Container gemäß der restart-Regel und wartet nicht auf Abhängigkeiten: Im Test wurden die beiden Container nach dem Neustart innerhalb derselben Sekunde gestartet. Die Anwendung muss daher damit zurechtkommen, dass die Datenbank in den ersten Sekunden noch nicht verfügbar ist.
Variablen und die .env-Datei
Die Verweise auf ${...} werden von Compose aus der Datei .env im Projektverzeichnis (sowie aus den Umgebungsvariablen der Befehlszeile) ausgefüllt, noch bevor die Container gestartet werden:
| Schreibweise | Bedeutung |
|---|---|
${DB_PASSWORD} |
Der Wert der Variablen; falls nicht angegeben, wird ein leerer Text angezeigt (mit einer Warnung) |
${WP_PORT:-8080} |
Standardwert: Wenn die Variable leer ist oder fehlt, 8080 |
${DB_PASSWORD:?üzenet} |
Erforderlich: Fehlt diese Angabe, bricht Compose mit einer Fehlermeldung ab. |
Die obligatorische Variable schützt vor einer Datenbank, die mit einem leeren Passwort gestartet wird. Im Test ohne die Datei .env hat Compose das Projekt gar nicht erst gestartet:
error while interpolating services.db.environment.MARIADB_PASSWORD: required variable DB_PASSWORD is missing a value: DB_PASSWORD in der .env-Datei angeben
Eine in der Befehlszeile angegebene Variable hat Vorrang vor der .env-Datei: WP_PORT=8090 docker compose up -d verwendet den Port 8090.
Verwechseln Sie die Datei .env nicht mit dem Schlüssel env_file:, der bei den Diensten angegeben werden kann. Die Werte aus der Datei .env werden an den Stellen ${...} in der Datei compose.yaml eingesetzt, während env_file: app.env die dort aufgeführten Variablen direkt als Umgebungsvariablen an den Container übergibt. Wenn eine Anwendung viele Einstellungen hat, ist die Verwendung von env_file übersichtlicher, als alle unter environment aufzulisten.
Überprüfung und Start
Bitte überprüfen Sie die Datei vor dem Start:
cd ~/wordpress
docker compose config -q
Wenn kein Fehler vorliegt, wird nichts ausgegeben. Ohne die Option -q wird die vollständige, ausgefüllte Konfiguration angezeigt, einschließlich der anstelle der Variablen eingetragenen Werte: So sehen Sie, was tatsächlich in die Container geschrieben wird. Bitte beachten Sie, dass auch Passwörter lesbar angezeigt werden; kopieren Sie diese Ausgabe daher nicht in ein Forum oder in ein Support-Ticket.
Start im Hintergrund:
docker compose up -d
Beim ersten Start lädt Compose die Images herunter, erstellt die Volumes und das Netzwerk und startet anschließend die Container. Ohne das -d-Argument läuft es im Vordergrund und zeigt die Protokolle aller Container im Terminal an; in diesem Fall werden die Container mit Strg+C beendet.
Alltägliche Befehle
Führen Sie jeden Befehl im Projektverzeichnis aus (oder geben Sie die Datei mit dem Schalter -f ~/wordpress/compose.yaml an).
| Befehl | Wirkung |
|---|---|
docker compose ps |
Die Container des Projekts, deren Status und Ports |
docker compose ls |
Alle auf dem Server ausgeführten Compose-Projekte |
docker compose logs -f |
Die Protokolle aller Container, fortlaufend (Beenden: Strg+C) |
docker compose logs --tail 50 wordpress |
Die letzten 50 Zeilen eines Dienstes |
docker compose exec db bash |
Befehlszeile im laufenden Container |
docker compose restart wordpress |
Neustart eines Dienstes |
docker compose stop |
Beenden; die Container bleiben erhalten |
docker compose start |
Starten von angehaltenen Containern |
docker compose down |
Beenden und Löschen der Container sowie des Netzwerks; die Volumes bleiben erhalten |
docker compose down -v |
Ähnlich wie beim down, werden jedoch auch die Volumes, d. h. die Daten, gelöscht |
Die Ausgabe von ps ist umfangreich; die wesentlichen Spalten können Sie wie folgt abfragen:
docker compose ps --format "table {{.Service}}\t{{.Status}}\t{{.Ports}}"
Im Protokoll steht am Anfang jeder Zeile der Name des Containers, farblich hervorgehoben:
Einige nützliche Beispiele für den Befehl exec:
# MariaDB-Kommandozeile mit dem in .env angegebenen Passwort
docker compose exec db mariadb -u wordpress -p wordpress
# PHP-Version im WordPress-Container
docker compose exec wordpress php -v
Fügen Sie bei der Ausführung über ein Skript oder Cron den Schalter -T hinzu (docker compose exec -T ...), da dort kein Terminal verfügbar ist.
Die Gefahr des Befehls „down -v“
Der Befehl docker compose down löscht lediglich die Container und das Netzwerk; der anschließende Befehl up -d startet mit denselben Volumes, d. h. mit denselben Daten. Der Befehl down -v löscht hingegen auch die benannten Volumes des Projekts und damit auch die Datenbank sowie die hochgeladenen Dateien. Im Test verschwand das Volume unmittelbar nach dem Befehl („Volume proba_adat Removed“) ohne Bestätigungsaufforderung. Verwenden Sie diesen Befehl nur, wenn Sie tatsächlich das gesamte Projekt und dessen Daten entfernen möchten, und erstellen Sie zuvor eine Sicherungskopie.
Aktualisierung
Die Aktualisierung der Images erfolgt über zwei Befehle:
docker compose pull
docker compose up -d
Der Befehl pull lädt die neuesten Images der Tags (z. B. wordpress:latest, mariadb:11.8) herunter, während up -d nur jene Container neu erstellt, deren Image oder Konfiguration sich geändert hat; die übrigen laufen weiter. Die Daten in den Volumes bleiben erhalten. Auf dieselbe Weise wendet der Befehl up -d auch Änderungen an der Datei compose.yaml oder .env an: Im Test wurde nach der Änderung von WP_PORT lediglich der WordPress-Container auf dem neuen Port neu gestartet.
Die alten Images verbleiben auf der Festplatte. Löschen Sie diese von Zeit zu Zeit. Wie dies funktioniert und wie Sie die Festplattennutzung überprüfen können, erfahren Sie im Artikel Docker-Images, Container und Volumes löschen.
Erstellen Sie vor einem Wechsel der Hauptversion (beispielsweise von MariaDB 11.8 auf eine neuere Version) stets eine Sicherungskopie und lesen Sie die Beschreibung des Images auf Docker Hub: Einige Datenbanken können nicht automatisch auf die neue Hauptversion umgestellt werden.
Erstellen eines eigenen Images
Verfügt die Anwendung über ein eigenes Dockerfile, gibt der Schlüssel build anstelle des image vor, woher Compose die Anwendung erstellen soll:
services:
web:
build: ./app
ports:
- "127.0.0.1:8081:80"
docker compose build # nur bauen
docker compose up -d --build # bauen und starten
Bitte beachten Sie: Der einfache Befehl docker compose up -d erstellt das Image nicht neu, wenn es bereits vorhanden ist. Im Test lieferte der Befehl up -d nach einer Änderung der Quelldatei weiterhin den alten Inhalt aus; erst der Befehl up -d --build übernahm den neuen Inhalt.
Reverse-Proxy: Mehrere Anwendungen auf einem Server
In diesem Beispiel ist WordPress nur unter der Adresse 127.0.0.1:8080 erreichbar. Die Ports 80 und 443 werden von einem Reverse-Proxy entgegengenommen, der anhand der in der Anfrage enthaltenen Domain entscheidet, an welchen Container die Anfrage weitergeleitet werden soll, und der auch das SSL-Zertifikat verwaltet. Ein Beispiel hierfür ist der über eine Weboberfläche verwaltbare Nginx Proxy Manager, dessen Einrichtung im Artikel Installation des Nginx Proxy Managers mit Docker beschrieben wird.
Wenn der Proxy in Docker läuft, muss der Port nicht einmal veröffentlicht werden: Es reicht aus, wenn sich der Web-Container im selben Netzwerk wie der Proxy befindet. Richten Sie das gemeinsame Netzwerk einmalig ein:
docker network create proxy
Entfernen Sie anschließend in der Datei compose.yaml den Abschnitt ports für WordPress und fügen Sie WordPress auch dem gemeinsamen Netzwerk hinzu:
wordpress:
# ... die übrigen Einstellungen unverändert, ohne den Abschnitt ports
networks:
- backend
- proxy
networks:
backend:
proxy:
external: true
external: true bedeutet, dass das Netzwerk weder von diesem Projekt erstellt noch bei einem down gelöscht wird. Die Datenbank sollte ausschließlich im backend-Netzwerk verbleiben: Im Test konnten die Namen wordpress und wordpress-wordpress-1 vom Proxy-Container aus aufgelöst werden, der Name db jedoch nicht, sodass die Datenbank auch über den Proxy nicht erreichbar ist.
Docker und die Firewall
Wenn Sie den Port ohne Adresse angeben (z. B. "8080:80"), öffnet Docker ihn an allen Netzwerkadressen mit seinen eigenen Firewall-Regeln. Diese haben Vorrang vor den Regeln von ufw und firewalld, sodass der Port von außen möglicherweise auch dann erreichbar ist, wenn Sie ihn in der Firewall nicht freigegeben haben (wir haben dies in unserem Artikel über SQL Server mit firewalld getestet: SQL Server Express unter Linux). Verbinden Sie daher jeden Port, der nur innerhalb des Servers oder von einem Proxy genutzt wird, mit dem Präfix 127.0.0.1:. Welche Dienste derzeit auf von außen erreichbaren Adressen laufen, können Sie gemäß dem Artikel Offene Ports auflisten: ss und lsof überprüfen; Informationen zur Konfiguration der Firewall finden Sie im Artikel Wie richte ich eine Firewall auf meinem VPS ein?.
Backup
Compose erstellt keine Sicherungen; Sie müssen den Inhalt der Volumes selbst sichern. Dazu gibt es zwei Möglichkeiten:
Datenbanksicherung während des Betriebs
Für Datenbanken ist das eigene Sicherungsprogramm die sicherste Lösung, da es auch vom laufenden Server aus konsistente Sicherungen erstellt:
cd ~/wordpress
set -a; . ./.env; set +a
docker compose exec -T db mariadb-dump -u root -p"$DB_ROOT_PASSWORD" --single-transaction wordpress > wordpress-$(date +%F).sql
Wiederherstellung in eine leere Datenbank:
docker compose exec -T db mariadb -u root -p"$DB_ROOT_PASSWORD" wordpress < wordpress-2026-11-08.sql
Sichern eines Volumes mit tar und einem Hilfscontainer
Die Volumes, die die Dateien enthalten, werden von einem kurzlebigen Hilfscontainer verpackt, der sowohl das Volume als auch den aktuellen Ordner eingebunden bekommt. Beenden Sie zuvor den Dienst, der in das Volume schreibt, damit sich die Dateien während der Sicherung nicht ändern:
cd ~/wordpress
docker compose stop wordpress
docker run --rm -v wordpress_wp_data:/data:ro -v "$PWD":/backup alpine \
tar czf /backup/wp_data-$(date +%F).tar.gz -C /data .
docker compose start wordpress
Der vollständige Name des Volumes besteht aus dem Projektnamen und dem Namen des Volumes, verbunden durch einen Unterstrich (wordpress_wp_data); dies wird vom Befehl docker volume ls ausgegeben. Der Eigentümer der Sicherungsdatei ist „root“, da der Hilfscontainer als „root“ ausgeführt wird.
Wiederherstellung: Löschen Sie das alte Volume, erstellen Sie es mit „Compose“ neu (mit create werden die Container und Volumes ohne Startvorgang erstellt), entpacken Sie die Sicherung darin und starten Sie anschließend das Projekt:
docker compose down
docker volume rm wordpress_wp_data
docker compose create
docker run --rm -v wordpress_wp_data:/data -v "$PWD":/backup:ro alpine \
tar xzf /backup/wp_data-2026-11-08.tar.gz -C /data
docker compose up -d
Im Test wurden die wiederhergestellten WordPress-Dateien zusammen mit ihrem Eigentümer (www-data) wiederhergestellt, und die Seite reagierte unmittelbar nach der Wiederherstellung.
Speichern Sie zusätzlich die Dateien compose.yaml und .env und kopieren Sie die Sicherungen regelmäßig auf einen anderen Rechner (siehe: Dateien zwischen Servern kopieren). Für die zeitgesteuerte Ausführung bietet der Artikel Crontab-Syntax und zeitgesteuerte Aufgaben unter Linux Hilfestellung.
Häufige Fehler
- „no configuration file provided: not found“: Sie haben den Befehl nicht im Projektordner ausgeführt, oder der Dateiname weicht von den üblichen Bezeichnungen ab. Wechseln Sie in den Ordner oder geben Sie die Datei mit dem Schalter
-fan. - „required variable … is missing a value“: Die Datei
.envfehlt, befindet sich in einem anderen Ordner oder die Variable ist darin nicht enthalten. Die Datei.envmuss sich neben der Dateicompose.yamlbefinden. - „port is already allocated“ oder „address already in use“: Der Port wird auf dem Server bereits anderweitig verwendet (von einem anderen Container oder einem nativen Webserver). Wählen Sie einen anderen Port oder beenden Sie den anderen Dienst.
- „permission denied while trying to connect to the docker API“: Der Benutzer ist kein Mitglied der Gruppe
dockeroder hat sich seit der Aufnahme in die Gruppe nicht erneut angemeldet. Verwenden Sie den Befehlsudooder melden Sie sich ab und wieder an. - Der Container startet immer wieder neu (Status
Restartinginps): Überprüfen Sie das Protokoll (docker compose logs --tail 50 <Dienst>); meist liegt eine fehlende Variable, ein falsches Passwort oder ein Berechtigungsfehler vor. Informationen zur systemweiten Protokollierung finden Sie im Artikel Protokoll-Dateien lesen: journalctl und /var/log.
Verwandte Artikel
- Wenn Sie eine Anwendung mit Compose in einer Produktionsumgebung ausführen möchten, sehen Sie sich das Beispiel zu n8n an: Schritt-für-Schritt-Anleitung zur Installation von n8n auf einem VPS.
- Falls Sie Ihre Compose-Projekte lieber über einen Browser verwalten möchten, finden Sie im Artikel Docker-Web-Management-Interfaces: Portainer, Dockge und Coolify entsprechende Tools.

