DocsNode.js and Python apps

Node.js and Python apps

Run Node.js, Python, Go and other server apps behind nginx with automatic ports, start commands, zero-downtime blue/green releases, multi-core Node.js and live logs.

Server applications (Node.js, Python, Go, Ruby, Java, .NET) run as a service of the hosting account, behind nginx, which handles the domain, SSL and static files. ZoPanel builds and starts them through the website's Deploy tab, keeps them running, and restarts them if they crash.

Install the runtimes

Administrators install language runtimes under Runtimes:

Runtime Versions offered Used for
Node.js 24, 22, 20, 18 (several side by side) Next.js, Nuxt, NestJS, Express and front-end builds. pnpm and yarn are included.
Python The system Python 3, with virtual environments Django, Flask, FastAPI
Go Latest Go services
Composer Latest Laravel and Symfony
Ruby, Java, .NET System packages (.NET 8.0) Rails/Rack, Spring Boot, ASP.NET Core

If a project asks for a Node.js major version that is not installed (in .nvmrc, .node-version or engines.node), the newest installed version is used.

Create the website

You have two options:

  • Git deploy (recommended): in Websites → New website, choose Git deploy and enter the repository. See Git deploy.
  • Node / Proxy: choose Node / Proxy to create a website that forwards every request to an application port, then set up the app on the Deploy tab with Uploaded files (File Manager): upload your project to domains/<domain>/source with the File Manager or SFTP and click Deploy.

Ports

Your app must listen on 127.0.0.1 at the port given in the PORT environment variable. ZoPanel sets it for you:

  • Each website gets its own base port, shown as Internal port on the Deploy tab (127.0.0.1:<port>).
  • For blue/green releases, the second slot uses the base port plus 10000. Never hard-code a port: always read PORT.
  • On customer accounts, the proxy port of a website is fixed by ZoPanel and cannot point to another local service. Only administrators can set a different Application port on the PHP & config tab.

Examples:

// Node.js (Express)
app.listen(process.env.PORT, "127.0.0.1");
# Python: bind the server to $PORT, e.g. in the start command
# .venv/bin/gunicorn myproject.wsgi:application --bind 127.0.0.1:$PORT

Start commands

ZoPanel proposes the start command from the project. You can change it in Build & run → Start command (one line; chain steps with &&).

Framework Proposed start command
Next.js npm run start (or npx next start)
Nuxt node .output/server/index.mjs
NestJS npm run start:prod or node dist/main
Express, Fastify, Koa, Hono npm run start, or node <main> / server.js / index.js / app.js
Django .venv/bin/gunicorn <project>.wsgi:application --bind 127.0.0.1:$PORT
FastAPI .venv/bin/uvicorn main:app --host 127.0.0.1 --port $PORT
Flask .venv/bin/gunicorn app:app --bind 127.0.0.1:$PORT
Go ./bin/<module name> (built with go build -o bin/…)
Any project with a Procfile the web: line

For Python, the install command creates a virtual environment in .venv and installs requirements.txt, pyproject.toml or Pipfile; gunicorn or uvicorn is added when it is missing. For Django, add your domain to ALLOWED_HOSTS (or read it from an environment variable).

Put secrets and settings in Environment variables. They are available during the build and at runtime, and members with view-only access cannot see their values.

Zero-downtime (blue/green) releases

Every server app has two slots. A new release:

  1. is built in its own release folder;
  2. starts in the idle slot, on that slot's port;
  3. must answer on the Health check path (default /) with a status below 500 within 90 seconds;
  4. only then receives traffic: nginx is reloaded gracefully to the new slot, and the old slot is stopped.

If the new release fails to build, exits during startup or never becomes healthy, the running release is not touched. Rollback in the Releases card (the last 5 builds are kept) uses the same mechanism.

Because two releases briefly run side by side, keep state that must survive a release in the database, Redis or a Persistent path (for example uploads or .env), not inside the release folder.

Use every CPU core (Node.js)

Node.js runs on a single core. For Node.js server apps, the Use every CPU core card can run one process per CPU of the package without code changes, which serves several times more requests. Turn on One process per CPU core (redeploys to apply) only if the app keeps no state in memory: keep sessions, cache and websockets in Redis or a database.

Start, stop and restart

The status card on the Deploy tab shows the app's state, framework, runtime and internal port, with buttons to Deploy now, Restart, stop and start the app. The service restarts automatically if the process exits.

Resource usage counts toward the account's package: memory, CPU and processes of all websites and apps are shown under Resources.

Logs

  • Application output on the Deploy tab shows the latest 400 lines written by the app (stdout and stderr, both slots). Turn on Live to follow it.
  • The build log of every deployment is in Tasks.
  • The Logs tab shows nginx's access and error logs. A 502 error there usually means the app is not listening on $PORT; Run diagnostics detects this and suggests a fix.

Troubleshooting

Symptom What to check
"The app did not respond on port … within 90s" The app must listen on 127.0.0.1:$PORT, not a fixed port.
"The app exited during startup" Read Application output: usually a missing environment variable or dependency.
"Node.js is not installed — install it under Runtimes" Ask the administrator to install a Node.js version.
Health check returns 404 or a redirect Any status below 500 passes. A 5xx means the app is broken; set Health check path to a route that answers quickly.

← Git deploy Website tools →