Docker auf dem VPS: Was ändert sich wirklich?
Docker auf einem VPS nutzt dieselbe Engine, aber weniger Spielraum: RAM läuft aus, Ports umgehen UFW, Container starten nach Reboots nicht und Docker füllt die Platte.
Was sich ändert, wenn Sie Docker auf einem VPS ausführen
Docker auf einem VPS verwendet dieselbe Engine und dieselben Images wie Docker auf Ihrem Laptop. Daher funktionieren alle Befehle, die Sie bereits kennen, weiterhin. Die Umgebung ändert sich. Ein Laptop verfügt über freien Arbeitsspeicher, eine Firewall, die niemand scannt, und ein ausreichend großes Laufwerk, sodass Sie dessen Belegung nie prüfen. Ein gemieteter Server hat eine feste Arbeitsspeichergrenze, eine öffentliche IP-Adresse, die innerhalb weniger Minuten nach dem Start gescannt wird, und ein Root-Dateisystem, das Docker ohne Rückfrage füllt.
Vier Unterschiede verursachen auf einem kleinen Server die meisten Probleme:
- Der Arbeitsspeicher ist begrenzt. Bei einem Engpass beendet der Kernel einen Prozess.
- Ein veröffentlichter Port wird direkt durch UFW (uncomplicated firewall) geleitet, weil Docker eigene Firewall-Regeln schreibt.
- Container werden nach einem Reboot nicht automatisch wieder gestartet, wenn Sie dies nicht vorher festgelegt haben.
- Images, Container, Volumes und der Build-Cache wachsen, bis der Datenträger voll ist.
Jeder Abschnitt unten nennt den Fehler, die tatsächlich angezeigte Meldung und die Anleitung, die das Problem ausführlich behebt. Wenn Sie noch keine Compose-Datei geschrieben haben, lesen Sie zuerst Docker-Compose-Grundlagen auf einem VPS und kehren Sie anschließend hierher zurück. Diese Seite setzt voraus, dass Sie einen Stack bereits starten können.
Wie viel RAM verwendet ein Docker-Container?
Weniger, als die meisten erwarten. Ein Container ist ein Prozess in einer cgroup (control group) und keine virtuelle Maschine. Daher gibt es weder einen Gastkernel noch eine feste Speicherzuweisung. Entscheidend ist, auf welche Ressourcen der Prozess im Container tatsächlich zugreift. Deshalb passt ein vollständiger Stack in 2 GB, während derselbe Stack aus virtuellen Maschinen dafür nicht ausreichen würde.
Die folgenden Werte sind typische Leerlaufwerte für Standard-Images unter Ubuntu 24.04 mit der Standardkonfiguration. Sie wurden wenige Minuten nach dem Start aus docker stats abgelesen. Die Werte dienen als Ausgangspunkt für die Planung und sind kein Benchmark für Ihre Arbeitslast. Führen Sie docker stats --no-stream auf Ihrem eigenen System aus, bevor Sie einem Wert vertrauen, auch diesen nicht.
The data behind this chart
[
{
"label": "nginx",
"idle_mb": 8,
"budget_mb": 64
},
{
"label": "Redis 7",
"idle_mb": 12,
"budget_mb": 128
},
{
"label": "Traefik v3",
"idle_mb": 40,
"budget_mb": 128
},
{
"label": "PostgreSQL 16",
"idle_mb": 45,
"budget_mb": 512
},
{
"label": "Uptime Kuma",
"idle_mb": 95,
"budget_mb": 256
},
{
"label": "MariaDB 11",
"idle_mb": 190,
"budget_mb": 512
},
{
"label": "Nextcloud (Apache)",
"idle_mb": 210,
"budget_mb": 768
}
]Die beiden Spalten erfüllen unterschiedliche Zwecke. idle_mb zeigt, wie viel Speicher der Container im Leerlauf verwendet. budget_mb zeigt, wie viel Speicher Sie bei der Planung reservieren sollten, weil die reale Nutzung nicht dem Leerlauf entspricht. PostgreSQL benötigt im Leerlauf etwa 45 MB und 512 MB, sobald Verbindungen, Sortiervorgänge und der Cache aktiv genutzt werden. Planen Sie mit der Budget-Spalte. Verwenden Sie die Leerlauf-Spalte zur Fehlersuche.
Beachten Sie die Verteilung dieser 7 Zeilen. nginx benötigt im Leerlauf 8 MB und Nextcloud 210 MB. Der Proxy vor Ihren Anwendungen benötigt nahezu keinen zusätzlichen Speicher. Sie müssen den Server vor allem für die Datenbank und die PHP-Anwendung dimensionieren.
Beachten Sie bei docker stats: Der Speicherwert enthält auch den Page Cache, den die Dateizugriffe des Containers eingelesen haben. Daher steigt der Wert nach dem Start zunächst eine Weile an und stabilisiert sich anschließend. Überwachen Sie den Wert eine Stunde lang, bevor Sie von einem Speicherleck ausgehen.
VPS-Größenplanung: Was in 2 GB, 4 GB und 8 GB passt
Ziehen Sie den Anteil des Hosts zuerst ab. Kernel, systemd, journald, sshd und der Docker-Daemon verwenden denselben Arbeitsspeicher wie Ihre Container. dockerd und containerd belegen davon etwa 100 MB. Außerdem benötigen Sie freien Speicher für den Page Cache und für die Lastspitze, wenn ein Image erstellt oder ein Datenbank-Dump ausgeführt wird.
The data behind this chart
[
{
"plan": "2 GB VPS",
"total_mb": 2048,
"host_reserve_mb": 768,
"container_mb": 1280
},
{
"plan": "4 GB VPS",
"total_mb": 4096,
"host_reserve_mb": 1024,
"container_mb": 3072
},
{
"plan": "8 GB VPS",
"total_mb": 8192,
"host_reserve_mb": 1536,
"container_mb": 6656
}
]host_reserve_mb deckt das Betriebssystem, den Docker-Daemon und den Puffer ab, der den Server auch unter Last reaktionsfähig hält. Was übrig bleibt, ist container_mb. Nur diesen Wert können Sie tatsächlich verplanen. Der Puffer wächst mit dem Tarif: von 768 MB beim kleinsten Server auf 1536 MB beim größten. Ein größerer Server führt mehr Container aus, schreibt mehr Logs und benötigt mehr Page Cache.
Ein Tarif mit 2 GB lässt 1280 MB für Container übrig. Wenn Sie davon 512 MB für PostgreSQL und 128 MB für Traefik einplanen, ist bereits die Hälfte verbraucht. Der Rest reicht für zwei kleine Anwendungen mit jeweils etwa 256 MB. Das ist ein realer und brauchbarer Server. Für Nextcloud und zusätzlich einen Suchcluster reicht der Speicher nicht aus.
Ein Tarif mit 4 GB lässt 3072 MB übrig. Darin finden eine Datenbank, ein Reverse Proxy, drei Anwendungen und ein Monitoring-Container gleichzeitig Platz. Das ist die kleinste sinnvolle Größe für alles, was Ihnen wichtig ist, weil der freie Speicher einen fehlerhaften Deploy abfängt.
Ein Tarif mit 8 GB lässt 6656 MB von insgesamt 8192 MB übrig. Die Begrenzung liegt dann normalerweise nicht mehr beim Arbeitsspeicher, sondern bei der CPU oder beim Datendurchsatz. Wenn die Berechnung zeigt, dass Ihr Stack nicht hineinpasst, wählen Sie den größeren Tarif, statt die Konfiguration darum herum zu optimieren: was ein VPS tatsächlich kostet erklärt, welchen Wert die zusätzlichen Gigabytes pro Monat haben.
Zwei Regeln halten die Berechnung realistisch. Setzen Sie für jeden Dienst ein Speicherlimit, damit ein außer Kontrolle geratener Prozess nicht den gesamten Server beeinträchtigt. Lassen Sie außerdem den oberen Teil des Budgets unverplant, weil docker compose build und pg_dump im ungünstigsten Moment beide Arbeitsspeicher benötigen. Speicherlimits in Docker Compose enthält die Syntax und die typischen Fehler.
Warum beendet sich mein Container mit Code 137?
Weil der Kernel ihn beendet hat. 137 entspricht 128 plus 9, und Signal 9 ist SIGKILL. Der Container hat mehr Speicher angefordert, als ihm zugewiesen war, und der Out-of-Memory-(OOM)-Killer hat ihn beendet.
CONTAINER ID IMAGE STATUS
9f2c1a4d7b3e postgres:16 Exited (137) 4 minutes agoBestätigen Sie die Ursache, statt zu raten:
docker inspect my-db | grep -i oomkilled
sudo journalctl -k | grep -i "out of memory""OOMKilled": true bedeutet, dass der Container sein eigenes cgroup-Limit erreicht hat. Das Kernel-Log nennt den ausgewählten Prozess:
Memory cgroup out of memory: Killed process 2417 (postgres) total-vm:1284416kBDas ist der gute Fall, weil der Schaden auf einen Container begrenzt blieb. Der schlechte Fall ist ein Container ohne Limit. Ohne Limit entspricht seine Obergrenze dem gesamten Arbeitsspeicher des Rechners. Ein Speicherleck in einem Dienst kann dann den Host auslasten. Anschließend wählt der Kernel im gesamten System einen Prozess als Opfer aus, meist anhand seiner Größe. In der Logzeile fehlt das Präfix Memory cgroup, und sie lautet Out of memory: Killed process 2417 (postgres). Der ausgewählte Prozess ist häufig Ihre Datenbank, während der Container mit dem Speicherleck weiterläuft. Deshalb ist ein Limit für jeden Dienst wichtiger als der genaue Wert eines einzelnen Limits.
Swap verändert den Zeitpunkt, nicht die Berechnung. Die meisten VPS-Images werden ohne Swap ausgeliefert. Prüfen Sie dies mit swapon --show. Wenn kein Swap vorhanden ist, gibt der Befehl überhaupt keine Ausgabe aus. Eine Swap-Datei bietet dem Kernel Platz für selten benötigte Speicherseiten. Dadurch gewinnen Sie Zeit, um das Problem zu erkennen.
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -hfree -h sollte jetzt in der Zeile Swap einen Wert ungleich 0 anzeigen. Swap erweitert den Arbeitsspeicher nicht. Ein Rechner unter dauerhaftem Speicherdruck wird so langsam, dass Sie sich möglicherweise nicht mehr per SSH anmelden können, um das Problem zu beheben. Betrachten Sie Swap daher als Puffer für Alarme und passen Sie die Speicherzuweisung an.
Warum blockiert UFW meinen veröffentlichten Docker-Port nicht?
Weil der Datenverkehr die Kette, die UFW überwacht, nie erreicht. Wenn Sie einen Port mit -p 5432:5432 oder einem Compose-ports:-Eintrag veröffentlichen, schreibt der Daemon eine DNAT-Regel (Destination Network Address Translation) in die nat-Tabelle und eine Accept-Regel in seine eigene DOCKER-Kette. Ein an einen Container gerichtetes Paket wird an diesen Container weitergeleitet, statt an den Host zugestellt zu werden. Daher wird es im FORWARD-Pfad verarbeitet und durchläuft nie die INPUT-Regeln, die UFW schreibt.
Sie können dies auf dem Server beobachten:
sudo ufw status | grep 5432
sudo iptables -t nat -L DOCKER -n | grep 5432UFW kann 5432 DENY IN Anywhere ausgeben, während die nat-Tabelle eine DNAT tcp ... to:172.18.0.2:5432-Regel für denselben Port enthält. Von einem anderen Rechner aus stellt nc -vz your.server.ip 5432 weiterhin eine Verbindung her. Die Datenbank ist aus dem öffentlichen Internet erreichbar, obwohl die Firewall etwas anderes anzeigt.
Die Lösung besteht darin, weniger zu veröffentlichen. Container in einem Compose-Projekt teilen sich ein Netzwerk und erreichen sich über den Servicenamen. Eine Datenbank, die nur die daneben laufende Anwendung bedient, benötigt daher überhaupt keinen ports:-Eintrag. Wenn Sie lokalen Zugriff benötigen, binden Sie die Veröffentlichung an das Loopback-Interface:
services:
db:
image: postgres:16
ports:
- "127.0.0.1:5432:5432"Nach docker compose up -d schlägt nc -vz your.server.ip 5432 von außen fehl, während psql -h 127.0.0.1 -p 5432 auf dem Host weiterhin funktioniert. In einem überschaubaren Stack veröffentlicht normalerweise nur der Reverse Proxy etwas, und zwar auf 80 und 443. Warum veröffentlichte Docker-Ports UFW umgehen behandelt die DOCKER-USER-Kette für Fälle, in denen Sie einen Port veröffentlichen und trotzdem filtern müssen. UFW-Grundlagen zur Firewall behandelt die darunterliegenden Host-Regeln.
Warum sind meine Container nach einem Reboot verschwunden?
Weil nichts festgelegt hat, dass sie wieder gestartet werden. Ein Container wird mit der Restart-Richtlinie no erstellt, sofern Sie keine andere Richtlinie festlegen. Nach einem Reboot bleibt er daher gestoppt, und der Daemon kümmert sich nicht darum. Reboots sind auf einem VPS nicht selten: Kernel-Updates durch unbeaufsichtigte Upgrades, Wartungsarbeiten des Providers und die oben beschriebene OOM-Sequenz führen alle zu einem Reboot.
Zwei Bedingungen müssen erfüllt sein. Der Daemon muss beim Booten gestartet werden:
systemctl is-enabled dockerBei einer unveränderten Ubuntu-Installation wird damit enabled ausgegeben. Danach benötigt jeder Dienst eine Restart-Richtlinie:
services:
app:
image: ghcr.io/example/app:1.4
restart: unless-stoppedunless-stopped startet den Container nach einem Reboot erneut und berücksichtigt einen Container, den Sie absichtlich gestoppt haben. always startet dagegen auch Container erneut, die Sie absichtlich gestoppt haben, sobald der Daemon neu gestartet wird. Das kann beim Debugging unerwartet sein. Das Bearbeiten der Datei allein reicht nicht aus, weil die Restart-Richtlinie beim Erstellen des Containers gesetzt wird. Führen Sie docker compose up -d aus, damit der Container neu erstellt wird, und prüfen Sie anschließend den aktuellen Wert:
docker inspect my-app | grep -A3 RestartPolicyStarten Sie den Server anschließend absichtlich neu und führen Sie docker compose ps im Projektverzeichnis aus. Wenn ein Stack einen geplanten Reboot übersteht, übersteht er auch einen ungeplanten Reboot. Wenn Ihr Stack eine garantierte Startreihenfolge oder einen einmaligen Job beim Booten benötigt, ist eine systemd-Unit das geeignetere Werkzeug: Docker Compose beim Booten starten enthält die Unit-Datei. Damit Sie feststellen können, ob ein zurückgekehrter Container tatsächlich Anfragen verarbeitet, fügen Sie Compose-Healthchecks hinzu.
Warum ist der Speicherplatz meiner VPS voll?
Weil Docker alles behält, bis Sie es ausdrücklich entfernen. Jeder Image-Tag, den Sie jemals abgerufen haben, jeder angehaltene Container, jedes anonyme Volume, das nach einer Neuerstellung zurückgeblieben ist, und jede Schicht des Build-Cache bleiben auf der Festplatte. Bei einem Root-Dateisystem mit 40 GB oder 80 GB, was bei diesen Tarifgrößen üblich ist, führt das innerhalb von Monaten statt Jahren zu einem Ausfall.
Ein volles Dateisystem sieht nicht wie ein Absturz aus. Sie erhalten innerhalb derselben Stunde no space left on device von einem Container, apt, Meldungen von journald und docker pull. PostgreSQL nimmt keine Schreibvorgänge mehr an. Der Server ist weiterhin erreichbar. Dadurch wird das Problem schwerer erkannt als eine Reboot-Schleife.
Prüfen Sie den Zustand, bevor Sie etwas löschen:
docker system df
df -h /docker system df teilt den Gesamtverbrauch in Images, Container, lokale Volumes und Build-Cache auf. Daneben steht jeweils eine RECLAIMABLE-Spalte. Auf einem Server, der seine Images selbst erstellt, ist der Build-Cache normalerweise der größte Posten.
docker image prune -a
docker builder prune
docker system dfdocker image prune -a entfernt jedes Image, das von keinem Container verwendet wird. docker builder prune leert den Build-Cache. Beides ist während des laufenden Betriebs sicher, weil verwendete Daten übersprungen werden. Nicht sicher ist docker system prune --volumes. Dieser Befehl löscht jedes Volume, auf das derzeit kein Container verweist. Ein Stack, den Sie für das Wochenende angehalten haben, entspricht genau diesem Fall. Sein Datenbank-Volume wird ebenfalls gelöscht. Lesen Sie Bind-Mounts und benannte Volumes vor der Eingabe dieses Flags. Erstellen Sie außerdem zuerst ein Backup.
Container-Logs wachsen langsamer, können aber ebenfalls viel Speicherplatz belegen. Der standardmäßige json-file-Treiber hat keine Größenbegrenzung. Ein Container mit vielen Meldungen schreibt daher Gigabytes in /var/lib/docker/containers. Begrenzen Sie die Größe für jeden Container in /etc/docker/daemon.json:
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}Wenden Sie die Änderung mit sudo systemctl restart docker an. Dadurch werden Ihre Container neu gestartet. Wählen Sie den Zeitpunkt daher entsprechend. Die Begrenzung gilt für Container, die nach der Änderung erstellt werden. Erstellen Sie die laufenden Container mit docker compose up -d --force-recreate neu und prüfen Sie die Einstellung:
docker inspect my-app | grep -A5 LogConfig
sudo du -sh /var/lib/docker/containersDie Ausgabe von inspect sollte zeigen, dass max-size gesetzt ist. Wenn der Wert leer ist, wurde dieser Container vor der Änderung erstellt. Er schreibt weiterhin ohne Größenbegrenzung.
Gewohnheiten, die einen kleinen Docker-Host stabil halten
Dafür brauchen Sie weder ein Dashboard noch ein Tool, in das Sie sich erst einarbeiten müssen.
- Führen Sie am ersten Tag jedes Monats
docker system dfunddf -h /aus. Zwei Befehle, dreißig Sekunden, und Sie erkennen den Trend lange bevor daraus ein Ausfall wird. - Legen Sie für jeden Dienst ein Speicherlimit fest, auch für Dienste, die Ihrer Einschätzung nach klein bleiben. Das Limit macht aus einem hostweiten Ausfall einen einzelnen Container, der neu gestartet wird.
- Überwachen Sie den Host von einem anderen System aus, damit Sie über Speicher- oder Festplattendruck informiert werden, bevor der Kernel eingreift. Uptime Kuma läuft in einem Container und benötigt im Leerlauf etwa 95 MB.
- Sichern Sie Volumes, nicht Container. Der Container ist ersetzbar, das Volume nicht. restic-Sicherungen auf einem VPS decken einen Zeitplan und einen Wiederherstellungstest ab.
- Fixieren Sie Image-Tags in der Compose-Datei und aktualisieren Sie sie an einem von Ihnen festgelegten Tag. Mit
latestentspricht die Version, die Sie vom nächstendocker compose pullerhalten, dem Stand, der an diesem Morgen veröffentlicht wurde.
Ein kleiner VPS mit Docker bleibt über Jahre stabil, wenn vier Werte im vorgesehenen Bereich bleiben: das Speicherbudget, die Liste der veröffentlichten Ports, die Restart-Policy jedes Dienstes und der freie Speicherplatz. Alles andere ist dasselbe Docker, das Sie bereits zu Hause betreiben.
FAQ
Wie viel RAM benötige ich, um Docker auf einem VPS zu betreiben?
Docker selbst benötigt wenig Ressourcen. Der Daemon und containerd belegen zusammen etwa 100 MB. Der restliche Bedarf entfällt auf Ihre Container. Reservieren Sie zuerst den Anteil des Hosts: 768 MB auf einem System mit 2048 MB für das Betriebssystem, den Daemon und eine Reserve. Damit bleiben 1280 MB für die Container. Eine Datenbank mit 512 MB, ein Reverse Proxy mit 128 MB und zwei kleine Anwendungen passen in dieses Budget. Messen Sie den Speicherbedarf Ihres eigenen Stacks mit docker stats --no-stream, statt veröffentlichten Angaben zu vertrauen.
Kann ich Docker auf einem VPS mit 1 GB betreiben?
Ja, für einen oder zwei leichte Container. Legen Sie vorher eine Swap-Datei an. Auf einem System mit 1 GB ist ungefähr die Hälfte belegt, sobald das Betriebssystem und der Docker-Daemon laufen. Damit bleibt Platz für eine kleine Anwendung und einen Reverse Proxy, aber nicht für eine Datenbank unter realer Last. Das Erstellen von Images auf einem System dieser Größe schlägt fehl oder beendet einen anderen Prozess. Erstellen Sie die Images daher an einem anderen Ort und laden Sie das fertige Image mit pull.
Schützt UFW einen Docker-Container?
Nicht bei veröffentlichten Ports. Docker erstellt eigene DNAT- und Forward-Regeln. Ein Paket an einen veröffentlichten Container-Port wird deshalb an den Container weitergeleitet, statt an den Host zugestellt zu werden. Die von UFW verwalteten INPUT-Regeln sehen dieses Paket nie. ufw deny 5432 kann aktiv sein, während dieser Port aus dem Internet erreichbar ist. Veröffentlichen Sie den Port mit 127.0.0.1:5432:5432 nur auf loopback, lassen Sie interne Dienste unveröffentlicht oder filtern Sie in der DOCKER-USER-Kette.
Werden meine Container nach einem VPS-Neustart neu gestartet?
Nur wenn sie mit einer Restart-Policy erstellt wurden. Setzen Sie restart: unless-stopped für jeden Dienst, führen Sie docker compose up -d aus, damit die Container mit dieser Einstellung neu erstellt werden, und prüfen Sie, ob systemctl is-enabled docker enabled ausgibt. Starten Sie das System anschließend gezielt neu und prüfen Sie docker compose ps. Eine Restart-Policy, die Sie nie getestet haben, ist keine verlässliche Restart-Policy.
Wie oft sollte ich Docker-Images bereinigen?
Für die meisten kleinen Server reicht eine monatliche Bereinigung. Sie können auch bereinigen, sobald docker system df Speicherplatz meldet, den Sie benötigen. docker image prune -a und docker builder prune sind sicher, während die Dienste laufen, da verwendete Images und Caches übersprungen werden. Vermeiden Sie docker system prune --volumes, sofern Sie nicht genau wissen, welche Volumes nicht referenziert werden. Der Befehl löscht die Daten jedes Stacks, der zu diesem Zeitpunkt angehalten ist.