Technical Guide Intermediate

Deploying a Next.js App Behind Nginx on Ubuntu

A practical guide for running a Next.js application with PM2 and exposing it through Nginx as a reverse proxy.

  • By Alain Ineza
  • 8 min read
Deploying a Next.js App Behind Nginx on Ubuntu cover image

What you'll learn

  • Prepare the Ubuntu server for deployment
  • Run the Next.js app with PM2
  • Configure Nginx as a reverse proxy
  • Test the app locally before exposing it
  • Avoid common deployment mistakes
On this page

Overview

Deploying a Next.js application properly is more than just starting the app with npm run start. In a real server environment, the application should run as a managed process, and Nginx should sit in front of it as a reverse proxy.

This setup allows Nginx to receive browser traffic on port 80 or 443 and forward requests internally to the Next.js app running on a local port such as 3000.

The goal is simple: users access the website using a normal domain name, while the application remains safely managed behind Nginx.

1. Prepare the server

Before deploying the application, confirm that the server has Node.js, npm, Git, Nginx, and PM2 installed.

Use a normal application user where possible, and avoid running the application directly as root.

Use this command to check the important tools:

node -v
npm -v
git --version
nginx -v
pm2 -v

If one of these tools is missing, install it before continuing.

2. Build the Next.js application

After pulling the latest code from Git, install dependencies and build the application.

cd /srv/example-app

npm install
npm run build

A successful build confirms that the application can compile correctly before it is started in production mode.

If the build fails, do not continue to Nginx configuration yet. Fix the application issue first.

3. Start the app with PM2

PM2 keeps the application running in the background and can restart it if the process crashes.

pm2 start npm --name example-app -- start
pm2 save
pm2 status

After starting the app, test it locally from the server:

curl -I http://127.0.0.1:3000

You should see a successful HTTP response. If the local test does not work, Nginx will not fix the problem.

4. Configure Nginx reverse proxy

Create an Nginx server block for the application domain.

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

    client_max_body_size 10M;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;

        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;
    }
}

The proxy_pass line is the most important part. It tells Nginx to forward public web traffic to the local Next.js process.

5. Test and reload Nginx

Always test the Nginx configuration before reloading the service.

sudo nginx -t
sudo systemctl reload nginx

Then test the website from the server:

curl -I http://example.com

6. Common mistakes to avoid

A common mistake is configuring Nginx before confirming that the application works locally.

Another common issue is using the wrong port in proxy_pass. If PM2 runs the app on port 3000, then Nginx must point to 127.0.0.1:3000.

Also check that the domain DNS points to the correct server IP address. If DNS points somewhere else, the Nginx configuration on this server will not be used.

7. Final checklist

Before considering the deployment complete, confirm the following:

The app builds successfully
PM2 shows the app as online
The local curl test works
Nginx configuration test passes
The domain resolves to the correct server
The website opens in the browser
Upload limits are configured if the app accepts files

Tip

Do not expose the application port directly to the internet unless there is a clear reason. In most deployments, users should access the app through Nginx, while the app itself listens locally.

Conclusion

A clean Next.js deployment should be predictable and easy to maintain. PM2 manages the application process, while Nginx handles public web traffic, reverse proxying, and upload limits.

This structure makes the deployment easier to troubleshoot because each layer has a clear responsibility.

Testing images

Add one image after the Overview section and one image after the Nginx reverse proxy section. This will confirm that:

Images upload correctly
Images appear inside the editor
Images render on the public article page
Public image sizing looks good

Topics

  • Ubuntu
  • DevOps
  • Next.js
  • PM2
  • Nginx

Continue learning

Explore more technical notes, guides, and connected learning from the same archive.

Explore Tech articles

Omni

Explore

Community

Core capabilities

Latest from Omni

Contact Omni