Europe/London
BlogAugust 12, 2026

Deploying Next.js Standalone with systemd, the Unfancy Way

Dubz
Most Next.js deployment advice ends at "push to a platform." Which is fine until you're running a small fleet and the platform bills multiply. The other path (a standalone build on a VPS under systemd) is documented in fragments everywhere and nowhere properly, so here is the whole pattern, as actually running in production right now. next build with output: "standalone" in next.config.mjs produces a self-contained folder: a server.js, a minimal node_modules, and a .next directory. The app is a single Node process. node server.js is the entire runtime. But here's the tripwire everyone hits: standalone does not copy your static assets. The .next/static folder and public/ are deliberately left out of the standalone output. Your app boots, renders HTML, and serves broken CSS and 404 images. Every guide forgets to mention it; every first-time deployer finds out live. The fix, and the heart of the whole setup, is a systemd unit that assembles the runtime before starting:
Ini
[Service]
Environment=PORT=3102
Environment=NODE_ENV=production
WorkingDirectory=/srv/mysite/.next/standalone
ExecStartPre=/bin/bash -c 'install -d .next/static public && cp -a ../static/. .next/static/ && cp -a ../../public/. public/'
ExecStart=/usr/bin/node server.js
Restart=always
RestartSec=5
Read the ExecStartPre as the deployment logic: before every start (fresh boot or crash-recovery), copy the static assets into the standalone directory. The app becomes self-healing: kill the process, restart the box, whatever. Systemd brings back a complete app every time. Restart=always with a 5-second backoff is the difference between "down" and "who noticed." Then nginx fronts it: a standard reverse proxy to 127.0.0.1:3102 with WebSocket upgrade headers and X-Forwarded-* set honestly, certificates via certbot. Deployment itself is: pull, npm ci, npm run build, systemctl restart. From commit to live in the time it takes to make tea. No preview deployments. No instant rollbacks: rollback is git checkout <sha> && rebuild && restart, which is fine but manual. No edge network; your users hit one box in one country. If any of those are load-bearing for you, the platform is worth the bill. What you get: full control, a debuggable production story (journalctl -u mysite shows you everything the app has ever said), no runtime surprises injected by a vendor, and a monthly bill measured in pocket change rather than salary. People assume this pattern is nostalgia. It's the opposite: standalone output is one of Next.js's most underrated features, and systemd has quietly become one of the best init systems ever shipped. The unfancy stack of 2026 is genuinely good engineering: one build artefact, one process manager that has survived twenty years of fashions, one proxy. Boring on purpose, observable by default, and cheap enough that a small operation can run a fleet. Ship your side project to a platform if you like. But the boxes are lovely this time of year.
Share this post: