SELFHOST.COMPUTER

Self-hosting on a small VPS: a RAM budget that works

#self-hosting#docker#performance

Small servers rarely run out of CPU. They run out of memory, and the Linux OOM killer then picks a process to shoot, usually your database. Here's how to see where your RAM goes, how to cap it, and how to survive a spike. Not sure how much you need in the first place? The VPS size calculator adds it up for you.

1. See what's really used

free -h
docker stats --no-stream

Look at the available column in free, not free. Linux fills spare RAM with disk cache and gives it back on demand, so a "full" server can be perfectly healthy. docker stats shows each container's real usage, which is where the surprises are.

2. Give every container a ceiling

One leaky container shouldn't take the whole box down. In Compose:

services:
  app:
    image: example/app
    mem_limit: 512m
    restart: unless-stopped

If the app goes over, Docker kills and restarts just that container instead of the kernel picking a victim. Set the limit to about 1.5x what docker stats shows at a normal busy time.

2b. Prefer SQLite where the app allows it

A separate PostgreSQL container is often the single biggest line item on a small server. Vaultwarden, Uptime Kuma, Forgejo and Miniflux-sized workloads run fine on SQLite or a shared database. If you need Postgres, run one instance and give each app its own database in it instead of one container per app.

3. Add swap as a safety net

Swap isn't extra RAM. It's a place for memory that's rarely touched, plus a buffer that turns a sudden crash into a slowdown. Many VPS images ship without any:

swapon --show

Empty output means none. Add a file:

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

Then tell the kernel to prefer RAM and only swap when it has to:

echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swappiness.conf
sudo sysctl --system

If a server is swapping constantly (watch the si and so columns in vmstat 5), swap is hiding a real shortage. Buy more RAM instead.

4. Keep the logs from eating the disk

Docker's default log driver has no size limit, and a chatty container can fill a small disk. Cap it for all containers in /etc/docker/daemon.json:

{
  "log-driver": "local",
  "log-opts": { "max-size": "10m", "max-file": "3" }
}
sudo systemctl restart docker

The new default only applies to containers created after the change, so recreate existing ones with docker compose up -d --force-recreate.

5. Things that eat RAM and don't look like it

  • Search and indexing features (Nextcloud previews, Immich machine learning). Turn them off or run them elsewhere.
  • JVM apps (Minecraft, Jenkins, Elasticsearch). They grab a heap up front. Set -Xmx on purpose.
  • Too many "tiny" services. Ten containers at 100 MB each is 1 GB before the OS.

When to stop tuning

If you're past about 80% of RAM at normal load, or tuning is taking more of your time than the extra memory would cost, resize. Compare plans on the VPS voucher page, and remember the codes there take a month off the first bill. Once it's stable, set up backups.

< all posts · netcup voucher codes

Keep reading