Zum Inhalt springen
Aktueller Gutschein:10 % RabattIn der Bestellung einlösen

Docker und Docker Compose auf einem VPS: Installation und Ihre erste App

Installieren Sie Docker aus dem offiziellen Repository, betreiben Sie WordPress mit MariaDB über Docker Compose und lernen Sie, Ihre Container zu verwalten, zu aktualisieren, zu sichern und per Firewall abzusichern.

  • Fortgeschrittene
  • ~25 Min.
  • Aktualisiert am 9.10.2026

Docker führt Apps in isolierten Containern aus, die alles mitbringen, was sie brauchen. Wir installieren es aus dem offiziellen Repository, betreiben mit Docker Compose WordPress mit einer Datenbank und gehen Verwaltung, Updates, Firewall und Backups durch.

Was Sie brauchen

  • Ein StormHost-VPS mit Ubuntu 24.04 oder Debian 13
  • Eine SSH-Verbindung als root (Anleitung)
  • Für WordPress mit HTTPS eine auf den VPS zeigende Domain
  • Etwa 25 Minuten

So funktioniert es#

BegriffWas es ist
ImageEine fertige App-Vorlage: Systembibliotheken, das Programm und seine Einstellungen. Es wird aus einer Registry heruntergeladen, meist Docker Hub (zum Beispiel wordpress, mariadb).
ContainerEine laufende Instanz eines Images. Er hat ein eigenes Dateisystem und Netzwerk, teilt sich aber den Kernel mit dem VPS. Einen Container können Sie jederzeit löschen und aus demselben Image neu erstellen.
VolumeDauerhafter Speicher für Daten außerhalb des Containers. Daten in einem Volume überleben das Löschen und Aktualisieren des Containers. Alles, was Sie behalten möchten, muss in einem Volume liegen.
PortEin Container hat ein eigenes Netzwerk. Damit er von außen erreichbar ist, wird sein Port auf einem Port des VPS „veröffentlicht“.
Docker ComposeEin Werkzeug, das eine ganze App aus mehreren Containern (zum Beispiel eine Website und eine Datenbank) aus einer einzigen Datei compose.yaml startet.

Installation#

Installieren Sie Docker aus dem offiziellen Repository von Docker. Das Paket docker.io aus dem Distributions-Repository ist tendenziell älter und kollidiert mit den offiziellen Paketen.

Entfernen Sie zuerst eventuell kollidierende Pakete. Auf einem frischen VPS findet der Befehl nichts, was in Ordnung ist.

Ubuntu 24.04

bash
apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc | cut -f1)

Debian 13

bash
apt remove $(dpkg --get-selections docker.io docker-compose docker-doc docker-buildx podman-docker containerd runc | cut -f1)
  • dpkg --get-selections … – listet auf, welche der genannten Pakete installiert sind.
  • | cut -f1 – behält nur die Paketnamen.
  • apt remove $(…) – deinstalliert sie. Daten in /var/lib/docker bleiben.

Fügen Sie dann das Docker-Repository hinzu und installieren Sie Docker:

Ubuntu 24.04

bash
apt update
apt -y install ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
apt update
apt -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Debian 13

bash
apt update
apt -y install ca-certificates curl
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
tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/debian
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
apt update
apt -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Was jede Zeile tut:

BefehlWas er tut
apt -y install ca-certificates curlZertifikate für HTTPS und ein Download-Werkzeug.
install -m 0755 -d /etc/apt/keyringsLegt ein Verzeichnis für die Signaturschlüssel von Repositories mit Berechtigungen 0755 an.
curl -fsSL …/gpg -o /etc/apt/keyrings/docker.ascLädt den Signaturschlüssel von Docker herunter. -f = bei HTTP-Fehlern abbrechen, -s = still, -S = Fehler trotzdem anzeigen, -L = Weiterleitungen folgen.
chmod a+r …/docker.ascJeder kann den Schlüssel lesen (apt liest ihn als anderer Benutzer).
tee /etc/apt/sources.list.d/docker.sources <<EOF … EOFSchreibt die Repository-Definition in eine Datei.
Types: debEin Repository mit Binärpaketen.
URIs:Die Repository-Adresse für Ihr System.
Suites: $(…)Der Codename des Systems (noble für Ubuntu 24.04, trixie für Debian 13), automatisch eingesetzt.
Components: stableNur stabile Releases.
Architectures: $(dpkg --print-architecture)Die CPU-Architektur (amd64).
Signed-By:Pakete müssen mit diesem Schlüssel signiert sein, sonst lehnt apt sie ab.
apt -y install docker-ce …Installiert Docker Engine (docker-ce), die Befehlszeile (docker-ce-cli), die Container-Laufzeit (containerd.io), das Bauen von Images (docker-buildx-plugin) und Compose (docker-compose-plugin).

Nach der Installation startet Docker von selbst und ist beim Booten des VPS aktiviert. Prüfen Sie die Installation:

bash
docker run --rm hello-world
docker compose version
  • docker run --rm hello-world – lädt ein Test-Image herunter und führt es aus; --rm löscht den Container nach dem Ende. Sie sehen Hello from Docker!.
  • docker compose version – gibt die Compose-Version aus, zum Beispiel Docker Compose version v5.…. Nach Version 2 sprang Compose direkt auf 5, ältere Installationen zeigen also v2.…. Die Befehle in dieser Anleitung funktionieren mit beiden.

Die Gruppe docker = Root-Rechte

Jeder in der Gruppe docker kann Container mit Zugriff auf die gesamte Festplatte des VPS starten, was praktisch Root-Rechten entspricht. Nehmen Sie nur Personen auf, denen Sie auch root geben würden. Wenn Sie als root arbeiten, brauchen Sie die Gruppe nicht.

Erste App: WordPress mit MariaDB#

Projekt und Passwörter#

  1. Legen Sie das Projektverzeichnis an und wechseln Sie hinein:

    bash
    mkdir -p /opt/wordpress
    cd /opt/wordpress

    Der Verzeichnisname (wordpress) wird zum Compose-Projektnamen und zum Präfix für Container- und Volume-Namen.

  2. Erstellen Sie mit nano .env eine Datei mit den Passwörtern. Erzeugen Sie die Passwörter mit dem Passwortgenerator (nur Buchstaben und Ziffern):

    /opt/wordpress/.envini
    DB_PASSWORD=<DATENBANK_PASSWORT>
    DB_ROOT_PASSWORD=<DATENBANK_ROOT_PASSWORT>
    • DB_PASSWORD – Passwort des WordPress-Datenbankbenutzers.
    • DB_ROOT_PASSWORD – Passwort des Benutzers root im Datenbankcontainer (für Backups und Verwaltung).
  3. Machen Sie die Datei nur für root lesbar:

    bash
    chmod 600 .env

Die Datei compose.yaml#

Erstellen Sie mit nano compose.yaml eine Datei mit diesem Inhalt:

/opt/wordpress/compose.yamlYAML
services:
  db:
    image: mariadb:lts
    restart: unless-stopped
    environment:
      MARIADB_DATABASE: wordpress
      MARIADB_USER: wordpress
      MARIADB_PASSWORD: ${DB_PASSWORD}
      MARIADB_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
    volumes:
      - db_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
      start_period: 10s
      interval: 10s
      timeout: 5s
      retries: 3

  wordpress:
    image: wordpress:latest
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    ports:
      - "127.0.0.1:8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: wordpress
      WORDPRESS_DB_PASSWORD: ${DB_PASSWORD}
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wp_data:/var/www/html

volumes:
  db_data:
  wp_data:

Zeile für Zeile:

ZeileWas sie tut
services:Die Liste der Container der App. Der Dienstname (db, wordpress) dient zugleich als Hostname im Projektnetzwerk.
image: mariadb:ltsDas offizielle MariaDB-Image; der Tag lts = das neueste Release mit Langzeitunterstützung.
restart: unless-stoppedDer Container startet nach einem Absturz und nach einem Neustart des VPS erneut, es sei denn, Sie haben ihn selbst gestoppt.
MARIADB_DATABASE, MARIADB_USER, MARIADB_PASSWORDLegt beim ersten Start die Datenbank und einen Benutzer mit Passwort an.
MARIADB_ROOT_PASSWORDDas Root-Passwort der Datenbank, beim ersten Start gesetzt.
${DB_PASSWORD}Der Wert wird aus der Datei .env im selben Verzeichnis eingesetzt, sodass die Passwörter nicht direkt in compose.yaml stehen.
volumes: - db_data:/var/lib/mysqlDatenbankdaten liegen im Volume db_data, sodass sie Container-Updates überleben.
healthcheck:Alle 10 s prüft Docker mit dem Skript healthcheck.sh aus dem MariaDB-Image, dass die Datenbank Verbindungen annimmt und initialisiert ist.
depends_on: db: condition: service_healthyWordPress startet erst, wenn die Datenbank bereit ist.
image: wordpress:latestDas offizielle WordPress-Image (Apache + PHP).
ports: - "127.0.0.1:8080:80"Port 80 des Containers ist auf Port 8080 des VPS verfügbar, aber nur vom VPS selbst (127.0.0.1). Von außen ist er nur über Nginx erreichbar.
WORDPRESS_DB_HOST: dbDie Datenbankadresse: der Dienstname db im Projektnetzwerk.
WORDPRESS_DB_USER, …_PASSWORD, …_NAMEDatenbank-Zugangsdaten; sie müssen zu den Werten des Dienstes db passen.
volumes: - wp_data:/var/www/htmlWordPress-Dateien (Plugins, Themes, hochgeladene Bilder) liegen im Volume wp_data.
volumes: am EndeDeklariert die benannten Volumes, die das Projekt verwendet.

Starten#

bash
docker compose up -d
docker compose ps
  • docker compose up -d – lädt die Images herunter, erstellt Netzwerk, Volumes und Container und startet sie im Hintergrund (-d, detached).
  • docker compose ps – listet die Container des Projekts und ihren Status auf. db zeigt healthy, wordpress zeigt Up.

Prüfung vom VPS aus: curl -I http://127.0.0.1:8080 liefert die Antwort-Header von WordPress (eine Weiterleitung zum Installer).

MARIADB_*-Variablen gelten nur beim ersten Start

Die Datenbank wird beim ersten Start mit einem leeren Volume angelegt. Eine spätere Änderung des Passworts in .env ändert es nicht in der Datenbank. Ändern Sie das Passwort in der Datenbank mit ALTER USER (siehe MariaDB auf einem VPS) und erst dann in .env.

Bereitstellung über Nginx und HTTPS#

WordPress lauscht jetzt nur auf 127.0.0.1:8080. Stellen Sie es über Nginx als Reverse Proxy nach der Anleitung Nginx und HTTPS bereit: Verwenden Sie in der Konfiguration proxy_pass http://127.0.0.1:8080; und Ihre Domain, zum Beispiel blog.example.cz, und holen Sie dann mit certbot --nginx -d blog.example.cz ein Zertifikat.

Das WordPress-Image erkennt HTTPS selbst am Header X-Forwarded-Proto, den Nginx sendet, wenn es wie in dieser Anleitung eingerichtet ist. Öffnen Sie zum Schluss https://blog.example.cz und schließen Sie die WordPress-Installation im Browser ab.

Container verwalten#

Führen Sie docker compose-Befehle im Projektverzeichnis aus (cd /opt/wordpress).

BefehlWas er tut
docker compose psStatus der Container des Projekts.
docker compose logs -fLogs aller Dienste; -f = live verfolgen (beenden mit Strg+C). Nur für einen Dienst: docker compose logs -f wordpress.
docker compose restartStartet die Container neu.
docker compose stop / startStoppt / startet die Container, ohne etwas zu löschen.
docker compose up -dErstellt nach einer Änderung an compose.yaml nur die geänderten Container neu.
docker compose downStoppt und löscht die Container und das Netzwerk. Volumes mit Daten bleiben.
docker compose exec db mariadb -uroot -pFührt einen Befehl in einem laufenden Container aus (hier die MariaDB-Konsole).
docker compose configValidiert compose.yaml und gibt es mit den eingesetzten Werten aus .env aus (Vorsicht, das zeigt auch die Passwörter).

Aktualisierung auf neue Image-Versionen:

bash
docker compose pull
docker compose up -d
docker image prune -f
  • docker compose pull – lädt neue Image-Versionen herunter.
  • docker compose up -d – erstellt Container mit neuerem Image neu. Daten in Volumes bleiben.
  • docker image prune -f – löscht alte, nicht mehr verwendete Images; -f überspringt die Bestätigung.

docker compose down -v löscht Daten

Das Flag -v von docker compose down löscht auch die Volumes des Projekts, also die Datenbank und die WordPress-Dateien. Das lässt sich nicht rückgängig machen. Verwenden Sie es nur, wenn Sie die App vollständig entfernen möchten und ein Backup haben.

Optionen für restart::

WertWann der Container erneut startet
noNie (Standard).
alwaysImmer, auch wenn Sie ihn manuell gestoppt haben (nach einem Neustart von Docker oder VPS).
on-failureNur wenn er mit einem Fehler endet.
unless-stoppedImmer, außer wenn Sie ihn selbst gestoppt haben. Empfohlen für Dienste.

Ports und die Firewall#

Docker veröffentlicht Container-Ports mit eigenen Firewall-Regeln, die UFW umgehen. Ein als 8080:80 veröffentlichter Port ist aus dem Internet erreichbar, auch wenn Sie ihn in UFW nicht erlauben. Die Anleitung Firewall UFW erklärt das ausführlich.

Eintrag in ports:Wer den Port erreichen kann
"8080:80"Jeder im Internet, unabhängig von UFW.
"127.0.0.1:8080:80"Nur Prozesse auf dem VPS (zum Beispiel Nginx). Empfohlen für Websites hinter Nginx.
kein ports:Nur andere Container im selben Projektnetzwerk (typischerweise Datenbanken).

Veröffentlichen Sie Datenbanken und andere interne Dienste gar nicht. Ports, die öffentlich sein müssen (zum Beispiel ein Gameserver in einem Container), beschränken Sie mit der Firewall im Kundenbereich (Anleitung). Sie sitzt vor dem VPS, und Docker kann sie nicht umgehen.

Speicherplatz freigeben#

Images, gestoppte Container und Logs belegen mit der Zeit viel Platz.

bash
docker system df
docker system prune
  • docker system df – wie viel Platz Images, Container, Volumes und der Cache belegen.
  • docker system prune – löscht nach Bestätigung gestoppte Container, ungenutzte Netzwerke, Images ohne Tag und den Build-Cache. Volumes mit Daten löscht es nicht, es sei denn, Sie ergänzen --volumes.

Vorsicht beim Löschen von Volumes und mit -a

docker system prune --volumes löscht auch ungenutzte anonyme Volumes (unbenannte, zum Beispiel durch docker run ohne -v erstellt). Enthielten sie Daten eines gerade gelöschten oder gestoppten Containers, sind diese Daten weg. docker volume prune -a löscht auch benannte Volumes wie wordpress_db_data, wenn kein Container sie verwendet, zum Beispiel nach docker compose down. docker system prune -a löscht alle Images, die kein Container verwendet. Lesen Sie vor der Bestätigung, was der Befehl zu löschen ankündigt.

Standardmäßig werden Container-Logs nicht rotiert und können die Festplatte füllen. Stellen Sie den Treiber local ein, der alte Logs von selbst entfernt (standardmäßig behält er höchstens 5 Dateien à 20 MB pro Container). Erstellen Sie die Datei nano /etc/docker/daemon.json mit dem Inhalt unten. Existiert die Datei bereits, überschreiben Sie sie nicht; ergänzen Sie nur die Zeile "log-driver" (Einträge durch Kommas getrennt):

/etc/docker/daemon.jsonJSON
{
  "log-driver": "local"
}

Starten Sie dann Docker neu (systemctl restart docker) und erstellen Sie die Container neu (docker compose up -d --force-recreate). Die Einstellung gilt nur für neu erstellte Container.

Backups#

Sichern Sie Volumes, nicht Container. Einen Container können Sie aus seinem Image neu erstellen, die Daten nicht.

  1. Legen Sie ein Verzeichnis für Backups an: mkdir -p /root/zalohy.

  2. Sichern Sie die Datenbank mit einem Export (konsistenter als das Kopieren von Dateien):

    bash
    cd /opt/wordpress
    docker compose exec -T db sh -c 'exec mariadb-dump --single-transaction --all-databases -uroot -p"$MARIADB_ROOT_PASSWORD"' > /root/zalohy/wp-db-$(date +%F).sql
    • docker compose exec -T db – führt einen Befehl im Container db aus; -T deaktiviert das Terminal, damit sich die Ausgabe in eine Datei umleiten lässt.
    • sh -c '…' – der Befehl läuft in der Shell des Containers, wo die Variable $MARIADB_ROOT_PASSWORD gesetzt ist.
    • mariadb-dump --single-transaction --all-databases – exportiert alle Datenbanken ohne Sperren.
    • > /root/zalohy/… – die Ausgabe wird auf dem VPS gespeichert, außerhalb des Containers.
  3. Sichern Sie die WordPress-Dateien, indem Sie das Volume archivieren:

    bash
    docker run --rm -v wordpress_wp_data:/data:ro -v /root/zalohy:/backup alpine tar czf /backup/wp-files-$(date +%F).tar.gz -C /data .
    • docker run --rm – startet einen einmaligen Container, der nach dem Ende gelöscht wird.
    • -v wordpress_wp_data:/data:ro – hängt das Volume wp_data des Projekts wordpress schreibgeschützt (ro) ein.
    • -v /root/zalohy:/backup – hängt das Backup-Verzeichnis vom VPS ein.
    • alpine tar czf … -C /data . – packt den Inhalt des Volumes mit dem kleinen Alpine-Image in ein Archiv.

    docker volume ls gibt die genauen Volume-Namen aus.

Wie Sie Backups automatisch mit cron ausführen und vom VPS wegkopieren, behandelt Backups auf einem VPS.

Überprüfung#

  • docker compose ps zeigt beide Container als laufend und die Datenbank als healthy.
  • https://blog.example.cz zeigt WordPress mit einem Schloss in der Adressleiste.
  • Nach einem Neustart des VPS (reboot) starten die Container von selbst.
  • Von einem anderen Computer können Sie sich nicht mit http://<VPS_IP_ADRESSE>:8080 verbinden (der Port ist nur an 127.0.0.1 gebunden).

Fehlerbehebung#

permission denied while trying to connect to the Docker daemon socket#

Sie sind nicht root, und Ihr Benutzer ist nicht in der Gruppe docker. Führen Sie den Befehl mit sudo aus oder melden Sie sich als root an.

Bind for 127.0.0.1:8080 failed: port is already allocated oder address already in use#

Port 8080 wird bereits von einem anderen Container (erste Meldung) oder einem anderen Programm auf dem VPS (zweite Meldung) verwendet. Finden Sie heraus, von welchem (ss -tlnp | grep 8080, docker ps), und wählen Sie in compose.yaml einen anderen Port, zum Beispiel 127.0.0.1:8081:80. Aktualisieren Sie dann auch proxy_pass in Nginx.

WordPress zeigt „Error establishing a database connection“#

Die Datenbank ist noch nicht bereit, oder die Zugangsdaten passen nicht. Prüfen Sie das Datenbank-Log (docker compose logs db). Haben Sie das Passwort in .env nach dem ersten Start geändert, hat die Datenbank noch das alte Passwort (siehe die Warnung unter Starten).

Der Container zeigt dauernd Restarting#

Die App im Container stürzt direkt nach dem Start ab. Die Ursache finden Sie im Log: docker compose logs --tail 50 <DIENSTNAME>.

toomanyrequests: You have reached your unauthenticated pull rate limit#

Docker Hub begrenzt anonyme Image-Downloads auf 100 pro 6 Stunden und IP-Adresse. Warten Sie, oder melden Sie sich mit Ihrem Docker-Hub-Konto an: docker login.

no space left on device#

Die Festplatte ist voll. Finden Sie heraus, was sie belegt (docker system df, df -h), und räumen Sie wie unter Speicherplatz freigeben beschrieben auf. Geht Ihnen der Platz tatsächlich aus, erhöhen Sie das RAM in der Registerkarte Tarif ändern; die Festplattengröße wächst mit dem Arbeitsspeicher.

Zusammenfassung

  • Installieren Sie Docker aus dem offiziellen Repository download.docker.com, nicht als Paket docker.io.
  • Daten gehören in Volumes; Container können jederzeit gelöscht und neu erstellt werden.
  • Veröffentlichen Sie Websites auf 127.0.0.1 und stellen Sie sie über Nginx mit HTTPS bereit; andernfalls umgeht Docker UFW.
  • Updates: docker compose pull und docker compose up -d.
  • docker compose down -v und prune --volumes löschen Daten; sichern Sie Ihre Volumes regelmäßig.

War diese Anleitung hilfreich?

Gilt für

  • Ubuntu 24.04
  • Debian 13
  • Docker Engine
  • Docker Compose v2 und neuer

Dienste

Fehler in der Anleitung melden

Anleitungen durchsuchen