Deploy a Next.js app on Hostinger VPS (step by step)
A complete walkthrough: provision the server, install Node 24, build with standalone output, keep it alive with PM2, put Nginx and free HTTPS in front, and script your redeploys.
Why a VPS, and not shared hosting
A Next.js app that uses server components, route handlers, ISR or any kind of server rendering is a long-lived Node process. Shared hosting is built to run PHP scripts that start and die inside a single request โ there is no supervisor to keep `node` running, no way to bind a port, and usually no SSH. That is the whole reason this guide starts with a VPS.
The exception is a fully static site. If your app runs with `output: 'export'` and has no server features at all, you can drop the generated HTML onto any shared plan and skip everything below. Everything else needs a real server.
The entry-tier KVM 1 is enough for a small-to-medium Next.js site: 1 vCPU, 4 GB RAM and 50 GB NVMe. RAM is the spec that matters most here, because `next build` is memory-hungry and 4 GB at this price point is unusually generous.
What you need before you start
Five minutes of setup saves an hour of debugging later.
- A VPS running Ubuntu 24.04 LTS or 26.04 LTS โ pick the OS template when you create the server.
- A domain with an A record pointing at your VPS's IP address. Set this up first; DNS takes time to propagate and Let's Encrypt will fail without it.
- Your app in a Git repository the server can reach.
- Your SSH public key added to the server during provisioning โ far safer than a root password.
Step 1 โ Log in and create a deploy user
Running your app as root is the single most common mistake in VPS tutorials. Create an unprivileged user that owns the app, and reserve root for package installs.
ssh root@YOUR_SERVER_IP
adduser --disabled-password --gecos "" deploy
usermod -aG sudo deploy
# Copy your SSH key across so you can log in as deploy
rsync --archive --chown=deploy:deploy ~/.ssh /home/deploy/ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw --force enable
ufw statusStep 2 โ Install Node.js 24 LTS
As of September 2026, Node 24 (Krypton) is the Active LTS line and the right default for new deployments. Node 22 is in maintenance until April 2027, so existing apps are not in danger, but new servers should start on 24.
Ubuntu's own `apt` repository ships an old Node. Use NodeSource instead so you get the version you actually asked for.
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt-get install -y nodejs
node -v # v24.x.x
npm -vStep 3 โ Add swap if you are on a 1 vCPU plan
`next build` can spike well past 1 GB of memory on a mid-sized app. With 4 GB of RAM you are usually fine, but a swap file costs nothing and turns a hard out-of-memory crash into a merely slow build. On smaller servers it is not optional.
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
free -h # confirm swap is listedStep 4 โ Clone, configure and build with standalone output
Setting `output: 'standalone'` in your Next config is the highest-value change you can make for a VPS deployment. Next.js traces exactly which files each page needs and copies them โ plus a minimal `server.js` โ into `.next/standalone`. You end up shipping a fraction of `node_modules`, which means faster deploys and a smaller disk footprint.
There is one catch that trips up almost everyone: the standalone folder does not include `public` or `.next/static`, because Next.js assumes a CDN will serve them. Copy them in yourself and `server.js` will serve them automatically.
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
output: "standalone",
};
export default nextConfig;sudo mkdir -p /var/www && sudo chown deploy:deploy /var/www
cd /var/www
git clone https://github.com/you/your-app.git myapp
cd myapp
npm ci
npm run build
# The bit everyone forgets:
cp -r public .next/standalone/
cp -r .next/static .next/standalone/.next/Step 5 โ Keep it running with PM2
Your app needs to restart when it crashes and come back after a reboot. PM2 handles both. Point it at the standalone `server.js` rather than `next start` โ the minimal server boots faster and does not need the full dependency tree.
Bind to `127.0.0.1`, not `0.0.0.0`. The Node process should only be reachable from Nginx on the same machine; anything else means people can bypass your reverse proxy by hitting port 3000 directly.
sudo npm install -g pm2
cd /var/www/myapp
PORT=3000 HOSTNAME=127.0.0.1 pm2 start .next/standalone/server.js --name myapp
pm2 save
pm2 startup systemd
# PM2 prints a sudo command โ copy it, run it, then run 'pm2 save' again.pm2 status
pm2 logs myapp --lines 100
pm2 restart myapp
pm2 monitStep 6 โ Put Nginx in front
The Next.js self-hosting documentation recommends a reverse proxy rather than exposing the Node server directly, and the reasoning is worth understanding: Nginx absorbs malformed requests, slow-connection attacks, oversized payloads and rate limiting, so your Node process spends its CPU rendering pages instead of validating junk traffic.
The `location /_next/static/` block below is an easy win. Those files are content-hashed and immutable, so letting Nginx serve them straight off disk with a long cache header takes that load off Node entirely.
sudo apt-get install -y nginx
sudo nano /etc/nginx/sites-available/myappserver {
listen 80;
server_name example.com www.example.com;
# Immutable, content-hashed assets โ let Nginx serve them directly.
location /_next/static/ {
alias /var/www/myapp/.next/static/;
expires 365d;
add_header Cache-Control "public, max-age=31536000, immutable";
access_log off;
}
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}
}sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginxStep 7 โ Free HTTPS with Let's Encrypt
Certbot edits your Nginx config, installs the certificate and sets up automatic renewal in one command. Renewal runs on a systemd timer, so this is genuinely fire-and-forget.
sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
# Confirm auto-renewal works
sudo certbot renew --dry-runStep 8 โ A one-command redeploy
Doing all of the above by hand every time you ship is how mistakes happen. Save this script and your deploys become a single command.
#!/usr/bin/env bash
set -euo pipefail
cd /var/www/myapp
git pull origin main
npm ci
npm run build
# Standalone output does not include these โ copy them every build.
cp -r public .next/standalone/
cp -r .next/static .next/standalone/.next/
pm2 restart myapp --update-env
pm2 save
echo "Deployed at $(date -Iseconds)"Gotchas worth knowing before they bite
Most Next.js VPS problems come from a short list of surprises. These are the ones that cost real time.
- ISR and the data cache are written to local disk on the instance. That works perfectly for one server, but if you ever scale to two, they will serve each other stale pages โ at that point you need a shared cache handler.
- `next/image` optimizes at runtime and needs `sharp`, which on glibc-based Linux can use a lot of memory. If RSS creeps up over days, this is the usual culprit.
- Dynamically rendered pages send `private, no-cache, no-store` automatically. Do not add a caching layer in Nginx that overrides this, or you risk serving one user's page to another.
- Copy `public` and `.next/static` on every single build, not just the first. A stale copy means your CSS silently belongs to last week's deploy.
- Check your VPS renewal price before the term ends โ the introductory rate is not the long-run rate.
Where the cost lands
One KVM 1 server comfortably runs several Next.js apps side by side โ add another PM2 process on port 3001, another Nginx server block, another certificate. That is the real economy of a VPS versus per-service platform pricing: the second app is free.
If you are provisioning one, coupon WEBHOST2026 applies the discount automatically through the links on this site, and longer terms carry the lower monthly rate.
Ready to save with coupon WEBHOST2026?
Get my discount link โKeep reading
Host a MERN stack app cheaply in 2026
Three realistic ways to put MongoDB, Express, React and Node online for roughly the price of a coffee โ with the security steps most tutorials skip.
ComparisonsHostinger VPS vs a $5 DigitalOcean droplet for a side project
The $5 droplet is $6 now, and it ships 1 GB of RAM. Here is the honest spec-and-cost comparison, including the renewal maths that most affiliate posts leave out.