Docker auf einem VPS: Was sich wirklich ändert
Docker auf einem VPS nutzt dieselbe Engine, hat aber weniger Spielraum: RAM endet im OOM-Kill, Ports umgehen UFW, Container starten nach Reboots nicht und Speicher läuft voll.
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 ist jedoch eine andere. Ein Laptop verfügt über freien Arbeitsspeicher, eine Firewall, die niemand scannt, und ein ausreichend großes Laufwerk, dessen Belegung Sie nie prüfen müssen. Ein gemieteter Server hat eine feste Speichergrenze, eine öffentliche IP-Adresse, die innerhalb weniger Minuten nach dem Booten 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 umgeht UFW (uncomplicated firewall), weil Docker eigene Firewall-Regeln schreibt.
- Container werden nach einem Reboot nicht automatisch erneut gestartet, sofern Sie dies nicht vorher festgelegt haben.
- Images, Container, Volumes und der Build-Cache wachsen, bis der Datenträger voll ist.
In jedem folgenden Abschnitt werden der Fehler, die tatsächlich angezeigte Meldung und die Anleitung genannt, die das Problem ausführlich behebt. Wenn Sie noch keine Compose-Datei erstellt haben, lesen Sie zuerst Grundlagen zu Docker Compose auf einem VPS und kehren Sie anschließend hierher zurück. Auf dieser Seite wird vorausgesetzt, dass Sie bereits einen Stack 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 keinen Gastkernel und keine feste Speicherzuweisung. Die Kosten entsprechen dem Speicher, auf den der Prozess im Container tatsächlich zugreift. Deshalb passt ein vollständiger Stack in 2 GB, während derselbe Stack aus virtuellen Maschinen darin nicht Platz hätte.
Die folgenden Werte sind typische Leerlaufwerte für unveränderte Images unter Ubuntu 24.04 mit der Standardkonfiguration. Sie wurden wenige Minuten nach dem Start aus docker stats ausgelesen. Sie 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 irgendeinem Wert vertrauen, auch diesen nicht.
Die Daten hinter diesem Diagramm
[
{
"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 haben unterschiedliche Aufgaben. idle_mb zeigt, wie viel der Container im Leerlauf verwendet. budget_mb ist der Wert, den Sie bei der Planung reservieren sollten, weil der reale Betrieb kein Leerlauf ist. PostgreSQL liegt im Leerlauf bei etwa 45 MB und benötigt 512 MB, sobald Verbindungen, Sortierungen und Cache aktiv sind. Planen Sie mit der Budget-Spalte. Verwenden Sie die Leerlauf-Spalte zur Fehlersuche.
Beachten Sie das Muster dieser 7 Zeilen. nginx verwendet im Leerlauf 8 MB und Nextcloud 210 MB. Der Proxy vor Ihren Anwendungen benötigt fast keinen Speicher. Für die Dimensionierung des Systems sind die Datenbank und die PHP-Anwendung entscheidend.
Ein Hinweis zu docker stats: Der Speicherwert umfasst auch den Page Cache, den die Dateizugriffe des Containers geladen haben. Daher steigt der Wert nach dem Start zunächst an und stabilisiert sich anschließend. Überwachen Sie ihn eine Stunde lang, bevor Sie von einem Speicherleck ausgehen.
Größe eines VPS: Was in 2 GB, 4 GB und 8 GB passt
Ziehen Sie zuerst den Anteil des Hosts ab. Kernel, systemd, journald, sshd und der Docker-Daemon verwenden denselben RAM wie Ihre Container, und dockerd mit containerd belegt davon etwa 100 MB. Sie benötigen außerdem freien Speicher für den Page Cache und für den kurzzeitigen Mehrbedarf, wenn ein Image erstellt oder ein Datenbank-Dump ausgeführt wird.
Die Daten hinter diesem Diagramm
[
{
"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 umfasst das Betriebssystem, den Docker-Daemon und die Reserve, die den Server auch unter Last reaktionsfähig hält. Übrig bleibt container_mb. Das ist die einzige Größe, über die Sie verfügen können. Die Reserve steigt 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. Für zwei kleine Anwendungen mit jeweils etwa 256 MB reicht der Rest noch aus. Das ist ein realistisch nutzbarer Server. Für Nextcloud und zusätzlich einen Suchcluster reicht der Speicher nicht.
Ein Tarif mit 4 GB lässt 3072 MB übrig. Darin finden gleichzeitig eine Datenbank, ein Reverse Proxy, drei Anwendungen und ein Monitoring-Container Platz. Das ist die kleinste sinnvolle Größe für wichtige Anwendungen. Die freie Speicherkapazität fängt dabei die Auswirkungen eines fehlerhaften Deployments ab.
Ein Tarif mit 8 GB lässt 6656 MB von insgesamt 8192 MB übrig. Die Grenze liegt dann meistens nicht mehr beim Speicher, sondern bei der CPU oder beim Datendurchsatz. SSD Nodes bietet keinen kleineren Tarif an: Der kleinste Tarif hat 8 GB RAM (Preisseite geprüft am 2026-10-11). Auf diesem Host gilt daher diese Zeile. Die Zeilen für 2 GB und 4 GB beschreiben kleinere Server anderer Anbieter. Bei einer Klasse von Containern wird die Größe anhand der Konfiguration und nicht anhand der Auslastung bestimmt: Ein lokaler Modellserver reserviert den KV-Cache proportional zur Größe seines Kontextfensters. Wenn Sie Ollamas num_ctx erhöhen, kann der Speicherbedarf um mehrere Gigabyte steigen, bevor die erste Anfrage eintrifft. Wenn die Berechnung ergibt, dass Ihr Stack nicht passt, wählen Sie den größeren Tarif, statt die Konfiguration um das Speicherlimit herum anzupassen: Was ein VPS tatsächlich kostet zeigt, welchen monatlichen Wert die zusätzlichen Gigabytes 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 mitreißt. Lassen Sie außerdem einen Teil des Budgets ungenutzt, weil docker compose build und pg_dump im ungünstigsten Moment beide Speicher benötigen. Speicherlimits in Docker Compose beschreibt die Syntax und die typischen Fehler.
Warum wird mein Container mit Exit-Code 137 beendet?
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-Killer (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 Prozess, den der Kernel ausgewählt hat:
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 ganz ohne Limit. Ohne Limit entspricht seine Obergrenze dem gesamten Host. Dadurch kann ein Speicherleck in einem Dienst den Host so stark belasten, dass der Kernel anschließend systemweit anhand der Prozessgröße einen Prozess als Opfer auswählt. In der Logzeile fehlt dann das Präfix Memory cgroup; sie lautet Out of memory: Killed process 2417 (postgres). Häufig trifft die Auswahl 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 nichts aus. Eine Swap-Datei gibt dem Kernel die Möglichkeit, selten verwendete Speicherseiten auszulagern. Dadurch gewinnen Sie einige Minuten, um ein 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 eine ungleich null große Gesamtsumme anzeigen. Swap erweitert den Arbeitsspeicher nicht. Ein System 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 korrigieren Sie die Dimensionierung.
Warum blockiert UFW meinen veröffentlichten Docker-Port nicht?
Weil der Netzwerkverkehr die von UFW überwachte Chain nicht erreicht. Wenn Sie einen Port mit -p 5432:5432 oder einem Compose-Eintrag ports: veröffentlichen, schreibt der Daemon eine DNAT-Regel (Destination Network Address Translation) in die nat-Tabelle und eine Accept-Regel in seine eigene Chain DOCKER. Ein Paket an einen Container wird an diesen Container weitergeleitet und nicht an den Host zugestellt. Daher wird es im FORWARD-Pfad verarbeitet und durchläuft nicht 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 meldet.
Die Lösung besteht darin, weniger Ports zu veröffentlichen. Container in einem Compose-Projekt verwenden ein gemeinsames Netzwerk und erreichen einander ü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 kleinen, korrekt konfigurierten Stack veröffentlicht normalerweise nur der Reverse Proxy Ports, und zwar 80 und 443. Warum veröffentlichte Docker-Ports UFW umgehen behandelt die DOCKER-USER-Chain für Fälle, in denen Sie einen Port veröffentlichen und trotzdem filtern müssen. Grundlagen der UFW-Firewall behandelt die zugrunde liegenden Host-Regeln.
Warum sind meine Container nach einem Reboot verschwunden?
Weil nichts festgelegt hat, dass sie wieder gestartet werden. Ein Container wird standardmäßig mit der Restart-Policy no erstellt, sofern Sie keine andere 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 dazu.
Zwei Voraussetzungen müssen erfüllt sein. Der Daemon muss beim Booten starten:
systemctl is-enabled dockerBei einer Standardinstallation von Ubuntu wird damit enabled ausgegeben. Anschließend benötigt jeder Dienst eine Policy:
services:
app:
image: ghcr.io/example/app:1.4
restart: unless-stoppedunless-stopped startet den Container nach einem Reboot erneut und berücksichtigt dabei 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 ist beim Debugging überraschend. Das Bearbeiten der Datei allein reicht nicht aus, weil die Restart-Policy beim Erstellen des Containers festgelegt wird. Führen Sie docker compose up -d aus, damit der Container neu erstellt wird, und prüfen Sie anschließend den aktuell verwendeten Wert:
docker inspect my-app | grep -A3 RestartPolicyFühren Sie danach absichtlich einen Reboot des Systems durch und anschließend docker compose ps im Projektverzeichnis. Wenn ein Stack einen geplanten Reboot übersteht, übersteht er auch einen ungeplanten. Wenn Ihr Stack eine garantierte Startreihenfolge oder einen einmalig beim Booten ausgeführten Job 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 wieder gestarteter Container tatsächlich Dienste bereitstellt, fügen Sie Compose-Healthchecks hinzu.
Warum ist die Festplatte meines VPS voll?
Docker behält alles, bis Sie es ausdrücklich entfernen. Jedes Image-Tag, das Sie jemals abgerufen haben, jeder angehaltene Container, jedes anonyme Volume, das bei einer Neuerstellung zurückgeblieben ist, und jede Schicht des Build-Cache bleibt auf der Festplatte. Eine größere Festplatte verschafft Ihnen nur mehr Zeit. Auch die 160-GB-SSD im kleinsten Tarif von SSD Nodes (Preisseite geprüft am 2026-10-11) läuft auf einem System, das jede Woche Images abruft und erstellt, auf dieselbe Weise voll. Ein kleineres Root-Dateisystem führt dann früher zu einem Ausfall.
Eine volle Festplatte sieht nicht wie ein Absturz aus. Innerhalb derselben Stunde erhalten Sie no space left on device von einem Container, von apt, von journald und von docker pull. PostgreSQL nimmt keine Schreibvorgänge mehr an. Das System läuft weiterhin. Dadurch wird das Problem schwerer erkannt als eine Neustartschleife.
Prüfen Sie die Belegung, 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 System, das eigene Images 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 im laufenden Betrieb sicher, weil verwendete Daten übersprungen werden. Nicht sicher ist docker system prune --volumes. Der Befehl entfernt angehaltene Container und anschließend jedes anonyme Volume, das von keinem verbleibenden Container verwendet wird. Ein Stack, den Sie über das Wochenende angehalten haben, entspricht genau diesem Muster. Wenn die Datenbank ihre Daten in einem anonymen Volume gespeichert hat, werden diese Daten ebenfalls gelöscht. Docker erstellt ein solches Volume, wenn ein Image ein Volume deklariert und Ihre Compose-Datei dort keinen Mount definiert. Benannte Volumes bleiben bei diesem Befehl erhalten. docker volume prune --all entfernt jedoch auch nicht verwendete benannte Volumes. Lesen Sie Bind-Mounts im Vergleich zu benannten Volumes, bevor Sie eines der beiden Flags verwenden, und erstellen Sie vorher ein Backup.
Container-Logs wachsen unauffälliger. Der standardmäßige json-file-Treiber hat keine Größenbegrenzung. Ein Container mit vielen Meldungen kann dadurch Gigabytes in /var/lib/docker/containers schreiben. 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. Dabei werden Ihre Container neu gestartet. Wählen Sie daher einen passenden Zeitpunkt. Die Begrenzung gilt für Container, die nach der Änderung erstellt werden. Erstellen Sie die laufenden Container daher 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 Inspect-Ausgabe sollte max-size gesetzt anzeigen. Ist der Wert leer, wurde der Container vor der Änderung erstellt und schreibt weiterhin ohne Begrenzung.
Gewohnheiten für einen kleinen Docker-Server
Dafür benötigen Sie weder ein Dashboard noch ein zusätzliches Tool, in das Sie sich erst einarbeiten müssen.
- Führen Sie am ersten Tag jedes Monats
docker system dfunddf -h /aus. Zwei Befehle und 30 Sekunden genügen, um den Trend zu erkennen, lange bevor es zu einem Ausfall kommt. - Legen Sie für jeden Dienst ein Speicherlimit fest, auch für Dienste, die Sie für unkritisch klein halten. Das Limit macht aus einem Ausfall des gesamten Hosts einen einzelnen Container, der neu gestartet wird.
- Überwachen Sie den Server von einem anderen System aus. So erfahren Sie von Speicher- oder Festplattendruck, 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 sowohl einen Zeitplan als auch einen Wiederherstellungstest ab.
- Fixieren Sie die Image-Tags in der Compose-Datei und aktualisieren Sie sie an einem von Ihnen festgelegten Tag. Mit
latestentspricht die Version, die Sie beim nächstendocker compose pullerhalten, dem Release-Stand dieses Morgens.
Ein kleiner VPS mit Docker bleibt über Jahre stabil, wenn vier Werte im zulässigen Bereich bleiben: das Speicherbudget, die Liste der veröffentlichten Ports, die Restart-Richtlinie 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 benötigen zusammen etwa 100 MB; der übrige Bedarf entfällt auf Ihre Container. Reservieren Sie zuerst den Anteil für den Host: 768 MB auf einem System mit 2048 MB für das Betriebssystem, den Daemon und zusätzliche Reserve. Damit verbleiben 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 Ihren eigenen Stack mit docker stats --no-stream, statt veröffentlichten Angaben zu vertrauen.
Kann ich Docker auf einem VPS mit 1 GB betreiben?
Ja, für ein oder zwei leichtgewichtige Container. Richten Sie vorher eine Swap-Datei ein. Sobald das Betriebssystem und der Docker-Daemon laufen, ist bei einem System mit 1 GB ungefähr die Hälfte belegt. 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 auf einem anderen System und laden Sie das fertige Image herunter.
Schützt UFW einen Docker-Container?
Nicht bei veröffentlichten Ports. Docker schreibt 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 nicht. ufw deny 5432 kann aktiv sein, während dieser Port aus dem Internet erreichbar ist. Veröffentlichen Sie den Port auf dem Loopback-Interface mit 127.0.0.1:5432:5432, lassen Sie interne Dienste unveröffentlicht oder filtern Sie in der DOCKER-USER-Kette.
Werden meine Container nach dem Neustart eines VPS 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 bewusst 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 unbedenklich, während die Dienste laufen, da verwendete Images und Caches übersprungen werden. Vermeiden Sie docker system prune --volumes, wenn Sie nicht genau wissen, welche Volumes nicht referenziert werden. Der Befehl löscht die anonymen Volumes jedes Stacks, der zu diesem Zeitpunkt zufällig angehalten ist. docker volume prune --all löscht zusätzlich nicht verwendete benannte Volumes.