Skip to Content
Examples09 · Process Manager

09 · Process Manager

An HTTP server on port 4000 that deliberately crashes on every 30th request, so you can watch 3va’s supervisor bring it back. You don’t need pm2.

Source: examples/09-process-manager

Start

cd examples/09-process-manager 3va start server.js --name demo-api
✓ Started process 'demo-api' (supervisor PID 3137986, 1 instance(s)) Logs: ~/.3va/processes/demo-api.log
3va status demo-api
Name PID Status Restarts Inst Entry -------------------- -------- -------- ---------- ------ -------------------- demo-api 3137986 running 0 1 server.js
curl localhost:4000 # {"pid":3138021,"uptime":"2.0s","requestCount":1}

Watch it restart

for i in $(seq 1 31); do curl -s localhost:4000; echo; done

Request 30 kills the worker, so it gets an empty reply, and so does any request that arrives while the worker is down. About half a second later a new worker is up with a new PID and requestCount back at 1:

3va logs demo-api
[worker] listening on :4000 (pid 3138021) [ERROR] Simulated crash after request #30 [worker] listening on :4000 (pid 3138739)

3va status now shows Restarts: 1.

Manage it

3va status # every managed process 3va logs demo-api --lines 200 3va restart demo-api 3va stop demo-api # SIGTERM, then SIGKILL if still alive after 30 s 3va delete demo-api # stop + remove metadata and logs

How it works

  • 3va start forks a supervisor and returns. The supervisor runs your script as a child and restarts it on crash with exponential backoff: 500 ms → 1 s → 2 s → … capped at 30 s. It gives up after 15 consecutive crashes (--max-restarts).

  • The state lives in ~/.3va/processes/<name>.json (entry, cwd, PIDs, restart count, flags) with logs next to it in <name>.log.

  • There are no --allow-* flags on 3va start. A managed process gets its permissions from package.json, so they’re recorded in the repo, not lost in someone’s shell history:

    package.json
    { "3va": { "no-prompt": true, "permissions": { ".": { "allow-net": ["*"] } } } }

Things to keep in mind

  • --port grants PORT, and only PORT. 3va start --port 4001 sets PORT for the process and also allows the script to read that one variable, so the server moves to 4001 with no allow-env in package.json. Any other configuration you pass through environment variables (DATABASE_URL, NODE_ENV, …) still needs an explicit grant. Without one, the variable just looks unset and you get no error:

    ".": { "allow-net": ["*"], "allow-env": ["DATABASE_URL"] }
  • "allow-net": ["*"] is broad. For a server that only listens, any network grant is enough to bind (see 02). Use * only if the process also makes outbound calls to hosts you can’t list.

  • Crashes are visible only in the logs. Requests in flight during a crash and the ~500 ms restart window fail. Use --instances N (load-balanced with SO_REUSEPORT) if you need to stay available while one worker restarts.

  • In a container, use --attach. It keeps the supervisor in the foreground so it can be PID 1. The default daemon mode forks and exits, which would end the container.

  • --no-autorestart makes a crash final. 3va status then shows error until you run 3va restart.

  • Process names are global per user (~/.3va/processes/). Two projects that both use --name api will collide.

  • See Process Manager for the full reference.