How to Install Node.js Applications in Plesk: A Complete Setup Guide

Node.js apps are just part of the furniture on Plesk boxes these days — sitting right next to the PHP sites and static pages we’ve always run. The good part is Plesk actually treats Node.js like a first-class citizen rather than bolting it on: the Node.js extension, which has shipped since Onyx and gotten steadily better through the Obsidian line, gives you a real interpreter version manager, Passenger integration, and a UI that covers most of what used to mean hand-writing systemd units and reverse proxy blocks.
None of that means it runs itself, though. There’s a strict order you need to follow — domain setup, then document root, then package.json checks, then dependencies, then picking the right startup file — and skipping a step is exactly how you end up staring at a 503 with zero clues in the browser. What follows is the process I actually use on production servers, plus the mistakes that generate the most support tickets in my experience.
What You Need Before You Start
Server and License Requirements
You want Plesk Obsidian for this (Onyx still limps along, but it’s EOL — don’t put new work on it). You’ll also need the Node.js extension installed, which you can check under Tools & Settings > Updates and Upgrades > Add/Remove Components, or just open a domain and see if “Node.js” shows up in the sidebar. Most license tiers include it, but a few of the stripped-down hosting-only SKUs leave it out, so verify this before you tell a client it’ll just work.
Enabling the Node.js Extension in Plesk
If it’s missing, head to Extensions, search “Node.js,” and install it. That extension is doing all the heavy lifting here — version manager, Passenger hooks, per-app config panel. Resist the urge to compile Node from source or run your own nvm setup on the shell; Plesk expects to own the interpreter binary paths, and going around it just creates conflicts you’ll be debugging later.
Preparing Your Application Files
Before you even open the Plesk UI, get your app itself into a deployable state:
- A valid
package.jsonwith a properstartscript, or at minimum a clearly named entry file likeapp.jsorserver.js. - A
.gitignoreor upload package that leaves outnode_modules— Plesk runs its ownnpm installanyway, and shipping a localnode_modulesfolder just bloats the upload and can conflict with platform-specific native builds. - Config values like API keys, DB connection strings, and ports pulled from
process.envinstead of hardcoded — you’ll wire these in through Plesk in a minute.
Understanding Plesk’s Node.js Architecture
The Node.js Extension and Version Manager
Plesk’s version manager keeps multiple Node runtimes installed side by side — 16.x, 18.x, 20.x, whatever your apps need — without touching the system’s own Node binary or disturbing anything else on the box. Each app picks its version independently, which is exactly what you want on a shared server where one client’s legacy app sits next to something built against a current LTS.
How Passenger Integrates with Node.js Apps
This is where most confusion comes from. Plesk doesn’t launch your app as a bare process bound to some random port managed by PM2 or forever — it hands the whole thing to Phusion Passenger. Passenger owns the process lifecycle: starting it, killing it, restarting it after a crash, recycling idle workers. It talks to your app over a local socket and does the supervision work you’d otherwise be configuring by hand with systemd or PM2.
Nginx and Apache’s Role in Request Handling
The web server layer — Nginx, sometimes with Apache sitting in front depending on how the server’s configured — takes the incoming connection and proxies it into Passenger, which then hands it to your app. That’s why things like WebSocket support or upload size limits get configured through Plesk’s Nginx directive panel rather than inside your app’s own routing — there’s more happening at the web server layer here than you’d get from a plain Node dev server.
Step 1: Creating a Domain or Subdomain for Your App
Setting Document Root vs. Application Root
More deployments go sideways here than anywhere else. The document root is where the web server pulls static assets from — usually a public, dist, or build folder if you’ve got a compiled React front end sitting in front of an Express backend, for example. The application root is a different thing entirely — it’s the base folder holding package.json, and it’s where Passenger actually starts your app. Mix these two up and you’ll either be serving raw source code to the world or watching every static asset 404.
Choosing Between a Domain, Subdomain, or Subdirectory
A standalone domain or subdomain is the cleanest setup for a Node app — you get a dedicated document root, its own SSL cert, and its own Node.js config panel. You can run Node under a subdirectory of an existing PHP domain, but it adds real friction since you’re now managing two stacks under one vhost. Unless you’re spinning up a quick proof of concept, just give the app its own subdomain and save yourself the headache.
Step 2: Uploading and Configuring the Application
Uploading Files via File Manager, Git, or FTP
You’ve got three realistic options: File Manager for a quick drag-and-drop, FTP for bulk transfers, or — what I’d recommend for anything that’s going to keep getting updated — the Git extension. With Git, you push to a remote, Plesk pulls the changes, runs npm install, and restarts the app through deployment actions. It’s essentially free CI/CD without bringing in a separate pipeline tool.
Verifying package.json and Start Script
Open the Node.js tab on the domain and confirm Plesk actually picked up package.json correctly. If your start script does something out of the ordinary — a build step before launch, say, rather than a plain node server.js — make sure that’s properly defined in scripts.start. Plesk’s “Run Script” dropdown reads straight from that field, so if it’s wrong there, it’s wrong everywhere.
Selecting the Node.js Version per Application
Same panel — pick your interpreter version from the dropdown. This setting is per-app, not server-wide, so a new app can run Node 20 while a legacy one sits on Node 16, both on the same box, with zero interference between them.
Step 3: Installing Dependencies and Starting the App
Running npm install Through Plesk
Use the “NPM Install” button in the panel instead of SSHing in and doing it yourself. Plesk logs the full output right there in the UI, and it runs the install in the same environment Passenger will actually use to serve the app — which matters more than it sounds like. When an install fails, that log is usually the quickest way to spot a missing native build dependency or a bad private registry credential.
Setting the Application Startup File
Double-check the “Application Startup File” field points at the right entry point — app.js, index.js, server.js, whatever it is for your app. Set Application Mode to production — this isn’t just a label, it changes real behavior in Express and most other frameworks: error verbosity, caching, how performance-sensitive middleware behaves. Put your environment variables — NODE_ENV, DB connection strings, API keys — into Plesk’s “Custom Environment Variables” section, not into a .env file that’s sitting in your Git repo.
Switching Application
Once everything’s set, hit “Restart App.” Plesk restarts just the Passenger process without taking down the rest of the domain’s web service. Going forward, any code change needs a restart triggered one of three ways — this button, a Git deployment hook, or the API — because Passenger deliberately won’t pick up file changes on its own in production mode.
Best Practices
- Run Application Mode as
productionon anything live — neverdevelopment. The gap in performance and security isn’t subtle. - Keep secrets in Plesk’s environment variable panel. Never hardcode them, and never commit them to Git.
- Use the Git extension with a post-deploy hook (
npm install && restart) rather than manual FTP uploads for anything actively being developed. - Keep document root and application root explicitly separate — don’t trust Plesk to guess right, because it won’t always.
- Pin the Node.js version per app instead of trusting “latest” — otherwise a server-wide update can break something you weren’t even thinking about.
- Handle WebSocket support and upload limits through “Additional nginx directives” — don’t hand-edit the vhost config, since Plesk will happily overwrite it on the next update.
- Check logs periodically even when nothing looks broken — memory leaks and slow degradation show up in the Passenger logs long before you get an actual outage.
Troubleshooting
Almost every Node.js problem I’ve seen on Plesk boils down to one of these:
| Symptom | Likely Cause | Fix |
|---|---|---|
| 502/503 error on app URL | App crashed or never started | Check Passenger log in Plesk’s log viewer or /var/www/vhosts/system/<domain>/logs; verify startup file path |
| Static assets 404 | Document root pointed at app root instead of build folder | Update document root to public/dist directory in domain hosting settings |
| npm install fails | Missing native build tools or wrong Node version | Switch Node.js version in extension panel; check install log for missing headers/deps |
| Environment variables not applied | Values set in local .env but not in Plesk panel | Add variables via “Custom Environment Variables” and restart app |
| Changes not reflecting after deploy | Passenger cached old process | Manually click “Restart App,” or trigger restart via Git deployment action |
| WebSocket connections dropping | Missing proxy headers/timeouts in Nginx | Add required directives via “Additional nginx directives,” not raw config edits |
Conclusion
Deploying Node.js under Plesk isn’t a fundamentally different job than deploying it anywhere else — you’re still dealing with npm, a start script, and a supervised process underneath it all. What’s different is where the supervision and routing actually happen: Passenger owns your app’s lifecycle, and the Node.js extension gives you a UI over the version manager, environment config, and restart hooks instead of a pile of hand-built systemd units. Get the document root and application root split correct, keep secrets out of your codebase, and lean on Git deployment instead of manual uploads for anything ongoing — do those three things and Nginx, Passenger, and the version manager will mostly just stay out of your way.
Frequently Asked Questions
Do I need to install Node.js manually on the server before using Plesk?
No — the Node.js extension comes with its own version manager, so you install and switch between Node versions per application without ever touching the system-wide Node install. That keeps apps isolated and stops different sites’ Node versions from stepping on each other.
What is the difference between the document root and application root in Plesk Node.js hosting?
The application root is where your package.json and source code live — it’s the folder Passenger launches the app from. The document root is the public-facing directory, often public or build, that Nginx/Apache serves static files out of. Get both set correctly and Passenger finds your app while static assets still get served efficiently.
Can I run multiple Node.js applications with different versions on the same Plesk server?
Yes — the built-in version manager lets each domain or subdomain run its own Node version independently. It doesn’t touch other sites or the server’s system Node install, which is exactly what you want in a multi-tenant hosting setup.
How does Plesk start and manage my Node.js application?
Passenger acts as the application server — it runs whatever startup file your package.json points to (usually via the start script) and takes care of process lifecycle, restarts, and scaling from there. All of that gets configured through the Plesk interface instead of you manually running node or pm2 commands yourself.
How do I set environment variables like NODE_ENV or API keys in Plesk?
There’s a dedicated environment variables section inside the Node.js app settings panel where you define NODE_ENV, database credentials, API keys, and so on. That keeps sensitive values out of your codebase entirely, rather than baked into a file that could end up committed somewhere it shouldn’t be.
How can I automatically redeploy my app when I push new code?
Hook up the Git extension to your repo and enable a deployment action that runs npm install and restarts the app on every push. If you’d rather not automate it, the “Restart App” button works fine as a manual trigger too, or you can call it via the API as part of your own CI/CD setup.
Related reading
- PHP 8.x End-of-Life Schedules & Compatibility: A Sysadmin’s Upgrade Guide
- Zero-Downtime Website Migration: A Complete rsync and DNS TTL Playbook
- Serverless Architecture vs. Traditional Hosting: Which Fits Your Workload?
- Ransomware & Malware Trends Targeting Hosting Environments: A Sysadmin’s Defense Guide