DEV Community

Cover image for Deploy Next.js on cPanel/WHM with Phusion Passenger
Sumeet Shroff Developer
Sumeet Shroff Developer

Posted on • Originally published at prateeksha.com

Deploy Next.js on cPanel/WHM with Phusion Passenger

Deploy Next.js on cPanel/WHM with Phusion Passenger

Step-by-step practical guide to host production Next.js apps on cPanel/WHM with Phusion Passenger — covers architecture, prerequisites, build/start scripts, deployment, SSL, and troubleshooting.

! Deploy Next.js on cPanel/WHM with Phusion Passenger

Quick summary

  • Use Passenger to run Next.js SSR or a custom server on cPanel/WHM without a separate Node host.
  • Choose SSR (next start / custom server) or static export (next export) depending on features and hosting limits.
  • Validate Node versions, prepare build/start scripts, set process.env.PORT, and use cPanel’s Node.js manager when available.

Why this approach

This guide shows practical, repeatable steps to run production Next.js on cPanel/WHM using Phusion Passenger. It targets developers with cPanel accounts and host admins running WHM. The goal: get SSR or a production Node server working reliably on shared or managed hosting when Passenger is available.

Architecture overview

  • Next.js: can run as SSR (Node server) or as a static export. SSR requires a Node runtime.
  • Node.js: executes the Next.js server code.
  • Phusion Passenger: spawns and manages Node processes behind Apache or nginx, forwarding HTTP requests.
  • cPanel/WHM: controls domains, SSL, and (sometimes) a Node.js app manager that hides Passenger details.

Request flow (typical Apache + Passenger)

  1. Browser requests https://example.com.
  2. Apache routes the request to Passenger, which manages the Node process.
  3. Passenger starts your startup file; your app listens on process.env.PORT.
  4. Next.js handles rendering and responds via Passenger.

Decide SSR vs static export

  • SSR: required for getServerSideProps, server-side APIs, or dynamic per-request rendering.
  • Static export: best for fully static sites (no Passenger needed, simplest on shared hosting).

Preparation checklist

  • Confirm Passenger is available via cPanel or WHM (ask support if unsure).
  • Confirm available Node versions match your Next.js requirements (Node 16/18/20 are common).
  • Prefer SSH access for builds and installs; cPanel UI can help but SSH simplifies reproducible deploys.
  • If you manage the server, plan to enable Passenger through WHM/EasyApache for a supported install.

Prepare your Next.js app for production

Repository layout (recommended):

  • next-app/
    • package.json
    • server.js <-- startup file for Passenger (SSR)
    • next.config.js
    • pages/ or app/
    • public/
    • .env.production

Add clear scripts in package.json so Passenger runs the correct production command. Example package.json snippets:

{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "NODE_ENV=production node server.js",
    "export": "next build && next export"
  }
}
Enter fullscreen mode Exit fullscreen mode

A minimal server.js must honor process.env.PORT so Passenger can route requests to the correct socket/port. Example:

const http = require('http');
const next = require('next');

const app = next({ dev: false });
const handle = app.getRequestHandler();

app.prepare().then(() => {
  const server = http.createServer((req, res) => {
    handle(req, res);
  });

  const port = process.env.PORT || 3000;
  server.listen(port, (err) => {
    if (err) throw err;
    console.log(`> Next.js server started on port ${port}`);
  });
});

// Optional: Graceful shutdown hooks
process.on('SIGINT', () => process.exit(0));
process.on('SIGTERM', () => process.exit(0));
Enter fullscreen mode Exit fullscreen mode

Notes:

  • Use NODE_ENV=production and build with next build before starting.
  • For fully static sites, run next export and upload the out/ folder into public_html; no Passenger needed.
  • Build locally or on the server. If native modules exist, building on the server avoids binary mismatches.

Enabling Phusion Passenger in WHM

Preferred: use WHM → EasyApache 4 to enable the Passenger Apache module. Steps:

  • Log into WHM, open EasyApache 4, Customize, add Passenger (if provided), and provision.
  • Restart Apache: sudo systemctl restart httpd

If EasyApache doesn’t provide the module, follow Phusion's official installer (advanced). Avoid mixing manual gem installs with EasyApache-managed modules.

Verify installation (root/WHM):

  • Check Apache modules and passenger utilities with passenger-status and passenger-memory-stats.
  • Tail Apache logs: see where errors land and confirm Passenger loaded successfully.

Deploy: upload, install deps, build, and run

Common flow (two options: cPanel Node manager or manual Passenger vhost):

1) Prepare start scripts

  • Either use next start -p $PORT or a server.js wrapper that listens on process.env.PORT.
  • Example start script in package.json: "start": "node server.js" or "next start -p $PORT".

2) Upload your project

  • Use Git (recommended), SFTP, or File Manager. Keep the app outside public_html when possible.

3) Install and build on the server (SSH):

  • Use package manager commands to install production deps and build the app:

cd ~/next-app
npm ci --only=production --no-audit --progress=false
npm run build

4) Configure the cPanel Node.js App manager (if available)

  • Create an app, select Node version, set app root and startup file (server.js or package.json start), add env vars, then Start.

5) Or configure Passenger via vhost include

  • Add PassengerAppRoot, PassengerNodejs (optional), and PassengerAppEnv to a vhost include and restart Apache.

6) Start and verify

  • cPanel: click Start or Restart.
  • Passenger spawns the app on first request (or on start depending on settings). Check logs for errors.

Useful server commands (root or account as allowed):

  • Tail webserver logs and Passenger status:
tail -f /usr/local/apache/logs/error_log
Enter fullscreen mode Exit fullscreen mode
passenger-status
passenger-memory-stats
passenger-config restart-app /home/username/path/to/app --ignore-app-not-running
Enter fullscreen mode Exit fullscreen mode

And to follow application logs:

cd ~/path/to/app
tail -f ./npm-debug.log ./next.log ./logs/*.log
Enter fullscreen mode Exit fullscreen mode

Domains, env vars, and SSL

  • Map your domain/subdomain to the app root or symlink into the domain DocumentRoot.
  • Passenger provides process.env.PORT; your app must respect it.
  • Set environment variables via cPanel’s Node.js manager, SetEnv in vhost, or a secure server-side secret store. Avoid committing .env files.
  • Use cPanel/WHM AutoSSL to install certificates; Passenger works behind Apache’s SSL termination. Redirect 80 → 443 and add HSTS when appropriate.

Troubleshooting checklist

Common symptoms and fixes:

  • 502/503: app failed to start — ensure npm install && npm run build ran and that the startup file listens on process.env.PORT.
  • Missing modules: reinstall node_modules on the server (npm ci) and match Node versions.
  • Dev server running: ensure you’re not running next dev; use next start or node server.js in production.
  • Permissions: set correct owner (chown -R username:username) and file modes (644/755).

Example package.json start scripts for clarity:

"scripts": {
  "build": "next build",
  "start": "next start -p $PORT"
}
Enter fullscreen mode Exit fullscreen mode

If you cannot access required logs on a shared host, include log excerpts and steps taken when contacting support.

Case study: shared cPanel deploy (short)

Steps taken:

  • Clone or upload a minimal Next.js app to /home/username/nodeapps/my-next-app:
git clone https://github.com/vercel/next.js.git
cd next.js/examples/basic-css
npm install
npm run build
Enter fullscreen mode Exit fullscreen mode
  • Build on the server:
npm ci
npm run build
Enter fullscreen mode Exit fullscreen mode
  • Configure cPanel Node manager to use server.js or next start -p $PORT and start the app.
  • Example custom server (Express + Next) can be used if needed:
// server.js
const express = require('express')
const next = require('next')

const dev = false
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const server = express()
  server.get('*', (req, res) => handle(req, res))

  const port = parseInt(process.env.PORT, 10) || 3000
  server.listen(port, err => {
    if (err) throw err
    console.log(`> Ready on http://localhost:${port}`)
  })
})
Enter fullscreen mode Exit fullscreen mode

Common fixes discovered: avoid dev server, rebuild native modules on the server, fix file ownership and permissions.

Conclusion

Running Next.js on cPanel/WHM with Phusion Passenger is a practical option for smaller SSR apps when Passenger is available. Validate Node versions, prepare a production build, use a startup script that honors process.env.PORT, and prefer building on the server when native modules exist. For larger scale, consider migrating to a platform designed for Node.js scaling.

Call to action
Try the steps above on a staging subdomain first. If you need help adapting a project or automating deployment, reach out for assistance.

Home: https://prateeksha.com
Blog: https://prateeksha.com/blog
Canonical: https://prateeksha.com/blog/deploy-nextjs-cpanel-phusion-passenger

Top comments (0)