How to Deploy a Next.js SSR Website on Linux: A Complete Guide

Building your web app with Next.js gives you the power of server-side rendering, hybrid static + dynamic rendering, and React’s ecosystem. But deploying an SSR Next.js app isn’t quite the same as deploying a plain frontend SPA. You need a running Node.js server, proper reverse proxying, process management, SSL, and deployment scripts.

In this guide, I’ll walk you through step by step how to deploy a Next.js SSR site on a Linux (Ubuntu, Debian, or similar) environment. By the end, you’ll have a production-ready SSR deployment, with automated restarts and secure HTTPS.

1. Prerequisites & System Setup

Before deploying, make sure you have:

  • A Next.js project configured and tested locally
  • A Linux server (VPS or cloud instance) with SSH access
  • A domain name pointing (A/AAAA) to that server
  • Basic familiarity with shell, SSH, and Linux commands

On your local development side, in package.json, include scripts:

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start"
  }
}

Ensure your Next.js version and dependencies work in production mode.

If using features like image optimization, ISR, or middleware, make sure they’re compatible under self-hosting.

2. Prepare Your Next.js App for Production

On your local machine (or in CI), do the following:

Build the application:

npm run build

This will compile pages, optimize assets, etc.

(Optional) Test production start:

npm run start

Confirm the server starts and SSR pages render correctly.

If your application has environment variables, ensure you manage both build-time and runtime variables appropriately. Use .env.production or a secrets store on the server, and prefix any public variables with NEXT_PUBLIC_.

(Optional optimization) Use next.config.js or custom configurations (like enabling sharp for image processing) for production performance.

Once things run locally, you're ready to deploy.

3. Provision Your Linux Server

Log into your server via SSH and run:

sudo apt update
sudo apt upgrade -y

Then install the necessary base dependencies:

sudo apt install -y git curl build-essential

Install Node.js (recommended version: the LTS you used locally). You can use NodeSource, nvm, or your preferred method. For example:

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

Check node -v and npm -v.

You should also install nginx:

sudo apt install -y nginx

Make sure the firewall allows HTTP (80) and HTTPS (443):

sudo ufw allow 'Nginx Full'
sudo ufw enable

4. Deploy the App (Clone, Build, Start)

On the server:

Choose a directory, e.g. /var/www/myapp or /home/username/apps/myapp.

Clone your repository (or pull from remote):

cd /var/www
sudo mkdir myapp
sudo chown $USER:$USER myapp
cd myapp
git clone <your-repo-url> .

Install dependencies:

npm install

Build:

npm run build

Start the server (we’ll wrap this in a process manager soon). For testing:

npm run start

By default, Next.js listens on port 3000.

If this works, you’re ready to set up process management and reverse proxy.

5. Process Management: PM2 or systemd

You don’t want your app to crash and disappear. Use a manager:

Option A: PM2

Install it globally:

sudo npm install -g pm2

Start your app under PM2:

pm2 start npm --name "myapp" -- start

You can scale or restart easily. Then save the PM2 process list and enable startup:

pm2 save
pm2 startup

This ensures PM2 (and your app) restarts automatically on server reboot.

Option B: systemd service

Create a systemd unit file /etc/systemd/system/myapp.service:

[Unit]
Description=Next.js App – myapp
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/var/www/myapp
ExecStart=/usr/bin/npm run start
Restart=on-failure
Environment=NODE_ENV=production

[Install]
WantedBy=multi-user.target

Then:

sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp

Check status:

sudo systemctl status myapp

Either approach works well.

6. Reverse Proxy with NGINX

We’ll configure NGINX to handle incoming HTTP/HTTPS and forward traffic to your Next.js server on localhost:3000.

Create a new site config, e.g. /etc/nginx/sites-available/myapp:

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

    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_cache_bypass $http_upgrade;
    }
}

Enable it:

sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Now HTTP requests to your domain will be forwarded to your Next.js app.

7. SSL/TLS Setup with Certbot

To secure it with HTTPS:

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

Certbot will automatically modify your NGINX config, obtain certificates, and set up a renewal job. After success, your site will be HTTPS-enabled.

You should test renewal:

sudo certbot renew --dry-run

8. Zero-Downtime & Deployment Automation

Manual deployment is fine for small sites, but for production you want safer deployment:

  • Use a staging branch or CI (GitHub Actions, GitLab CI) to build and test.
  • Transfer the built app (or container) to server.
  • Use “blue/green” or rolling strategies: deploy new version to a separate directory and switch symlink.
  • Graceful restarts: ensure the old process stays alive until new one is ready.
  • PM2 offers reload commands (e.g. pm2 reload) that help with zero-downtime restarts.
  • Backups of application and environment variables are essential.

A sample script:

# On server
cd /var/www
git pull origin main
npm install
npm run build
pm2 reload myapp

You can wrap this in a shell script and execute via SSH from CI.

9. Monitoring, Logs & Health Checks

  • Use PM2 or journalctl to view logs and errors.
  • Monitor memory, CPU, response times.
  • Use uptime checks (e.g. ping /health endpoint).
  • Set up alerting (e.g. email or Slack) for crashes or high resource usage.
  • Enable log rotation to avoid disk overflows.

10. Tips, Caveats & Scaling Considerations

  • Use NODE_ENV=production for optimal behavior.
  • Avoid building on server in high-load environments; prefer building in CI and deploy artifacts.
  • For SSR with large loads, consider using multiple instances behind a load balancer.
  • Shared cache (for ISR or image cache) may require storing cache in a shared store (Redis, S3) rather than local disk.
  • Keep dependencies (e.g. sharp) updated and compatible on Linux.
  • If your app uses WebSockets or serverless features (Edge functions), ensure your architecture supports them.
  • Regularly renew SSL, update system packages, and monitor for security patches.

Conclusion

Deploying a Next.js SSR site on Linux is fully achievable with a few core components:

  • A Node.js server running your Next.js app
  • A process manager (PM2 or systemd)
  • NGINX as a reverse proxy
  • SSL via Certbot
  • Deployment scripts or CI automation

Once set up, you’ll have a reliable, maintainable SSR deployment under your control.

Comments Add
No comments yet.