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#
| Begriff | Was es ist |
|---|---|
| Image | Eine fertige App-Vorlage: Systembibliotheken, das Programm und seine Einstellungen. Es wird aus einer Registry heruntergeladen, meist Docker Hub (zum Beispiel wordpress, mariadb). |
| Container | Eine 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. |
| Volume | Dauerhafter 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. |
| Port | Ein Container hat ein eigenes Netzwerk. Damit er von außen erreichbar ist, wird sein Port auf einem Port des VPS „veröffentlicht“. |
| Docker Compose | Ein 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
apt remove $(dpkg --get-selections docker.io docker-compose docker-compose-v2 docker-doc docker-buildx podman-docker containerd runc | cut -f1)Debian 13
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/dockerbleiben.
Fügen Sie dann das Docker-Repository hinzu und installieren Sie Docker:
Ubuntu 24.04
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-pluginDebian 13
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-pluginWas jede Zeile tut:
| Befehl | Was er tut |
|---|---|
apt -y install ca-certificates curl | Zertifikate für HTTPS und ein Download-Werkzeug. |
install -m 0755 -d /etc/apt/keyrings | Legt ein Verzeichnis für die Signaturschlüssel von Repositories mit Berechtigungen 0755 an. |
curl -fsSL …/gpg -o /etc/ | Lä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.asc | Jeder kann den Schlüssel lesen (apt liest ihn als anderer Benutzer). |
tee /etc/ | Schreibt die Repository-Definition in eine Datei. |
Types: deb | Ein 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: stable | Nur 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:
docker run --rm hello-world
docker compose versiondocker run --rm hello-world– lädt ein Test-Image herunter und führt es aus;--rmlöscht den Container nach dem Ende. Sie sehenHello from Docker!.docker compose version– gibt die Compose-Version aus, zum BeispielDocker Compose version v5.…. Nach Version 2 sprang Compose direkt auf 5, ältere Installationen zeigen alsov2.…. 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#
Legen Sie das Projektverzeichnis an und wechseln Sie hinein:
bash mkdir -p /opt/wordpress cd /opt/wordpressDer Verzeichnisname (
wordpress) wird zum Compose-Projektnamen und zum Präfix für Container- und Volume-Namen.Erstellen Sie mit
nano .enveine Datei mit den Passwörtern. Erzeugen Sie die Passwörter mit dem Passwortgenerator (nur Buchstaben und Ziffern):/opt/ wordpress/ ini.env 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).
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:
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:
| Zeile | Was sie tut |
|---|---|
services: | Die Liste der Container der App. Der Dienstname (db, wordpress) dient zugleich als Hostname im Projektnetzwerk. |
image: mariadb:lts | Das offizielle MariaDB-Image; der Tag lts = das neueste Release mit Langzeitunterstützung. |
restart: unless-stopped | Der 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_PASSWORD | Legt beim ersten Start die Datenbank und einen Benutzer mit Passwort an. |
MARIADB_ROOT_PASSWORD | Das 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/mysql | Datenbankdaten 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_healthy | WordPress startet erst, wenn die Datenbank bereit ist. |
image: wordpress:latest | Das 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: db | Die Datenbankadresse: der Dienstname db im Projektnetzwerk. |
WORDPRESS_DB_USER, …_PASSWORD, …_NAME | Datenbank-Zugangsdaten; sie müssen zu den Werten des Dienstes db passen. |
volumes: - wp_data:/var/www/html | WordPress-Dateien (Plugins, Themes, hochgeladene Bilder) liegen im Volume wp_data. |
volumes: am Ende | Deklariert die benannten Volumes, die das Projekt verwendet. |
Starten#
docker compose up -d
docker compose psdocker 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.dbzeigthealthy,wordpresszeigtUp.
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).
| Befehl | Was er tut |
|---|---|
docker compose ps | Status der Container des Projekts. |
docker compose logs -f | Logs aller Dienste; -f = live verfolgen (beenden mit Strg+C). Nur für einen Dienst: docker compose logs -f wordpress. |
docker compose restart | Startet die Container neu. |
docker compose stop / start | Stoppt / startet die Container, ohne etwas zu löschen. |
docker compose up -d | Erstellt nach einer Änderung an compose.yaml nur die geänderten Container neu. |
docker compose down | Stoppt und löscht die Container und das Netzwerk. Volumes mit Daten bleiben. |
docker compose exec db mariadb -uroot -p | Führt einen Befehl in einem laufenden Container aus (hier die MariaDB-Konsole). |
docker compose config | Validiert compose.yaml und gibt es mit den eingesetzten Werten aus .env aus (Vorsicht, das zeigt auch die Passwörter). |
Aktualisierung auf neue Image-Versionen:
docker compose pull
docker compose up -d
docker image prune -fdocker 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::
| Wert | Wann der Container erneut startet |
|---|---|
no | Nie (Standard). |
always | Immer, auch wenn Sie ihn manuell gestoppt haben (nach einem Neustart von Docker oder VPS). |
on-failure | Nur wenn er mit einem Fehler endet. |
unless-stopped | Immer, 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.
docker system df
docker system prunedocker 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):
{
"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.
Legen Sie ein Verzeichnis für Backups an:
mkdir -p /root/zalohy.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).sqldocker compose exec -T db– führt einen Befehl im Containerdbaus;-Tdeaktiviert 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_PASSWORDgesetzt 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.
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_– hängt das Volumewp_ data:/ data: ro wp_datades Projektswordpressschreibgeschü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 lsgibt 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 pszeigt beide Container als laufend und die Datenbank alshealthy.https://blog.example.czzeigt 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://verbinden (der Port ist nur an<VPS_ IP_ ADRESSE>: 8080 127.0.0.1gebunden).
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 Paketdocker.io. - Daten gehören in Volumes; Container können jederzeit gelöscht und neu erstellt werden.
- Veröffentlichen Sie Websites auf
127.0.0.1und stellen Sie sie über Nginx mit HTTPS bereit; andernfalls umgeht Docker UFW. - Updates:
docker compose pullunddocker compose up -d. docker compose down -vundprune --volumeslöschen Daten; sichern Sie Ihre Volumes regelmäßig.
Weiterführende Links#
- Docker: Install Docker Engine on Ubuntu (öffnet sich in einem neuen Fenster)
- Docker: Install Docker Engine on Debian (öffnet sich in einem neuen Fenster)
- Docker: Paketfilterung und Firewalls (öffnet sich in einem neuen Fenster)
- Docker: Port-Veröffentlichung (öffnet sich in einem neuen Fenster)
- Docker: Compose-Dateireferenz (öffnet sich in einem neuen Fenster)
- Docker Hub: wordpress (öffnet sich in einem neuen Fenster)
- Docker Hub: mariadb (öffnet sich in einem neuen Fenster)