Skip to content

[Feature] Opt-in to make npm run wait for the actual app process (direct exec / exec-replace) for graceful shutdown on Kubernetes #237

@seungwanHam

Description

@seungwanHam

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

In containerized environments (e.g., Docker/Kubernetes), npm run <script> spawns /bin/sh -c "<script>". On pod termination, SIGTERM is delivered to PID 1 and forwarded to npm, which forwards it to its direct child (the shell). On some shells (e.g., ash/dash), the shell exits promptly on SIGTERM without waiting for its grandchild process (node). npm then exits because its direct child has exited; PID 1 considers the container finished; the container stops before the Node.js app completes its graceful shutdown (e.g., server.close() / cleanup). This effectively bypasses terminationGracePeriodSeconds.

Expected Behavior

npm should have an opt-in mechanism to wait for the actual target process (e.g., node) so that graceful shutdown reliably completes on Kubernetes. The default behavior should remain unchanged for compatibility, but an option should exist to:

  • Direct exec (Unix): If the script is a simple single command without shell metacharacters, spawn it directly (no /bin/sh).
  • Exec-replace (Unix): Keep the shell but automatically exec-replace it so the shell becomes the target process.

Possible surfacing to be coordinated with npm/cli (for context): a CLI flag like npm run --prefer-direct-exec, an .npmrc config, or an env var. Windows can keep the current behavior.

Steps To Reproduce

  1. Environment: Linux container (e.g., node:18-slim), PID 1 = dumb-init or tini, orchestrated by Kubernetes.
  2. package.json: "start": "node index.js" (no exec).
  3. Run the pod and terminate it via kubectl delete pod (sends SIGTERM).
  4. Observe: /bin/sh (ash/dash) exits promptly on SIGTERM; npm exits because its direct child is gone; the container stops before the Node app finishes its graceful shutdown work.

Environment

  • npm: 10.x
  • Node: 18.x
  • OS: Linux container
  • PID 1: dumb-init (or tini)
  • platform: Kubernetes (SIGTERM on pod termination via kubectl delete pod)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions