11 min read
Enhance has run Node.js applications since version 12.11.0, released on September 22, 2025. They live inside the same website container as everything else on that site, behind the same proxy, under the same resource limits. There is a form in the panel, you fill it in, and the app comes up.
The site you are reading is one of them. It is a Next.js application running as an Enhance Node app, which is how we know which parts of this go smoothly and which parts cost you an evening. The panel handles more than you would expect. The gaps are all in the same place: the distance between a process the panel starts for you and a process you can reach.
This is the deployment, the settings that actually matter, and the four things that catch people after the app is already running.
What you are getting
The app runs in the website container, with an isolated network stack, subject to the same isolation and resource limits as PHP on that site. Enhance proxies inbound requests to it on a path you choose. Nothing about it is a separate server, and its memory and CPU come out of the same package allowance as everything else on the site.
Two things are worth knowing before you start.
The first is Enhance's own position on support, quoted from their Node.js documentation: "We recommend that only experienced system administrators install and manage Node applications. Enhance can not offer app related support." That is a fair thing to say and you should take it literally. The panel starts your process. What the process does after that is entirely yours.
The second is that Node.js has to be enabled on the hosting package at all. Packages created after September 22, 2025, have it on by default. Packages created before that date have it off, and it stays off until someone turns it on. If the Node.js entry is missing from the panel, that is usually why, and it is a package setting rather than a website one.
The server also needs outbound access to github.com, since that is where the Node install is fetched from.
Deploying the app
Get your files onto the server first, by whatever route you normally use. SSH into the site
and git clone, or upload through the file manager. Put them in a directory under the home
directory and install dependencies with npm ci while you are there.
Then in the panel, open Websites, select the site, and choose Node.js from the Advanced dropdown. Deploy app gives you the form.
Working directory is a path relative to the home directory. Not an absolute path. This is an easy way to get a first deploy wrong, because an absolute path looks right and is the thing you just typed into your SSH session. Leave it empty and the app runs from the home directory itself.
Startup command is what Enhance runs to bring the app up, along the lines of
npm start --production. It runs after Enhance has entered the working directory.
Node version is a choice from the major versions available on that server. Pick the one your CI builds against. If your build pipeline tests on 22 and the server happens to default to something newer, you will find out through a runtime error in a dependency rather than through a failed build, which is a slow way to learn it.
Port is the port your app listens on. It only has to be unique inside that website, since the network stack is per-container, and it has to be between 1024 and 65535.
Path is the URL path the proxy hands to the app, and it has to be unique within the site. You cannot deploy onto a path that already has an application on it.
Then enable the proxy, which is the part that makes the app reachable from the internet at all. Without it you have a running process nobody can visit.
Automatic or manual
Automatic mode is the one you want for anything real.
Enhance starts the app for you, runs the right nvm use first, and restarts the process
whenever it exits. Output goes to persistent_app_ID.log in the home directory, which is
recycled at every start, so it holds the current run and not the history. That log is where
your crash is, and on a first deploy it is usually the only thing that will tell you what
went wrong.
Manual mode starts nothing and restarts nothing. You run the app yourself over SSH as the
website user, nvm use included, after running install_nvm_and_node.sh once and logging out
and back in. There are no logs. It is useful while you are working out why an app will not
boot, and it is not where you leave a production site, because nothing brings the process back
after a crash or a reboot.
The restart control in automatic mode is worth knowing before you need it: you switch the app to manual and back to automatic. There is no restart button, which matters for automated deploys, as the next section explains.
The four things that catch people
Know what address you are binding
Enhance's documentation does not state which address your application should listen on, so
bind 0.0.0.0 and the question never comes up. We have not tested whether a 127.0.0.1
bind is reachable from the proxy on this platform, and there is no documented answer either
way, so treat 0.0.0.0 as the safe default rather than a rule we can point at.
What did catch us is narrower and entirely repeatable. Next.js standalone binds to whatever
$HOSTNAME contains, and bash always sets HOSTNAME to the machine's hostname, so the
variable is never empty and never the thing you wanted. Setting it explicitly in the startup
script is the fix:
export HOSTNAME=0.0.0.0
The symptom, if you miss it, is a process that looks perfectly healthy in the log and a proxy error in the browser.
Your SSH session is not your app
On our server, SSH sessions and the Node app run in separate containers. They share the
website volume, so the files are the same files, but they do not share a process namespace.
From SSH, curl 127.0.0.1:3000 answers normally while pgrep node finds nothing at all and
ps aux lists about five processes, none of them yours.
The consequence is that you cannot signal your own application. Every deploy script that ends
in pgrep node | xargs kill stops working here, and it stops working silently, in the way
where the deploy reports success and the old build keeps serving.
Since the panel's only restart is a manual toggle in the UI, an automated deploy needs another route. The one we use is a sentinel file, which works because the two containers do share a filesystem and because automatic mode restarts the process whenever it exits. The startup command runs a small script instead of running node directly:
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"
rm -f .deploy-restart
node server.js &
NODE_PID=$!
trap 'kill -TERM "$NODE_PID" 2>/dev/null || true; exit 0' TERM INT
while kill -0 "$NODE_PID" 2>/dev/null; do
if [ -f .deploy-restart ]; then
rm -f .deploy-restart
kill -TERM "$NODE_PID" 2>/dev/null || true
wait "$NODE_PID" 2>/dev/null || true
exit 0
fi
sleep 2
done
wait "$NODE_PID"
The script owns the node process, so it can signal it. A deploy over SSH does its build and
then runs touch .deploy-restart in the app directory. The watcher sees the file within a
couple of seconds, stops node, and exits, and because the app is in automatic mode Enhance
starts it again on the new build. Have your deploy check that the sentinel file was consumed,
so a restart that never happened fails loudly rather than quietly.
Environment variables
Enhance's documentation describes no way to set environment variables on a Node app. It is not in the Node.js page and it is not in the release notes.
So they have to come from somewhere your startup command can read, which in practice means a file in the app directory that the script loads before it launches node. Two details make this less annoying than it sounds. Parse the file rather than sourcing it, because real values contain spaces and sourcing will happily execute them. And let anything already set in the environment win, so that a value the platform provides is never overwritten by your file.
The trap here is that a lot of frameworks read a .env file in development and stop doing it
in production builds. Next.js standalone is again the example: the generated server.js does
not read .env at all. Add a variable, restart, and it is simply not there, with no error to
tell you so. If your app expects a variable and behaves as though it is unset, check that
something is actually loading the file before you go looking at the code.
Hand-edited vhost config does not survive
Enhance regenerates the web server configuration for a site. On our server those files live under the LiteSpeed config tree, and we have watched the active file get rewritten well after the site went live.
Anything you add to that file by hand is temporary, and it fails in the worst way, which is that it works when you test it and disappears at some later regeneration you did not trigger and will not notice. If a piece of behavior matters, it belongs in your application, or in front of the origin at your CDN, and not in a file the panel owns.
The smaller print
Only the primary domain is proxied to the app. Aliases pointed at the same site do not reach
it, and the documented approach is to add them as aliases and redirect them to the primary
domain with .htaccess in the document root.
Restoring a website backup restores the application files but does not redeploy the app. The restore finishing is not the app running, so check it afterward.
If you proxy the whole site rather than a sub-path, certificate renewal needs
.well-known/acme-challenge to stay with the web server instead of going to your app.
Enhance excludes it from the proxy on all web server kinds, which it has done since 12.13.1 in
November 2025. If you are on an older version, check it.
One more version note. A race condition could leave an automatically started Node app outside its correct control group, which means the resource limits you set were not necessarily the limits it ran under. That was fixed in 12.14.2 in January 2026.
Node is heavier than PHP on the same box, and it holds memory between requests rather than releasing it at the end of one. If you sell Enhance packages, set CPU and memory limits at the package level before customers deploy Node apps rather than after.
When this is the wrong home for it
Enhance is a good answer for a Node app that is part of a hosting estate you already run: a marketing site, an internal tool, an API sitting alongside other sites. The container handles it, the proxy handles it, and it is one less machine to patch.
It is the wrong answer when the app is the thing you are building the infrastructure for. If you need a process manager you control, background workers, a queue, a database on the same host, or the ability to restart your own application without a file-watching trick, you have outgrown a website container and you want a server.
If you want to do any of this yourself, our KVM VPS includes the Enhance control panel for up to 10 websites at either service level, so the panel described above arrives with the server rather than as a license you bolt on. Our web hosting is cPanel, so if you are on one of those plans none of this is available to you and you would need a VPS.
KVM VPS 8 is 4 dedicated cores, 8 GB of DDR5 ECC and 256 GB of NVMe, on AMD EPYC 9645 in the US or Europe, at $39.95 a month self-managed or $69.95 fully managed. Fully managed adds $30 a month at any size and covers provisioning, the control panel, our security baseline, the patching and the day-to-day platform administration. Root access is where the two differ: self-managed hands you full root from the moment it boots, and on fully managed we run the operating system, so root stays with us by default.
Two support boundaries meet here. Enhance does not support Node applications, and our managed service covers the platform rather than the code you deploy on it. Neither of us is going to debug your app. If that is a problem, self-managed with full root is the better fit, and the hidden cost of an unmanaged VPS is worth reading first for a realistic view of what that costs you in evenings.
If you are not sure whether your app belongs in a website container or on its own server, ask us. We will tell you when the answer is that you do not need to buy anything.