SYSTEMS OPERATIONAL
Hosting

Deploy a Node.js App in cPanel: Setup Node.js App Guide

Getwebup 5 min read

If your hosting plan supports it, cPanel can run a Node.js app for you without you ever touching a systemd service file - it uses Phusion Passenger under the hood to keep your app alive, proxy requests to it, and restart it when it crashes. Most support tickets we see aren't about whether it works, they're about one confusing step in the setup or a cryptic Passenger error after a deploy. Here's how the feature actually works and how to get past the errors that trip people up.

What "Setup Node.js App" Actually Does

In cPanel, under Software, there's a tool called Setup Node.js App. When you create an application there, cPanel does three things behind the scenes:

  • Creates an isolated Node.js virtual environment for that app, pinned to whichever Node version you pick
  • Registers the app with Passenger so Apache/LiteSpeed can proxy traffic to it on an internal port
  • Writes a .htaccess file in the application's public directory that routes requests into Passenger

This means you don't manage a port yourself, and you don't run node app.js in a screen session and hope it survives a reboot. Passenger owns the process lifecycle. That's the whole point of the tool - but it's also why the errors look different from a plain Node deployment.

Step-by-Step: Deploying Your First App

1. Create the application

In cPanel, open Setup Node.js App → Create Application and fill in:

  • Node.js version - pick the version your app was built and tested against, not just the newest one available
  • Application mode - Production for a live site (sets NODE_ENV=production), Development only while you're actively debugging
  • Application root - a folder outside public_html, e.g. myapp, which resolves to /home/username/myapp
  • Application URL - the domain or subdomain that should serve this app
  • Application startup file - usually app.js, server.js, or index.js - whatever file calls app.listen() or exports your server

Click Create. cPanel will show you a command to activate the app's virtual environment - copy it, you'll need it in a minute.

2. Upload your code and install dependencies

Upload your project (via Git, SFTP, or File Manager) into the application root you just set. Do not upload node_modules - it gets rebuilt for the environment cPanel created. Then, over SSH, run the activation command cPanel gave you and install:

source /home/username/nodevenv/myapp/18/bin/activate && cd /home/username/myapp
npm install

Skipping the source step is the single most common mistake here. Run npm install without activating the virtual environment first, and packages get installed against the server's system Node (or fail outright with permission errors), not the isolated version cPanel is actually going to run your app with.

3. Set environment variables

Back in the Setup Node.js App interface, scroll to Environment Variables and add whatever your app needs - DATABASE_URL, API_KEY, and so on. PORT is not one of them - Passenger assigns the port internally via process.env.PORT, so don't hardcode a port in your code. Save, then click Restart.

Common Errors and How to Fix Them

SymptomLikely CauseFix
"We're sorry, but something went wrong" (Passenger error page)App crashed on startup, or the startup file path is wrongCheck ~/logs/<domain>.error.log or the app's own log path for the actual stack trace
Error: Cannot find module 'express'npm install was run without activating the virtual environmentRe-run source .../bin/activate then npm install from the application root
App works via node app.js over SSH but not through the browserApp is hardcoded to listen on a specific port instead of process.env.PORTChange app.listen(3000) to app.listen(process.env.PORT)
Changes to code don't show up after redeployPassenger caches the running process and doesn't reload files automaticallyClick Restart in Setup Node.js App, or run touch tmp/restart.txt in the app root
EACCES: permission denied during npm installFiles were uploaded as a different user or with root-owned permissions from a prior manual installFix ownership with chown -R username:username /home/username/myapp, then reinstall
Static assets (CSS/images) 404 even though the app runsThe .htaccess Passenger routing rules are intercepting requests meant for static filesServe static files through Express (express.static()) rather than relying on Apache to find them directly

Restarting Correctly After a Deploy

Every time you pull new code or change an environment variable, the app needs a restart - Passenger won't pick up changes on its own. You have two options:

  • Click Restart in the Setup Node.js App UI - the reliable option, works every time
  • Run touch tmp/restart.txt inside the application root over SSH - useful if you're scripting deploys with Git hooks

If you're pulling code via a post-receive Git hook, add the touch tmp/restart.txt command as the last line of the hook so every push restarts the app automatically.

Checking Logs When Something's Wrong

Passenger's own errors land in cPanel's domain error log, but anything your app logs with console.log or a logging library goes to stdout.log/stderr.log inside the application's log folder, viewable from the Setup Node.js App page itself under the app's detail view. Check both - a database connection failure, for instance, usually shows up in your app's own log, not Apache's.

Prevention

  • Pin your Node version in package.json's engines field so it's obvious which version the app expects
  • Never commit node_modules or a .env file - use cPanel's Environment Variables panel for secrets instead
  • Test locally with NODE_ENV=production before deploying, since some packages behave differently in production mode
  • Keep a lightweight /health route in your app that just returns 200 - it makes it much faster to tell "is Passenger running my app at all" apart from "is my app logic broken"

Frequently asked questions

Can I run a Node.js app on shared hosting, or do I need a VPS?

Setup Node.js App is available on most shared and reseller cPanel plans that have it enabled, not just VPS. Resource limits (CPU/memory via CloudLinux LVE) still apply, so a heavy app will hit those limits faster on shared hosting than on a VPS.

Why does my app work when I run it manually over SSH but not through the domain?

This almost always means the app is listening on a hardcoded port instead of process.env.PORT. Passenger assigns the port internally and proxies requests to it - if your code ignores that variable, Passenger can't reach the process.

Do I need to restart the app every time I edit a file?

Yes. Unlike a local dev server with hot-reload, Passenger runs your app as a long-lived process and won't notice file changes on its own. Click Restart in Setup Node.js App or touch tmp/restart.txt after every deploy.

Can I use PM2 instead of Passenger?

You can install PM2 inside the app's virtual environment, but it's redundant - Passenger already handles process supervision, restarts on crash, and port proxying. Running PM2 on top usually just adds a second layer of process management fighting with the first.

Where do I find my app's actual error output?

Open Setup Node.js App, click into the application, and check the log files listed there (stdout/stderr), plus the domain's error log under cPanel's Errors or Metrics section for Passenger-level failures like a missing startup file.

#cpanel #nodejs #passenger #deployment #npm #application-manager

Keep reading

Chat with Support