Swap, OOM kills and memory limits on a small VPS
- Add a small swap file
- Set memory limits per container
- Leave headroom
- Protect the database
- Know when it happened
- Runtime-specific settings
- When to resize
Small VPS plans with 2 to 4 GB of RAM can run a surprising number of containers. They also fail in a particular way: one app grows, the kernel runs out of memory, and the OOM killer picks a victim, often your database. A few settings make this predictable.
Add a small swap file
Many cloud images ship with no swap. A modest swap file gives the kernel room to move rarely used pages out of RAM:
fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile && swapon /swapfile
echo "/swapfile none swap sw 0 0" >> /etc/fstab
Set vm.swappiness=10 in /etc/sysctl.d/ so swap is a safety net, not a habit. Swap will not make an undersized server fast, but it turns sudden crashes into gradual slowdowns you can notice.
Set memory limits per container
Without limits, every container can use all host memory. In Compose:
services:
api:
mem_limit: 512m
worker:
mem_limit: 768m
Now a leak in the worker kills the worker, which restarts, instead of the kernel killing Postgres.
Leave headroom
Add up the limits and keep the total below about 80% of RAM. The host itself, Docker, the reverse proxy and page cache need the rest. Databases benefit a lot from page cache, so starving it hurts performance even without OOM kills.
Protect the database
Give the database a realistic limit and tune it to fit inside: for Postgres, shared_buffers around 25% of its limit is a common starting point. You can also lower its OOM priority with oom_score_adj: -500 in Compose so the kernel prefers other victims.
Know when it happened
Check for kills with:
dmesg -T | grep -i "killed process"
docker inspect --format '{{.State.OOMKilled}}' <container>
Alert on container restarts. A service that restarts every few hours is usually hitting its memory limit.
Runtime-specific settings
- Node.js: set
--max-old-space-sizebelow the container limit - JVM: use
-XX:MaxRAMPercentage=75 - Python workers: recycle them with a max requests setting
When to resize
If swap is used constantly or limits are hit daily under normal load, the server is too small. Upgrading one plan size is usually cheaper than the time spent squeezing.
Memory per dollar differs a lot between providers. This guide to choosing a VPS provider compares plans for self-hosting.
Disclosure: I work on Peon.
Write a comment