SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-28

Docker op een VPS: wat verandert er echt?

Docker op een VPS is anders dan op uw laptop. Ontdek hoe u geheugentekorten, openstaande UFW-poorten, ontbrekende restart-policies en een volle schijf effectief beheert.

Wat verandert er als u Docker op een VPS draait

Docker op een VPS gebruikt dezelfde engine en dezelfde images als Docker op uw laptop, dus elk commando dat u al kent, werkt nog steeds. Wat verandert, is de omgeving eromheen. Een laptop heeft extra geheugen, een firewall die niemand scant en een schijf die groot genoeg is om nooit naar om te kijken. Een gehuurde server heeft een vast geheugenplafond, een publiek IP-adres dat binnen enkele minuten na het opstarten wordt gescand en een root-bestandssysteem dat Docker zonder vragen volschrijft.

Vier verschillen veroorzaken de meeste problemen op een kleine server:

  • Geheugen is eindig en de kernel lost een tekort op door een proces te beëindigen.
  • Een gepubliceerde poort gaat rechtstreeks door UFW (uncomplicated firewall) heen, omdat Docker zijn eigen firewallregels schrijft.
  • Containers komen niet terug na een reboot, tenzij u daar vooraf om heeft gevraagd.
  • Images, containers, volumes en build-cache groeien totdat de schijf vol is.

Elke sectie hieronder benoemt de fout, de tekst die u daadwerkelijk zult zien en de handleiding die dit grondig oplost. Als u nog geen compose-bestand heeft geschreven, lees dan eerst Docker Compose basics on a VPS en kom daarna terug. Deze pagina gaat ervan uit dat u al in staat bent om een stack op te starten.

Hoeveel RAM verbruikt een Docker container?

Minder dan de meeste mensen verwachten. Een container is een proces in een cgroup (control group), geen virtuele machine; er is dus geen gast-kernel en geen vaste toewijzing. De kosten zijn gelijk aan wat het proces binnenin aanspreekt. Daarom past een volledige stack in 2 GB, terwijl dezelfde stack opgebouwd uit virtuele machines dat niet zou doen.

De onderstaande cijfers zijn typische waarden voor inactieve standaardimages op Ubuntu 24.04 met de standaardconfiguratie, uitgelezen via docker stats enkele minuten na de start. Ze dienen als uitgangspunt voor planning, niet als benchmark voor uw werklast. Voer docker stats --no-stream uit op uw eigen systeem voordat u op enig cijfer vertrouwt, inclusief deze.

ChartTypical idle container memory, stock images, megabytes
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
  }
]

De twee kolommen vervullen verschillende functies. idle_mb is wat de container verbruikt terwijl deze niets doet. budget_mb is wat u moet reserveren bij het plannen, omdat werkelijk gebruik niet inactief is. PostgreSQL is inactief rond de 45 MB en vereist 512 MB zodra verbindingen, sorteringen en cache actief zijn. Plan met de budget-kolom. Debug met de inactieve-kolom.

Let op de vorm van die 7 rijen. nginx is inactief op 8 MB en Nextcloud op 210 MB. De proxy voor uw applicaties is nagenoeg gratis. De database en de PHP-applicatie zijn bepalend voor de grootte van uw server.

Eén waarschuwing over docker stats: het geheugencijfer bevat de page cache die door de eigen bestand-reads van de container is binnengehaald, dus het stijgt enige tijd na de start en stabiliseert daarna. Observeer dit gedurende een uur voordat u besluit dat er sprake is van een lek.

Een VPS dimensioneren: wat past er in 2 GB, 4 GB en 8 GB

Trek eerst het aandeel van de host eraf. De kernel, systemd, journald, sshd en de Docker daemon verbruiken hetzelfde RAM-geheugen als uw containers, en dockerd met containerd neemt daarvan ongeveer 100 MB in beslag. U heeft ook vrij geheugen nodig voor de page cache en voor pieken tijdens het bouwen van een image of het maken van een database-dump.

ChartRAM budget by plan size, megabytes
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 dekt het besturingssysteem, de Docker daemon en de reserve die de server responsief houdt onder belasting. Wat overblijft is container_mb, en dat is het enige getal dat u daadwerkelijk kunt benutten. De reserve groeit mee met het abonnement, van 768 MB op de kleinste server tot 1536 MB op de grootste, omdat een grotere server meer containers draait, meer logs schrijft en meer page cache vereist.

Een 2 GB-abonnement laat 1280 MB over voor containers. Besteed 512 MB daarvan aan PostgreSQL en 128 MB aan Traefik, en de helft is al verbruikt. De rest is ruimte voor twee kleine applicaties van ongeveer 256 MB per stuk. Dat is een reële en bruikbare server. Het is echter onvoldoende ruimte voor Nextcloud en een search-cluster daarbovenop.

Een 4 GB-abonnement laat 3072 MB over; dit is de ruimte waarin een database, een reverse proxy, drie applicaties en een monitoring-container tegelijkertijd passen. Dit is de kleinste omvang die de moeite waard is voor alles wat voor u belangrijk is, omdat het vrije geheugen de klappen opvangt van een mislukte deploy.

Een 8 GB-abonnement laat 6656 MB over van de 8192 MB, en de beperkende factor verschuift meestal van geheugen naar CPU of schijfdoorvoer. Eén type container bepaalt zijn eigen omvang op basis van configuratie in plaats van belasting: een lokale model-server reserveert KV-cache in verhouding tot zijn context-window, dus het verhogen van Ollama's num_ctx kan gigabytes aan het budget toevoegen nog voordat er een enkel verzoek binnenkomt. Als de rekensom aangeeft dat uw stack niet past, schaf dan een groter abonnement aan in plaats van te proberen het systeem bij te sturen: wat een VPS werkelijk kost zet uiteen wat de extra gigabytes per maand waard zijn.

Twee regels houden de berekening betrouwbaar. Stel een geheugenlimiet in op elke service, zodat één op hol geslagen proces niet de hele server platlegt. En laat het bovenste deel van het budget onbenut, omdat docker compose build en pg_dump beide geheugen vereisen op het meest kritieke moment. Geheugenlimieten in Docker Compose bevat de syntax en de valkuilen.

Waarom stopt mijn container met code 137?

Dit komt doordat de kernel het proces heeft beëindigd. 137 is 128 plus 9, en signaal 9 staat voor SIGKILL. De container vroeg om meer geheugen dan toegestaan, waarna de out of memory (OOM) killer het proces heeft beëindigd.

CONTAINER ID   IMAGE         STATUS
9f2c1a4d7b3e   postgres:16   Exited (137) 4 minutes ago

Bevestig de oorzaak in plaats van te gissen:

docker inspect my-db | grep -i oomkilled
sudo journalctl -k | grep -i "out of memory"

"OOMKilled": true betekent dat de container zijn eigen cgroup-limiet heeft bereikt, en het kernellogboek vermeldt het proces dat werd gekozen:

Memory cgroup out of memory: Killed process 2417 (postgres) total-vm:1284416kB

Dit is het gunstige scenario, omdat de schade beperkt bleef tot één container. Het ongunstige scenario is een container zonder enige limiet. Zonder limiet is het plafond de gehele machine; een geheugenlek in één service put de host uit, waarna de kernel op basis van grootte een slachtoffer kiest uit het gehele systeem. De logregel verliest dan het Memory cgroup-voorvoegsel en luidt Out of memory: Killed process 2417 (postgres). Het proces dat wordt gekozen is vaak uw database, terwijl de container met het lek blijft draaien. Daarom is een limiet op elke service belangrijker dan de exacte waarde van een individuele limiet.

Swap verandert de timing, niet de berekening. De meeste VPS-images worden geleverd zonder swap. Controleer dit met swapon --show, dat niets weergeeft als er geen swap aanwezig is. Een swap-bestand geeft de kernel een plek om inactieve pagina's te plaatsen, wat u minuten tijd geeft om een probleem op te merken.

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 -h

free -h zou nu een waarde ongelijk aan nul moeten tonen in de Swap-rij. Swap voegt geen RAM toe. Een server onder constante geheugendruk wordt zo traag dat u niet meer via SSH kunt inloggen om het te herstellen; beschouw swap daarom als een alarmbuffer en corrigeer de dimensionering.

Waarom blokkeert UFW mijn gepubliceerde Docker-poort niet?

Omdat het verkeer de chain die UFW bewaakt nooit bereikt. Wanneer u een poort publiceert met -p 5432:5432 of een ports:-item in een compose-bestand, schrijft de daemon een DNAT-regel (destination network address translation) naar de nat-tabel en een accept-regel naar zijn eigen DOCKER-chain. Een pakket dat voor een container is bestemd, wordt doorgestuurd naar die container in plaats van afgeleverd bij de host. Het wordt daarom afgehandeld in het FORWARD-pad en passeert nooit de INPUT-regels die UFW schrijft.

U kunt dit proces op de server monitoren:

sudo ufw status | grep 5432
sudo iptables -t nat -L DOCKER -n | grep 5432

UFW kan 5432 DENY IN Anywhere weergeven terwijl de nat-tabel een DNAT tcp ... to:172.18.0.2:5432-regel voor dezelfde poort bevat. Vanaf een andere machine maakt nc -vz your.server.ip 5432 nog steeds verbinding. De database staat op het publieke internet, terwijl de firewall aangeeft dat dit niet het geval is.

De oplossing is om minder poorten te publiceren. Containers in hetzelfde compose-project delen een netwerk en bereiken elkaar via de servicenaam. Een database die alleen de applicatie ernaast bedient, heeft dus helemaal geen ports:-item nodig. Wanneer u wel lokale toegang wilt, bindt u de publicatie aan de loopback-interface:

services:
  db:
    image: postgres:16
    ports:
      - "127.0.0.1:5432:5432"

Na docker compose up -d mislukt nc -vz your.server.ip 5432 vanaf een externe locatie, terwijl psql -h 127.0.0.1 -p 5432 op de server zelf nog steeds werkt. In een gezonde, kleine stack publiceert alleen de reverse proxy poorten, namelijk 80 en 443. Waarom Docker-gepubliceerde poorten UFW omzeilen behandelt de DOCKER-USER-chain voor situaties waarin u een poort moet publiceren en deze toch wilt filteren, en Basisprincipes van de UFW-firewall behandelt de onderliggende host-regels.

Waarom zijn mijn containers na een herstart verdwenen?

Omdat er geen instructie is gegeven om ze opnieuw op te starten. Een container wordt aangemaakt met het restart-beleid no tenzij u anders instelt; na een herstart blijft de container dus gestopt en de daemon onderneemt geen actie. Herstarts zijn niet zeldzaam op een VPS: kernel-updates via unattended-upgrades, onderhoud door de provider en de hierboven genoemde OOM-volgorde resulteren er allemaal in.

Er moet aan twee voorwaarden worden voldaan. De daemon moet starten bij het opstarten van het systeem:

systemctl is-enabled docker

Dit geeft enabled weer op een standaard Ubuntu-installatie. Vervolgens heeft elke service een beleid nodig:

services:
  app:
    image: ghcr.io/example/app:1.4
    restart: unless-stopped

unless-stopped zorgt ervoor dat de container na een herstart terugkeert en houdt rekening met containers die u handmatig heeft gestopt. always herstart ook de containers die u bewust heeft gestopt zodra de daemon herstart, wat voor verrassingen kan zorgen tijdens het debuggen. Het bewerken van het bestand is op zichzelf niet voldoende, omdat het restart-beleid wordt vastgelegd bij het aanmaken van de container. Voer docker compose up -d uit zodat deze opnieuw wordt aangemaakt en controleer vervolgens de actieve waarde:

docker inspect my-app | grep -A3 RestartPolicy

Herstart daarna de server bewust en voer docker compose ps uit in de projectmap. Een stack die een geplande herstart overleeft, overleeft ook een ongeplande herstart. Als uw stack een specifieke opstartvolgorde vereist of een eenmalige taak bij het opstarten moet uitvoeren, is een systemd-unit een betere oplossing: Docker Compose starten bij boot bevat het unit-bestand. Om te controleren of een container die is opgestart ook daadwerkelijk functioneert, voegt u Compose healthchecks toe.

Waarom is mijn VPS-schijf vol?

Omdat Docker alles bewaart totdat u aangeeft dat dit niet meer nodig is. Elke image-tag die u ooit heeft binnengehaald, elke gestopte container, elk anoniem volume dat is achtergebleven na een herstart en elke laag van de build-cache blijft op de schijf staan. Op een root-bestandssysteem van 40 GB of 80 GB, wat gebruikelijk is bij deze pakketgroottes, leidt dit binnen maanden in plaats van jaren tot een uitval.

Een volle schijf ziet er niet uit als een crash. U krijgt no space left on device van een container, van apt, van journald en van docker pull in hetzelfde uur. PostgreSQL stopt met het accepteren van schrijfacties. De server is nog steeds actief, wat het lastiger maakt om op te merken dan een reboot-loop.

Kijk eerst voordat u verwijdert:

docker system df
df -h /

docker system df splitst het totaal op in images, containers, lokale volumes en build-cache, met daarnaast een RECLAIMABLE-kolom. Op een server die zijn eigen images bouwt, is de build-cache meestal de grootste post.

docker image prune -a
docker builder prune
docker system df

docker image prune -a verwijdert elke image die door geen enkele container wordt gebruikt. docker builder prune wist de build-cache. Beide acties zijn veilig terwijl services draaien, omdat alles wat in gebruik is wordt overgeslagen. Wat niet veilig is, is docker system prune --volumes; dit verwijdert elk volume waarnaar op dat moment geen enkele container verwijst. Een stack die u voor het weekend heeft gestopt, heeft precies die status, en het databasevolume wordt dan verwijderd. Lees bind mounts versus named volumes voordat u die vlag typt en maak eerst een back-up.

Container-logs zijn een stillere groeifactor. De standaard json-file-driver heeft geen groottelimiet, waardoor één spraakzame container gigabytes naar /var/lib/docker/containers schrijft. Begrens dit voor elke container in /etc/docker/daemon.json:

{
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
  }
}

Pas dit toe met sudo systemctl restart docker, wat uw containers herstart; kies dus het juiste moment. De limiet geldt voor containers die na de wijziging zijn aangemaakt, dus maak de draaiende containers opnieuw aan met docker compose up -d --force-recreate en bevestig dit:

docker inspect my-app | grep -A5 LogConfig
sudo du -sh /var/lib/docker/containers

De inspect-output hoort max-size als ingesteld te tonen. Als dit leeg is, dateert die container van vóór de wijziging en schrijft deze nog steeds zonder limiet.

Gewoontes voor het onderhoud van een kleine Docker-server

Hiervoor is geen dashboard of complexe tool vereist.

  • Voer op de eerste dag van de maand docker system df en df -h / uit. Twee commando's, dertig seconden werk, en u ziet de trend lang voordat deze leidt tot een uitval.
  • Stel voor elke service een geheugenlimiet in, ook voor services waarvan u zeker weet dat ze klein zijn. De limiet zorgt ervoor dat een probleem beperkt blijft tot één herstartende container in plaats van een uitval van de gehele host.
  • Monitor de server vanaf een externe locatie, zodat u op de hoogte bent van geheugen- of schijfdruk voordat de kernel ingrijpt. Uptime Kuma draait in een container en verbruikt in ruststand ongeveer 95 MB.
  • Maak back-ups van volumes, niet van containers. De container is vervangbaar, het volume niet. restic backups op een VPS behandelt een back-upschema en een hersteltest.
  • Pin image-tags in het compose-bestand en update deze op een dag naar keuze. Met latest is de versie die u bij de volgende docker compose pull ontvangt, de versie die die ochtend is uitgebracht.

Een kleine VPS met Docker blijft jarenlang stabiel als vier waarden binnen de perken blijven: het geheugenbudget, de lijst met gepubliceerde poorten, het restart-beleid van elke service en de vrije schijfruimte. Al het overige is de standaard Docker-omgeving die u al gebruikt.

FAQ

Hoeveel RAM heb ik nodig om Docker op een VPS te draaien?

Docker zelf is licht. De daemon en containerd verbruiken samen ongeveer 100 MB; de rest van de vereisten hangt af van uw containers. Reserveer eerst het deel voor de host: 768 MB op een machine van 2048 MB voor het besturingssysteem, de daemon en wat reserve, wat 1280 MB overlaat voor uw containers. Een database van 512 MB, een reverse proxy van 128 MB en twee kleine applicaties passen hierin. Meet uw eigen stack met docker stats --no-stream in plaats van te vertrouwen op gepubliceerde cijfers.

Kan ik Docker draaien op een 1 GB VPS?

Ja, voor één of twee lichte containers, mits u een swap-bestand toevoegt voordat u begint. Ongeveer de helft van een 1 GB-server is in gebruik zodra het besturingssysteem en de Docker-daemon draaien. Dit laat ruimte over voor een kleine applicatie en een reverse proxy, maar niet voor een database onder echte belasting. Het bouwen van images op een dergelijke machine zal falen of andere processen doen crashen; bouw daarom elders en haal de voltooide image op.

Biedt UFW bescherming voor een Docker-container?

Niet voor poorten die u publiceert. Docker schrijft zijn eigen DNAT- en forward-regels. Een pakket dat gericht is op een gepubliceerde containerpoort wordt doorgestuurd naar de container in plaats van afgeleverd bij de host, waardoor de INPUT-regels die UFW beheert het pakket nooit zien. ufw deny 5432 kan actief zijn terwijl die poort antwoordt vanaf het internet. Publiceer naar loopback met 127.0.0.1:5432:5432, laat interne services ongepubliceerd, of filter in de DOCKER-USER chain.

Starten mijn containers opnieuw na een VPS-reboot?

Alleen als ze zijn aangemaakt met een restart policy. Stel restart: unless-stopped in op elke service, voer docker compose up -d uit zodat de containers hiermee opnieuw worden aangemaakt, en bevestig dat systemctl is-enabled docker de waarde enabled toont. Voer daarna een geplande reboot uit en controleer docker compose ps. Een restart policy die u nooit heeft getest, is geen betrouwbare restart policy.

Hoe vaak moet ik Docker-images opschonen?

Maandelijks is voor de meeste kleine servers voldoende, of wanneer docker system df aangeeft dat er schijfruimte vrijgemaakt kan worden die u nodig heeft. docker image prune -a en docker builder prune zijn beide veilig terwijl services draaien, omdat images en cache die in gebruik zijn worden overgeslagen. Vermijd docker system prune --volumes tenzij u precies weet welke volumes niet langer worden gebruikt, omdat dit de data verwijdert van elke stack die toevallig gestopt is.