Add Swap to a Small Django Server
Add a swap file to a 1-2 GB Django server so pip installs, migrations, and traffic spikes do not trigger the OOM killer: sizing, fstab, swappiness, Docker and cloud caveats, and when swap is the wrong fix.
Problem statement
Small VPS plans with 1 or 2 GB of RAM run a Django app with Gunicorn, Nginx, and sometimes PostgreSQL and Redis on the same machine. Normal traffic fits, but occasional peaks do not: pip install compiling a package, collectstatic with heavy post-processing, a Celery task loading a large file, or a few extra concurrent requests. When memory runs out, the kernel's OOM killer terminates a process, often PostgreSQL or a Gunicorn worker, and the site fails.
Many cloud images ship without swap. A modest swap file gives the kernel somewhere to move rarely used memory pages during short peaks, turning a crash into a brief slowdown. This guide adds one correctly and explains when more memory, not swap, is the real fix.
- Check current memory and swap:
free -handswapon --show. - Create a 1-2 GB swap file with
fallocate, set permissions to600, runmkswap, andswapon. - Add it to
/etc/fstabso it survives reboots. - Set
vm.swappinessto a low value (for example 10) so swap is used for idle pages, not active ones. - Monitor swap usage: occasional use is fine, sustained swapping means resize the server or reduce workers.
- Do not rely on swap to run more Gunicorn workers than RAM can hold.
Step-by-step solution
1. Check the current state
free -h
swapon --show
cat /proc/sys/vm/swappiness
If swapon --show prints nothing, there is no swap. Also check for recent OOM kills, which confirm the need:
sudo journalctl -k | grep -i -E "out of memory|killed process" | tail
2. Create the swap file
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
swapon --show
Sizing for small Django servers: 1 GB of swap for a 1 GB server, 1-2 GB for a 2 GB server. More swap rarely helps: if the system needs several gigabytes of swap, it is thrashing and responses will be extremely slow anyway.
On filesystems where fallocate does not produce a usable swap file (some older XFS or Btrfs setups), use dd instead:
sudo dd if=/dev/zero of=/swapfile bs=1M count=2048 status=progress
Btrfs needs additional steps (a dedicated subvolume or btrfs filesystem mkswapfile on recent versions).
3. Make it permanent
echo "/swapfile none swap sw 0 0" | sudo tee -a /etc/fstab
sudo findmnt --verify
Without the fstab entry, swap disappears on the next reboot, often unnoticed until the next OOM kill.
4. Tune swappiness
echo "vm.swappiness = 10" | sudo tee /etc/sysctl.d/99-swappiness.conf
sudo sysctl --system
cat /proc/sys/vm/swappiness
A low value tells the kernel to prefer dropping file cache over swapping application memory, so swap holds pages that are genuinely idle (a Celery process that has not run a task in hours, parts of PostgreSQL's memory that are rarely touched), while active Gunicorn workers stay in RAM.
5. Fit the app to the RAM you have
Swap buys headroom; it does not create capacity. Keep the steady state within physical memory:
- Measure per-worker memory:
ps -o pid,rss,cmd -C gunicorn. Multiply by the worker count and add PostgreSQL, Redis, Celery, and Nginx. - On a 1 GB server, two or three Gunicorn workers are typically the limit with PostgreSQL on the same machine.
- Use
--max-requestswith jitter to recycle workers that grow over time. - Limit Celery concurrency; each prefork child is a full process.
- Tune PostgreSQL's
shared_buffersandwork_memdown to match a small machine. - Build Docker images in CI instead of on the server; builds are among the largest memory spikes.
6. Monitor swap
vmstat 5 5
The si and so columns show pages swapped in and out per second. Occasional non-zero values during deploys are fine. Continuous swapping during normal traffic means the server is under-provisioned: resize, reduce workers, or move PostgreSQL to a separate server.
Add alerts for swap usage above 50 percent and for any OOM kill.
Verification check:
free -h
swapon --show
grep swapfile /etc/fstab
cat /proc/sys/vm/swappiness
Reboot once during a maintenance window and confirm swap is active again.
Explanation
Linux can move memory pages that have not been used recently to swap, freeing RAM for active processes. Without swap, when memory is exhausted, the only options are dropping file cache and, once that is gone, killing a process. For small Django servers, where memory peaks are short and predictable (deploys, periodic tasks), a small swap area with low swappiness prevents those kills while keeping active processes in RAM. Swap is slow compared with RAM, which is why it is a safety net, not a way to run more than the machine can hold.
Edge cases / notes
Containers and memory limits
Docker containers can use host swap unless limited. A container memory limit without a swap limit (memory-swap equal to memory) means the container is killed at the limit regardless of host swap. Decide deliberately per service.
Kubernetes nodes
Kubernetes traditionally requires swap to be disabled on nodes; newer versions support swap under specific configurations. Follow your cluster's guidance rather than adding swap to nodes directly.
Cloud burst credits and disks
On instances with network-attached disks, heavy swapping also consumes disk I/O, which may be limited or billed. Another reason to treat sustained swapping as a sizing problem.
When to automate this
Add swap creation to your provisioning (cloud-init or Ansible) for small instance types, and ship the swappiness setting with it. Include swap usage and OOM kill alerts in your standard monitoring.
Next steps
- If processes are already being killed, see Fix Gunicorn Workers Killed by the OOM Killer.
- Size memory properly with Size a Server for a Django App: Capacity Planning.
- Choose worker counts for small servers with How to Optimize Gunicorn Workers for Django.
- Small plans are common on Deploy Django on AWS Lightsail.
FAQ
Should a production Django server have swap?
On small servers, a modest swap file is a sensible safety net against short memory peaks. It should not be used continuously.
How much swap does a Django server need?
About 1-2 GB for servers with 1-2 GB of RAM. Larger swap mostly lets a thrashing server stay slow for longer.
What swappiness value should I use?
A low value such as 10 keeps active processes in RAM and uses swap mainly for idle pages.
Will swap let me run more Gunicorn workers?
Not usefully. Workers that are swapped out respond very slowly. Size workers to fit in physical RAM.