Yong Sen - Full-Stack Developer

Deploy Next.js on Ubuntu with Git, PM2, Nginx, and Certbot

Production-ready guide to deploy a Next.js app on Ubuntu using Git for code, PM2 for process management, Nginx as reverse proxy, and Certbot for HTTPS.

January 20, 2025
3 min read

Deploy Next.js on Ubuntu with Git, PM2, Nginx, and Certbot

This guide walks through a production-ready deployment of a Next.js app on Ubuntu using:

  • Git for pulling code
  • PM2 for process management and restarts
  • Nginx as reverse proxy
  • Certbot (Let's Encrypt) for HTTPS

Replace placeholders like <domain>, <repo_url>, and <project_name> with your values.

1) Install Node.js and PM2

sudo apt-get update
# If Node is missing, install from NodeSource (recommended) or apt
# NodeSource example for Node 20 (optional, better than old apt repo):
# curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
# sudo apt-get install -y nodejs

# Or use apt's nodejs (ensure it's recent enough for Next.js)
sudo apt-get install -y nodejs npm

# PM2 global
sudo npm install -g pm2
pm2 --version

2) Pull Your Project from Git

cd /var/www
sudo mkdir -p /var/www/<project_name>
sudo chown $USER:$USER /var/www/<project_name>
cd /var/www/<project_name>

git clone <repo_url> .

3) Install Dependencies and Build

npm install
npm run build

4) Start with PM2

pm2 start "npm run start" --name <project_name>
pm2 save
pm2 status
# (Optional) Enable startup at boot
pm2 startup systemd
# Follow PM2's printed command to enable the service, then:
pm2 save
  • Default Next.js npm run start listens on port 3000. You can set PORT=3000 in your env if needed.

5) Install and Configure Nginx

sudo apt install -y nginx

Create a site config (e.g., /etc/nginx/conf.d/<domain>.conf):

server {
    root /var/www/html;
    server_name <domain>;

    location /doc {
        try_files $uri $uri/ =404;
    }

    location / {
        proxy_set_header   X-Forwarded-For $remote_addr;
        proxy_set_header   Host $http_host;
        proxy_pass         http://127.0.0.1:3000;
    }
}

Test and reload Nginx:

sudo nginx -t
sudo systemctl reload nginx

If you used /etc/nginx/sites-available/ + symlink to sites-enabled/, place the file there instead.

6) Get HTTPS with Certbot (Let's Encrypt)

sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d <domain>

Certbot will inject SSL directives and create a redirect server block automatically. A typical HTTPS config looks like:

server {
    root /var/www/html;
    server_name <domain>;

    location /doc {
        try_files $uri $uri/ =404;
    }

    location / {
        proxy_set_header   X-Forwarded-For $remote_addr;
        proxy_set_header   Host $http_host;
        proxy_pass         http://127.0.0.1:3000;
    }

    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/<domain>/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/<domain>/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

server {
    if ($host = <domain>) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    server_name <domain>;
    listen 80;
    return 404; # managed by Certbot
}

Test and reload:

sudo nginx -t
sudo systemctl reload nginx

7) Firewall and Health Checks

# UFW examples
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
# or specifically ports: sudo ufw allow 80, sudo ufw allow 443
sudo ufw enable
sudo ufw status

Verify:

  • curl -I http://<domain> should return 301 to HTTPS.
  • curl -I https://<domain> should return 200.
  • pm2 logs <project_name> shows app output.

8) Updates and Zero‑Downtime Restarts

cd /var/www/<project_name>
git pull
npm install
npm run build
pm2 reload <project_name>
pm2 save

Troubleshooting Tips

  • Blank page / 502: ensure PM2 app is listening on 127.0.0.1:3000 and Nginx proxy points to the same port.
  • Permissions: ensure project dir ownership is correct for the deploy user.
  • SSL renewal: Certbot installs a systemd timer/cron for auto-renewal; verify with sudo systemctl list-timers | grep certbot.
  • Environment variables: place them in a .env file and load via your start script or PM2 ecosystem file.

You're now running Next.js in production with process supervision, reverse proxy, and HTTPS. 🎉

Post Details

January 20, 2025
3 min read
Tags
Next.jsUbuntuPM2NginxCertbotDevOpsDeployment