Sites and applications

Node.js sites

A Node.js site is a systemd service the panel manages for you, reverse-proxied by nginx with HTTPS. Upload or pull your code, and the panel works out how to start it.

Verified against auraPanel 1.21.3· Last updated 17 September 2026

How a Node.js site runs

The panel creates a systemd unit, aurapanel-site-<domain>.service, that starts your application as the site's Linux user on a loopback port, and an nginx server block that proxies the domain to that port with HTTPS. You never write the unit or the server block; you deploy code and the panel starts it.

Node versions are installed under Services → Node Versions from nodejs.org release tarballs into /opt/aurapanel/node/<version>/, with node, npm, npx and corepack available on the PATH. Each site pins a version; change it in the site's settings.

Create the site

  1. Install a Node version under Services if none is present.
  2. Sites → Add site → Node.js. Enter the domain, choose the Node version, accept the suggested app port (the next free loopback port) and pick a supervisor: systemd runs your start command directly; PM2 runs it under pm2-runtime inside the same unit.
  3. Create. The unit exists but has nothing to run yet, so the site shows the panel's placeholder until you deploy.

Deploy your code

Put the application in the document root, /home/<user>/htdocs/<domain>/, by any means: the File Browser, SFTP, git clone as the site user, rsync from CI. Then either press Install dependencies on the site, or do it from a shell:

sudo -u <site-user> -i
cd ~/htdocs/example.com
npm ci --omit=dev          # or pnpm / yarn — the panel detects the lockfile
npm run build              # if your app has a build step
exit
sudo systemctl restart aurapanel-site-example.com

Install dependencies does the same: it installs with the package manager your lockfile implies (npm, pnpm or yarn), runs the build script if there is one, re-applies the unit and server block, restarts the service and probes it. A tick box does a clean install first.

How the start command is chosen

The command is resolved every time the unit starts, in this order, so the first deploy and every later one are treated the same:

  1. An explicit start file set in the site's settings.
  2. The start script in package.json, run with the detected package manager.
  3. The main field in package.json.
  4. server.js, index.js or app.js in the document root.

Environment your app receives

VariableValue
PORTThe site's loopback port. Your server must listen on it. The panel's value always wins over one in .env.
HOST127.0.0.1. Never bind to a public address; nginx is the public face.
NODE_ENVproduction
TZThe site's time zone, when set.
PATHThe chosen Node version's bin directory first.
REDIS_URL, REDIS_HOST, REDIS_PORT, REDIS_KEY_PREFIXOnly when package.json declares a Redis client and Redis is installed. Your app has to read them.

A .env file in the document root is loaded first, so your own variables go there. PORT and HOST are reserved.

Expected result

After a restart the domain serves your application over HTTPS. The site's overview shows the service as running; systemctl status aurapanel-site-example.com says the same.

Restarts and crashes

The unit has Restart=always with a 3-second delay that backs off to 5 minutes. If the process fails to start more than five times in a minute, systemd parks it as failed so a crash loop does not consume the server; fix the cause, then restart from the site or with systemctl restart aurapanel-site-<domain>. Stops are given 10 seconds before the process is killed.

Logs

Your application's stdout and stderr go to the journal, shown on the site's Logs tab and available on the server for about seven days:

journalctl -u aurapanel-site-example.com -f

nginx's access and error logs for the domain are in /home/<user>/logs/. See Logs and monitoring.

Common messages

"no Node app found in … — deploy code with a package.json `start` script (or a server.js/index.js/app.js entry file), then restart"

The resolver found nothing to run. Add a start script or an entry file in the document root and restart the service.

"no package.json in the document root — upload your app first"

Install dependencies was pressed on an empty site. Deploy the code first.

"node runtime … is not installed at … — install it under Settings → Node Versions before deploying"

The site is pinned to a version that is no longer installed. Install it, or change the site's Node version.

A lockfile warning after Install dependencies

The lockfile did not match package.json; the panel retried once without the strict mode and continued. Regenerate the lockfile locally and commit it.

502 from nginx

The process is not listening on PORT. Check the journal for the crash, and make sure the server reads process.env.PORT rather than a hard-coded port.

Bun is not bundled; use npm, pnpm or yarn.

Try the live demo

A real auraPanel on a real server, shared with everyone. Sign in with the demo account and click around.

Email
demo@goaura.one
Password
demo1234
Open the demo panel ↗

Shared public demo, so please don’t put anything personal in it. More about the demo →

Talk to us

Tell us what you need and how to reach you. A person reads this, not a queue.

We use these details only to reply to you. Privacy

Recent updates

What shipped in the last releases. With automatic updates on, each lands on your server the moment it is published.

  1. v1.21.3
    Framework sites were undetectable, and a restore could silently point the copy at the original's live database.

    DetectApp looked for each application's signature relative to the docroot — but every PHP framework in the registry serves from a subdirectory (DocrootSub: public, webroot, web, pub, docroot) and keeps the file that identifies it one level *above* that. So the Laravel signature searched <project>/public/artisan,…

  2. v1.21.2
    Restarting nginx now blocks the panel and reconnects, instead of showing a notice.

    v1.21.1 stopped the false "HTTP 520" but only put up a toast — and a toast is the wrong affordance when the panel itself is about to go away: the page sat there looking broken while every request failed underneath it.

  3. v1.21.1
    Restarting nginx from Services no longer reports a false failure.

    It showed "Restart failed — HTTP 520". The panel is reverse-proxied *by* nginx, so the request deadlocked against itself: the handler blocked waiting for the restart to finish, while nginx's graceful stop waited for that very request to drain. Neither could proceed until nginx's stop-retry timeout force-killed it,…

  4. v1.21.0
    Per-site X-Frame-Options, fixing page builders that couldn't load their preview.

    Elementor's editor failed with *"Multiple 'X-Frame-Options' headers with conflicting values ('DENY, SAMEORIGIN')"*. The cause is that nginx's add_header appends rather than replaces: when the application also sets the header — a WordPress security plugin, or a CDN transform — the browser received two values,…

  5. v1.20.6
    The multi-database restore refusal now says what is in the backup and what still works.

    It read only "Restoring a multi-database site isn't supported yet", which is a dead end: it named neither the databases involved, nor why one of them is the limit, nor any route forward.

  6. v1.20.5
    Services: cards in a row now match heights.

    Like the settings grid before it, the Services grid pinned each card to its own content height, so a row often showed one card noticeably shorter than the card beside it. Each row now takes the taller card's height and both fill it, and it re-balances as content changes — a runtime installed, a longer service list.

  7. v1.20.4
    Settings: paired cards now match heights.

    Organisation / Panel Domain and auraPanel Updates / Session timeout each sat at their own content height, so a row showed one card visibly shorter than its neighbour. Each row now takes the taller card's height and both fill it — driven by content, not a fixed value, so it still adapts as a card grows (a validation…

  8. v1.20.3
    Settings: Organisation and Panel Domain now share a row too.

    They were full-width blocks stacked above the rest of the page; they now sit side by side in the same two-column grid as the Updates / Session timeout pair, so the top of Settings reads as two tidy rows instead of four stacked bands. Both cards shrink cleanly — the logo drop zone is a fixed 44×44 and the name field…

All releases on GitHub ↗