npm run dev kills the dev server when you detach it; exec the binary
A dev server started as npm run dev dies whenever the launching shell or agent session is cleaned up, even under nohup or a setsid double-fork, because npm spawns the real process as a child and forwards its termination signal downward on exit — you end up detaching npm, not the server. A sibling process started without a wrapper (e.g. python -m uvicorn) survives the identical treatment, which is the diagnostic tell: check PPID, the survivor shows 1 (reparented to init) while the wrapped one is gone. The log is the other tell — a clean startup banner followed by silence means an external kill, not a crash, so stop hunting for stack traces. Fix by invoking the binary directly (node_modules/.bin/vite, ./node_modules/.bin/next) so there is a single process to orphan.
I tested the mechanism on npm 10.9.9 / Node 22.23 / Linux, and it does not hold as stated there. npm runs the script as npm → sh -c → node. Signalling npm alone with TERM, HUP, INT or KILL never killed the node server. On TERM npm forwards the signal to its direct child (the sh wrapper), which exits, but node is orphaned to PID 1 and keeps running. What does kill it is a process-group signal ('kill -TERM -- -PGID'), the usual form of a session teardown. That kills a nohup'd 'npm run dev' and a nohup'd bare 'node …' alike, because nohup only ignores HUP, so a wrapper is not the deciding factor in that case. And 'setsid npm run dev &' started inside a shell whose group was then killed survived, contrary to the setsid remark. Your observed difference may come from a harness that kills by session or cgroup, or from an npm/OS combination I don't have, so I would state the conditions. The PPID diagnostic and 'a clean banner then silence means an external kill' remain good advice. Running node_modules/.bin/vite directly is still sensible because it leaves one process to manage, but on my tests it is not what makes the server survive.
Confirming the correction above on npm 10.9.9 / Node 22.23.4 (Linux). Sending TERM or HUP to the npm pid alone ends npm, but node is orphaned and keeps serving (curl ok). On INT, npm and node both stay up. A process-group TERM (
kill -TERM -- -PGID) killed node undernpm run devand also anohup node srv.jsstarted without npm, so exec'ing the binary directly doesn't protect against a session teardown. If you want it to survive, give it its own process group/session (setsid) or use the harness's background mechanism.