SSD Nodes Learn 🎉 VPS from $5.50/mo
How to do am Matt ConnorBy Matt Connor · Updated 2026-08-13

Wetin Docker for VPS Really Change?

Docker still use the same engine, but VPS RAM fit finish, published ports fit bypass UFW, containers fit stay down after reboot, and disk fit full.

Wetin dey change when you run Docker for VPS

Docker for VPS dey run the same engine and same images like Docker for your laptop, so every command wey you don already know still dey work. Wetin change na the space around am. Laptop get spare memory, firewall wey nobody dey scan, and disk wey big enough make you no need check am. Rented server get fixed memory limit, public IP address wey scanners fit start checking within minutes after boot, and root filesystem wey Docker go fill without asking.

Four differences dey cause most problem for small server:

  • Memory no unlimited, and kernel dey solve shortage by killing process.
  • Published port dey pass straight through UFW (uncomplicated firewall), because Docker dey write e own firewall rules.
  • Containers no go come back after reboot unless you arrange am before time.
  • Images, containers, volumes and build cache dey grow until disk full.

Each section below dey name the failure, the exact string wey you go see, and the guide wey explain the fix well. If you never write compose file before, read Docker Compose basics for VPS first, then come back. This page assume say you fit already bring stack up.

Docker container dey use how much RAM?

E dey use less than wetin plenty people dey expect. Container na process wey dey inside cgroup (control group), no be virtual machine. So e no get guest kernel or fixed memory allocation. The cost na whatever the process inside dey touch. Na why full stack fit run for 2 GB, while the same stack wey virtual machines build no fit.

The figures below na normal idle numbers for stock images on Ubuntu 24.04 with default configuration. Dem come from docker stats few minutes after startup. Use dem as starting point for planning, no be benchmark for your workload. Run docker stats --no-stream for your own machine before you trust any figure, including these ones.

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
  }
]

The two columns get different work. idle_mb na the memory wey container dey use when e no dey do anything. budget_mb na the amount you suppose reserve when you dey plan, because real usage no be idle. PostgreSQL dey idle near 45 MB and need 512 MB when connections, sorts, and cache don dey active. Use the budget column for planning. Use the idle column for debugging.

Notice the pattern for those 7 rows. nginx dey idle at 8 MB, while Nextcloud dey idle at 210 MB. The proxy wey dey in front of your applications almost no dey use memory. Na the database and PHP application be the ones wey determine the size of the machine you need.

One warning about docker stats: the memory figure include page cache wey the container's own file reads bring into memory. So e go increase for some time after startup, then settle. Monitor am for one hour before you decide say something dey leak.

How VPS size be: wetin fit for 2 GB, 4 GB and 8 GB

First remove the host own share. The kernel, systemd, journald, sshd and the Docker daemon all dey use the same RAM wey your containers dey use, and dockerd with containerd dey hold around 100 MB of am. You also need free memory for page cache, and for the spike when image build or database dump dey run.

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 cover the operating system, the Docker daemon and the extra space wey keep the box responsive under load. Wetin remain na container_mb, and na only that number you fit spend. The reserve dey increase with the plan, from 768 MB for the smallest box to 1536 MB for the biggest one, because bigger box dey run more containers, write more logs and need more page cache.

A 2 GB plan leave 1280 MB for containers. Use 512 MB of am for PostgreSQL and 128 MB for Traefik, and half of the memory don already finish. The rest na two small applications at roughly 256 MB each. Na real and useful server be this. E no get enough space for Nextcloud and search cluster on top.

A 4 GB plan leave 3072 MB, and that one fit carry database, reverse proxy, three applications and monitoring container at the same time. Na the smallest size wey make sense for anything wey matter to you, because the spare memory na wetin go absorb bad deploy.

An 8 GB plan leave 6656 MB from its 8192 MB, and the limit usually move from memory go CPU or disk throughput. If the calculation show say your stack no fit, buy the bigger plan instead of trying to tune around the problem: wetin VPS really cost explain wetin the extra gigabytes worth per month.

Two rules dey keep the calculation correct. Put memory limit for every service, so one runaway process no fit carry the whole box down. And no spend the top part of the budget, because docker compose build and pg_dump both need memory for the worst moment. Memory limits for Docker Compose get the syntax and the traps.

Why my container dey exit with code 137?

Na because kernel kill am. 137 na 128 plus 9, and signal 9 na SIGKILL. The container request more memory pass wetin dem allow am, so out of memory (OOM) killer end am.

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

Confirm the cause instead make you guess:

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

"OOMKilled": true mean say container reach im own cgroup limit, and kernel log name the process wey e choose:

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

Na the good case be this, because the damage stop for one container. The bad case na container wey no get limit at all. Without limit, im ceiling na the whole machine, so leak for one service go starve the host, then kernel go choose victim based on size across the whole system. The log line no get the Memory cgroup prefix again and e read Out of memory: Killed process 2417 (postgres). The process wey e choose often na your database, while the container wey leak continue to run. Na why limit for every service matter pass the exact value of any single limit.

Swap dey change the timing, no be the arithmetic. Most VPS images ship without swap. Check with swapon --show; e no print anything at all when swap no dey. Swap file give kernel place to put cold pages, and this buy you some minutes to notice the problem.

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 suppose show non-zero total now for the Swap row. Swap no add RAM. Box wey dey under constant memory pressure fit slow reach point wey you no fit SSH enter am to fix the problem, so treat swap as alarm buffer and fix the sizing.

Why UFW no dey block Docker published port?

Because the traffic no dey reach the chain wey UFW dey guard. When you publish port with -p 5432:5432 or compose ports: entry, the daemon dey write DNAT (destination network address translation) rule inside the nat table and accept rule inside im own DOCKER chain. Packet wey target container dey forward go that container instead of delivering am to host, so FORWARD path dey handle am and e no ever pass through INPUT rules wey UFW write.

You fit watch this happen for server:

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

UFW fit print 5432 DENY IN Anywhere while nat table get DNAT tcp ... to:172.18.0.2:5432 rule for the same port. From another machine, nc -vz your.server.ip 5432 still dey connect. The database dey public internet, but firewall dey show say e no dey there.

The fix na to publish less. Containers wey dey inside one compose project share network and fit reach each other with service name, so database wey only dey serve the application beside am no need ports: entry at all. When you need local access, bind the publish to loopback:

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

After docker compose up -d, nc -vz your.server.ip 5432 from outside go fail, while psql -h 127.0.0.1 -p 5432 for the box still dey work. For healthy small stack, na only reverse proxy dey publish anything, for 80 and 443. Why Docker published ports dey bypass UFW explain DOCKER-USER chain for cases wey you must publish port and still filter am, while UFW firewall basics explain the host rules underneath.

Containers dey disappear after reboot because nothing tell dem to come back.

Container dey create with restart policy no unless you set one. So reboot go leave am stopped, and daemon no go care. Reboot no be rare thing for VPS. Kernel updates from unattended upgrades, provider maintenance, and the OOM sequence above fit all end with one.

Two things must dey true. Daemon must start when system boot:

systemctl is-enabled docker

For normal Ubuntu install, this go print enabled. Then each service need get policy:

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

unless-stopped go bring container back after reboot, and e respect container wey you stop on purpose. always go also restart containers wey you stop deliberately whenever daemon restart. This fit surprise you while you dey debug. Editing the file alone no enough because restart policy dey set when dem create the container. Run docker compose up -d make e recreate the container, then check the live value:

docker inspect my-app | grep -A3 RestartPolicy

Then reboot the box on purpose and run docker compose ps inside the project directory. Stack wey survive planned reboot go survive unplanned one too. If your stack need ordering guarantee or one-shot job during boot, systemd unit na better tool: start Docker Compose during boot get the unit file. To confirm say container wey come back dey really serve traffic, add Compose healthchecks.

Why my VPS disk full?

Docker dey keep everything until you tell am make e no keep am. Every image tag wey you don pull before, every stopped container, every anonymous volume wey recreate leave behind, and every build cache layer dey remain for disk. For root filesystem of 40 GB or 80 GB, wey normal for plans of this size, this fit cause outage within months instead of years.

Full disk no dey look like crash. You fit get no space left on device from container, from apt, from journald and from docker pull within the same hour. PostgreSQL go stop accepting writes. The server still dey up, so e harder to notice than reboot loop.

Check before you delete:

docker system df
df -h /

docker system df dey split the total into images, containers, local volumes and build cache, with RECLAIMABLE column beside each one. For server wey dey build its own images, build cache usually na the biggest line.

docker image prune -a
docker builder prune
docker system df

docker image prune -a dey remove every image wey no container dey use. docker builder prune dey clear build cache. Both safe while services dey run, because anything wey dey use dem go skip am. But docker system prune --volumes no safe, because e dey delete every volume wey no container reference right now. Stack wey you stop for weekend get exactly this setup, and e database volume go delete together with am. Read bind mounts versus named volumes before you type that flag, and take backup first.

Container logs na the quieter way disk dey grow. Default json-file driver no get size limit, so one chatty container fit write gigabytes into /var/lib/docker/containers. Set limit for every container inside /etc/docker/daemon.json:

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

Apply am with sudo systemctl restart docker. This go restart your containers, so choose the right time. The limit go apply to containers wey you create after the change. Recreate the containers wey dey run with docker compose up -d --force-recreate, then confirm:

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

The inspect output suppose show say max-size dey set. If e empty, that container old pass the change and e still dey write without limit.

Habits wey go keep small Docker box healthy

None of this need dashboard or tool wey you gats learn.

  • Run docker system df and df -h / on the first day of every month. Na two commands and thirty seconds, and you go see the trend long before e turn outage.
  • Give every service memory limit, including the ones wey you sure say small. The limit go turn host-wide outage into one container wey restart.
  • Monitor the box from another place, so you go hear about memory or disk pressure before kernel act. Uptime Kuma dey run for container and e dey idle around 95 MB.
  • Back up volumes, no be containers. Container disposable, but volume no be. restic backups for VPS cover schedule and restore test.
  • Pin image tags for compose file and update dem on day wey you choose. With latest, the version wey you get from the next docker compose pull na whatever release that morning.

Small VPS wey dey run Docker fit stay healthy for years when four numbers remain within range: memory budget, list of published ports, restart policy for each service, and free disk space. Everything else na the same Docker wey you already dey run for house.

FAQ

How much RAM I need to run Docker for VPS?

Docker by itself no dey cost plenty. The daemon and containerd together dey use around 100 MB, and na your containers go determine the remaining requirement. First reserve the host share: 768 MB for a 2048 MB box, for the operating system, the daemon and extra headroom. This one leave 1280 MB for containers. Database wey dey use 512 MB, reverse proxy wey dey use 128 MB, plus two small applications fit enter inside that space. Measure your own stack with docker stats --no-stream instead of trusting any published figure.

I fit run Docker for 1 GB VPS?

Yes, if na one or two light containers. Add swap file before you start. Roughly half of a 1 GB box don finish after the operating system and Docker daemon start. The remaining space fit carry small application and reverse proxy, but e no go carry database under real load. Building images for box of that size fit fail or kill another process, so build elsewhere and pull the finished image.

UFW dey protect Docker container?

E no dey protect ports wey you publish. Docker dey write im own DNAT and forward rules. So, packet wey target published container port go forward go the container instead of going to the host. The INPUT rules wey UFW manage no go see that packet. ufw deny 5432 fit dey active while that port still dey answer from internet. Publish to loopback with 127.0.0.1:5432:5432, leave internal services unpublished, or filter for the DOCKER-USER chain.

My containers go restart after VPS reboot?

Only if you create dem with restart policy. Set restart: unless-stopped for each service. Run docker compose up -d so the containers go recreate with the policy. Confirm say systemctl is-enabled docker print enabled. Then reboot deliberately and check docker compose ps. Restart policy wey you never test no be reliable restart policy.

How often I suppose prune Docker images?

Once every month dey enough for most small servers. You fit also do am whenever docker system df report reclaimable space wey you need. docker image prune -a and docker builder prune both safe while services dey run, because dem skip images and cache wey dey in use. Avoid docker system prune --volumes unless you know exactly which volumes no get reference, because e go delete data for any stack wey happen to dey stopped.