Knowledgebase › Docker on a small VPS — what to expect, what to avoid

Docker on a small VPS — what to expect, what to avoid

Docker (and its rootless cousin Podman) is a popular way to run multiple applications on a single VPS in isolated containers. On a small VPS — say 1 vCPU and 2 GB RAM — Docker is workable but has tradeoffs that aren't obvious until you hit them. This article covers what realistically fits, what doesn't, and the operational patterns that make Docker on a small VPS pleasant rather than painful.

The honest baseline

Each Docker container is essentially a process group with isolated namespaces and a virtual filesystem layer. The container itself adds minimal overhead — kernel features doing the work, not a hypervisor or VM. But every container is also a real process tree that has to fit in your VPS's RAM and CPU.

Rough memory estimates per container (active, not just running):

ContainerIdle RAMUnder light load
nginx (static site) 15 MB 30 MB
PostgreSQL 15 (small DB) 40 MB 200-500 MB
Redis 10 MB 20-100 MB
Node app (Express) 40 MB 100-300 MB
Python app (Django + gunicorn) 80 MB 200-500 MB
Java app (Spring Boot) 300 MB 500-1500 MB
Nextcloud (PHP-FPM + Apache) 200 MB 400-800 MB
WordPress (PHP-FPM + nginx) 120 MB 250-500 MB

On a 2 GB VPS, you can comfortably run 3-5 small services. On a 1 GB VPS, 1-2 small services. Java applications break this rule — budget heavily.

Install Docker (or Podman)

Debian / Ubuntu:

# Docker's official repo (current, supported):
apt update
apt install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian $(. /etc/os-release && echo $VERSION_CODENAME) stable" \
  | tee /etc/apt/sources.list.d/docker.list
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

AlmaLinux: Podman is preinstalled and is the recommended container engine (rootless, daemonless, CLI-compatible with Docker). Use it directly:

podman run hello-world

Most of this article applies to both — substitute podman for docker mentally.

The four traps

1. The disk fills up silently

Docker accumulates layers, stopped containers, dangling images, and volume data. On a small VPS this can fill 20 GB faster than you'd expect.

# See what's using space:
docker system df

# Clean up aggressively:
docker system prune -a --volumes

Read the warning--volumes deletes volumes not attached to a running container, which includes your database data if the database container is stopped at the moment.

Set up a weekly cleanup cron once you're comfortable with what you want to keep:

# /etc/cron.weekly/docker-prune
#!/bin/sh
docker image prune -a -f --filter "until=168h"

2. Memory pressure kills containers without obvious cause

When the VPS hits memory pressure, the kernel's OOM-killer picks something to kill. Docker containers usually score high (large process trees, no init protecting children) so they get reaped first.

Symptoms: a container that was running yesterday is now in "Exited (137)" state. Exit code 137 = killed by OOM.

Fixes:

  • Limit each container's memory with --memory:
    docker run --memory=256m --memory-swap=256m myimage
    Forces the container to OOM internally rather than triggering host-wide pressure.
  • Add swap on the host. A small swap file (1-2 GB) absorbs short-term spikes. Performance is degraded when you're actually swapping, but it beats OOM-kills.
  • Bigger VPS. If you're consistently OOM-killing, you're undersized for the workload.

3. Bridge networking and the firewall

Docker creates iptables/nftables rules to handle bridge networking and port publishing. These can conflict with UFW or firewalld rules — Docker's rules are added before yours, so a -p 80:80 publishes port 80 to the world even if UFW says block it.

Two patterns:

  • Bind to localhost only when you don't want public access:
    docker run -p 127.0.0.1:8080:80 myimage
    Then reverse-proxy through nginx/Caddy on the host.
  • Disable Docker's iptables rules entirelyand manage them yourself (advanced, only if you really understand iptables):
    # /etc/docker/daemon.json
    { "iptables": false }

4. The image-pull bandwidth is real

Pulling a Node or Python base image is 100-300 MB; an Ubuntu image is 70 MB. If your VPS plan includes generous bandwidth this doesn't matter, but if you're rebuilding images frequently (CI, deploy-on-push) the egress to Docker Hub can add up.

Use multi-stage builds to keep your final images small (only the runtime, not the build dependencies) and cache base images locally with docker build --pull=false when you know the base hasn't changed.

Recommended pattern for a small-VPS deployment

For most "run a few services" use cases, this works well:

  1. Caddy or nginx on the host (not in a container) — handles TLS, reverse-proxies to containers on localhost-bound ports.
  2. Each application in its own container, bound to 127.0.0.1:PORT (not 0.0.0.0).
  3. Postgres / Redis on the host — databases are stateful and harder to manage in containers on small VPS where memory matters. apt install postgresql and use the host instance.
  4. docker compose for grouping related services with shared networks/volumes. Avoid running multiple compose stacks on a single small VPS; one is plenty.
  5. Volumes for state, with mountpoints in a known location like /data/<service> so off-host backups can sweep one directory.

Alternatives to Docker for small VPS

  • systemd services — for single Go/Rust/Java binaries, you don't need a container; just a systemd unit file. Lower overhead, fewer moving parts.
  • Podman with --quadlet — systemd-managed containers; nice if you like Podman's rootless model.
  • Nix / nix-darwin — declarative system config without containers. Steeper learning curve, powerful once you're in.

Docker isn't the only answer, and on a small VPS it's not always the best one. But when you need isolation between services, a documented runtime environment, or the ability to docker pull something you didn't write — it's hard to beat for ease.

Also Read

« « Back

Powered by WHMCompleteSolution