TutorialsJune 12, 20263 min read

How to deploy a Next.js app on a VPS

Self-host Next.js on your own server for full control and predictable cost. Learn to build, run with PM2, reverse-proxy with Nginx, and add HTTPS.

NBy Nxeon

You don't need a managed platform to run Next.js. Self-hosting on a VPS gives you full control, no per-request pricing, and no vendor lock-in. The production Next.js server runs great behind Nginx with a process manager. Here's the complete deployment.

Step 1: Prepare the server

On a fresh Ubuntu VPS, update and add a deploy user:

sudo apt update && sudo apt upgrade -y
sudo adduser deploy && sudo usermod -aG sudo deploy

Install a current Node.js LTS:

curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install nodejs -y

Step 2: Get the code and build

Clone your repo, install dependencies, and create the production build:

git clone https://github.com/you/my-next-app.git
cd my-next-app
npm ci
npm run build

The build compiles your app into an optimized production bundle. Set production environment variables in a .env or .env.production file — things like your database URL and API keys — before building if they're needed at build time.

Step 3: Run the production server with PM2

Next.js has its own production server started with next start (via your npm start script). Keep it alive with PM2:

sudo npm install -g pm2
pm2 start npm --name my-next-app -- start
pm2 startup systemd
pm2 save

By default Next.js listens on port 3000. Confirm it's up:

pm2 status
curl http://localhost:3000

If you need a different port, set it: pm2 start npm --name my-next-app -- start -- -p 4000.

Step 4: Reverse-proxy with Nginx

Expose the app on ports 80/443 through Nginx rather than opening 3000 to the world. Install Nginx and create /etc/nginx/sites-available/my-next-app:

server {
    listen 80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://localhost: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-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

Enable and reload:

sudo ln -s /etc/nginx/sites-available/my-next-app /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Step 5: Add HTTPS

Point your domain's A record at the VPS, then let Certbot handle TLS:

sudo apt install certbot python3-certbot-nginx -y
sudo certbot --nginx -d example.com -d www.example.com

Step 6: Firewall

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

Serving images and caching

Next.js image optimization and server rendering benefit from real CPU and fast disk. On a NVMe VPS the build step and on-demand rendering are noticeably quicker than on slower storage. If your site is image-heavy, give it a bit more RAM so the Node process has headroom.

Updating deploys

Your update routine:

cd my-next-app
git pull
npm ci
npm run build
pm2 restart my-next-app

For zero-downtime, PM2 restarts are near-instant, and Nginx keeps serving until the new process is ready.

Optional: run it in Docker

If you prefer containers, add a Dockerfile using Next.js standalone output and run it with docker run or Compose, still behind Nginx or Caddy. Either way the front-end proxy pattern is the same.

Why self-host Next.js

You get predictable flat-rate pricing, no cold starts, full access to the filesystem and background processes, and the freedom to run whatever database and services you like alongside the app. On a Nxeon full-root NVMe VPS, a Next.js deploy is a one-time setup followed by four-line updates — your app, your server, your rules.

#nextjs#deployment#pm2#nginx#vps

Deploy your first server in under a minute

No credit card required to get started. Spin up a VPS, break things, and only pay for what you keep running.