deploy · tutorial · vps
Next.js 'Killed' on 1GB VPS? The 5-Minute Swapfile Fix
Next build dies with Killed on tiny boxes because linking eats 1.5GB RAM. A 2GB swapfile fixes it in five minutes. Commands inside.

Killed with no other explanation
You push to the $5 box, the build spins through "Compiled successfully," starts "Collecting page data," and dies mid-word:
KilledNo stack trace. No error code. Just Killed — the kernel's OOM assassin ending your build because RAM ran out.
Locally on your 16GB Mac it never happens. On 1GB of VPS it happens every time past a dozen routes.
The nextjs oom fix vps playbook is three moves: confirm it is memory, add swap, cap the hungry processes. In that order, because guessing wastes afternoons.
Where 1GB actually goes
| Consumer | Idle RAM | Build-time spike |
|---|---|---|
| OS + sshd + daemons | ~150 MB | ~150 MB |
| PM2 app serving traffic | ~250 MB | ~250 MB (still live!) |
next build workers |
0 | 600–900 MB at linking |
| Node default heap per worker | — | Up to 2 GB allowed |
| Total vs 1 GB box | ~400 MB fine | ~1.4 GB — OOM killer fires |
Three details make it bite harder than the table suggests:
- The app stays up during the build. PM2 keeps serving users while
next buildruns beside it — noble, and 250MB you cannot borrow. The spike lands on top of live traffic, not an empty box. - Linking parallelizes against you. Next spawns a worker per core. Two vCPUs each holding hundreds of MB of module graph peaks exactly at "Collecting page data" — the step where every log of this death ends.
- Bigger boxes mask it until the bill lands. The standard advice is "upgrade to 2GB," which doubles the server cost forever to cover a 90-second spike. Swap covers the spike for $0.
The kernel is not wrong to kill. Something had to die, and the build was the hungriest. Give it somewhere harmless to overflow.
Confirm, swap, cap
These are the same steps BoringPush's setup runs on every fresh CX22.
Step 1 — Confirm the killer, not a bug.
dmesg | tail -20 | grep -i "oom\|killed process"
free -hYou want a line like Out of memory: Killed process <pid> (node). If it names node during next build, this post is your fix. If it names your database or PM2 at idle, you have a leak instead — read pm2 monit first and fix the leak before adding swap.
Step 2 — Add the 2GB swapfile.
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
sudo sysctl vm.swappiness=10fallocate reserves the space instantly, 600 keeps the page contents private, fstab survives reboots, and swappiness=10 tells the kernel to prefer RAM and touch swap only for spikes — exactly our build burst. Verify with free -h: the Swap row reads 2.0G. Total time: under five minutes, no reboot.
Step 3 — Cap the heap so builds stay polite.
# in the deploy hook, before npm run build:
export NODE_OPTIONS="--max-old-space-size=1536"
npm run build1.5GB of heap across 1GB RAM + 2GB swap leaves the live app untouched while linking peaks. Add output: "standalone" in next.config.js so the runtime server carries only traced files — smaller boot, smaller resident memory, faster reloads. The build gets slower by a minute (swap is disk, not RAM); the site never notices.
That's the play — even the smallest box builds clean.
Before and after on 1GB
| Check | Before swap | After swap + cap |
|---|---|---|
npm run build, 20 routes |
Killed at page data |
Completes in ~3 min |
free -h during build |
50 MB free, then death | 800 MB swap used, alive |
| Live traffic during build | Dead with the builder | Served — PM2 untouched |
| Monthly cost delta | $0 but broken | $0 and green |
The honest trade: swap is NVMe wearing per page-write, and a box that lives in swap (not spikes into it) is a box that needs RAM. Watch vmstat 5 during a normal hour — if si/so columns stay busy outside builds, upgrade to 2GB instead of leaning on disk.
Swap is a shock absorber, not a second engine. And when traffic truly outgrows the small box, the same repo moves up one plan with zero code changes.
FAQ
Is a nextjs oom fix vps swapfile safe for an SSD?
Yes for this pattern. Build spikes page a few hundred MB for ~90 seconds per deploy — trivial wear on modern NVMe rated for hundreds of terabytes written.
What kills SSDs is constant swap churn from an undersized box running hot 24/7. Spike-only swap is the intended use; chronic swap is the upgrade signal.
Why not just set NODE_OPTIONS and skip the swapfile?
The heap cap alone shrinks Node's appetite but the box still has only 1GB against a live app plus two build workers. Capping without swap turns Killed into slower Killed.
Swap without capping works but pages aggressively. Together they hold: the cap bounds the spike, swap absorbs it.
Does output: standalone change how I deploy?
Slightly, for the better: next build emits .next/standalone/server.js plus traced node_modules, and PM2 starts that file instead of the full install. Smaller boot image, less resident RAM, fewer native-module surprises. The deploy needs one path change, which the BoringPush deployment guide documents.
How do I know the OOM killer struck versus a normal crash?
dmesg names it explicitly: Killed process with oom_score context, timestamped to your build. Normal crashes leave JS stack traces; the OOM killer leaves one word and a kernel timestamp. Match the timestamp to your push and the diagnosis is certain in thirty seconds.
Will two PM2 instances plus a build fit on 1GB at all?
Yes, with this exact combination: 2GB swap, 1536MB heap cap, standalone output, builds scheduled off-peak. It is the tightest viable shape — comfortable, not roomy. The moment you add a second service (workers, search), take the 2GB plan.
BoringPush runs 2 instances on 1GB in dev and 2–4 on 2GB in prod for exactly this reason.
When should I stop fixing and just buy RAM?
When vmstat shows swap activity outside build windows, or reloads start timing out under normal load. RAM is $2–4/mo per GB at Hetzner-class pricing — cheaper than any hour you spend tuning. The swapfile buys you months of growth; it was never meant to buy years.
Keep reading
- PM2 Reloads Nobody Feels: Next.js With Zero Downtime — the reload config this memory headroom protects
- git push production deploy: Skip the 9-Minute CI Queue — the hook where the heap flag lives
- Deploy Next.js on Hetzner $5 by Dinner Tonight — fresh-box setup, swap included from step one
+ all the boring stuff (analytics, auth, SQLite ↔ Supabase, full marketplace, webhooks)
Command from your phone.
Built so autonomous AI agents build what you want without breaking what's already paying you.
- Built-in analytics — cookie-free mode, $0 forever, no PostHog bill
- SQLite ↔ Supabase: one setting, same field names
- Full digital marketplace & feedback boards included
- 3 payment rails: Stripe, LemonSqueezy & Dodo Payments
- Agent fences: lint gate, read-only data access, auto-rollback
- Ship from your phone via SSH & WireGuard/Tailscale in under a minute
$199
Get BoringPushOne-time payment · Lifetime updates · Unlimited projects.
Keep reading


