SSD Nodes Learn 🎉 VPS vanaf $5.50/mnd
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-13

Docker op een VPS: wat verandert er echt?

Docker op een VPS is niet hetzelfde als lokaal draaien. Leer hoe u geheugentekorten voorkomt, UFW-omzeiling door poorten blokkeert en schijfruimte beheert bij container-opslag.

Wat er verandert wanneer 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 zal vullen.

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 herstart, tenzij u daar vooraf om heeft gevraagd.
  • Images, containers, volumes en build-cache groeien totdat de schijf vol is.

Elke onderstaande sectie benoemt het probleem, de melding 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 op een 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 hebben 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, sorteeracties en cache actief zijn. Plan met de budget-kolom. Debug met de inactieve kolom.

Let op de verhoudingen 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 bepalen de benodigde grootte van de server.

Eén waarschuwing over docker stats: het geheugencijfer bevat de page cache die door de eigen bestandstoegang 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 beslaat daar ongeveer 100 MB van. 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 geen 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 extra geheugen de klappen opvangt bij een mislukte deploy.

Een 8 GB-abonnement laat 6656 MB over van de 8192 MB, en de beperkende factor verschuift dan meestal van geheugen naar CPU of schijfdoorvoer. Als de berekening aangeeft dat uw stack niet past, schaf dan het grotere abonnement aan in plaats van te proberen het systeem krampachtig te optimaliseren: 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 een deel van het budget onbenut, omdat docker compose build en pg_dump beide geheugen opeisen op het meest kritieke moment. Geheugenlimieten in Docker Compose bevat de syntax en de valkuilen.

Waarom stopt mijn container met exitcode 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 is 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 in 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 die het lek veroorzaakte blijft draaien. Daarom is een limiet op elke service belangrijker dan de exacte waarde van een individuele limiet.

Swap verandert de timing, niet de rekenkunde. 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 rij Swap. Swap voegt geen RAM toe. Een server die constant onder geheugendruk staat, wordt zo traag dat u niet meer via SSH kunt inloggen om het probleem te verhelpen; beschouw swap daarom als een waarschuwingsbuffer 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:-entry 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 observeren:

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

UFW kan 5432 DENY IN Anywhere tonen 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 openbare 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:-entry nodig. Wanneer u wel lokale toegang wilt, bindt u de publicatie aan de loopback:

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, specifiek op 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 reboot verdwenen?

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

Er moet aan twee voorwaarden worden voldaan. De daemon moet bij het opstarten worden geladen:

systemctl is-enabled docker

Dit commando toont enabled op een standaard Ubuntu-installatie. Vervolgens moet elke service een policy hebben:

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

unless-stopped zorgt ervoor dat de container na een reboot terugkeert en respecteert 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 bestand bewerken is op zichzelf niet voldoende, omdat de restart policy wordt vastgelegd bij het aanmaken van de container. Voer docker compose up -d uit zodat deze opnieuw wordt aangemaakt en controleer daarna de actieve waarde:

docker inspect my-app | grep -A3 RestartPolicy

Herstart vervolgens de server bewust en voer docker compose ps uit in de projectmap. Een stack die een geplande reboot overleeft, overleeft ook een ongeplande reboot. Als uw stack een specifieke opstartvolgorde vereist of een eenmalige taak bij het opstarten nodig heeft, is een systemd unit het betere hulpmiddel: Docker Compose bij opstarten starten bevat het unit-bestand. Om te controleren of een container die opnieuw 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 hoeft. Elke image-tag die u ooit heeft binnengehaald, elke gestopte container, elk anoniem volume dat is achtergebleven na een hercreatie 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 ontvangt 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 kolom RECLAIMABLE. Op een server die 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, omdat dit elk volume verwijdert waarnaar op dat moment geen enkele container verwijst. Een stack die u voor het weekend heeft gestopt, heeft precies die vorm, en het bijbehorende databasevolume wordt dan verwijderd. Lees bind mounts versus named volumes voordat u die vlag typt en maak eerst een back-up.

Container-logs zorgen voor een minder opvallende groei. De standaard json-file-driver heeft geen groottelimiet, waardoor een spraakzame container gigabytes aan data schrijft naar /var/lib/docker/containers. 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 hercreëer de draaiende containers met docker compose up -d --force-recreate en controleer het resultaat:

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

De inspect-output zou moeten tonen dat max-size is ingesteld. 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. Met deze twee commando's ziet u in dertig seconden trends voordat deze leiden tot een uitval.
  • Stel voor elke service een geheugenlimiet in, ook voor services waarvan u zeker weet dat ze klein zijn. Deze 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 on a VPS beschrijft een planning en een hersteltest.
  • Pin image-tags in het compose-bestand en update deze op een dag naar keuze. Zonder pinning is de versie die u bij de volgende docker compose pull via latest 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 werkt hetzelfde als de Docker-omgeving die u al thuis 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 het geheugenverbruik wordt bepaald door uw containers. Reserveer eerst het deel voor de host: 768 MB op een systeem met 2048 MB voor het besturingssysteem, de daemon en wat extra ruimte. Dit laat 1280 MB over voor uw containers. Een database van 512 MB, een reverse proxy van 128 MB en twee kleine applicaties passen hierin. Meet het verbruik van uw eigen stack met docker stats --no-stream in plaats van af te gaan op gepubliceerde cijfers.

Kan ik Docker draaien op een 1 GB VPS?

Ja, voor een of twee lichte containers, mits u een swap-bestand toevoegt voordat u begint. Ongeveer de helft van een 1 GB-systeem 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 reële belasting. Het bouwen van images op een systeem van deze omvang 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; de INPUT-regels die UFW beheert, zien dit verkeer niet. ufw deny 5432 kan actief zijn terwijl die poort bereikbaar is vanaf het internet. Publiceer naar localhost 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. Start daarna de server bewust opnieuw op en controleer docker compose ps. Een restart policy die u niet 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 ruimte vrijgemaakt kan worden die u nodig heeft. docker image prune -a en docker builder prune zijn veilig om uit te voeren 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 op dat moment is gestopt.