Skip to content

Always kill PM2 daemon when running site stop --all#2683

Open
gcsecsey wants to merge 3 commits intotrunkfrom
gcsecsey/fix-pm2-stop-all
Open

Always kill PM2 daemon when running site stop --all#2683
gcsecsey wants to merge 3 commits intotrunkfrom
gcsecsey/fix-pm2-stop-all

Conversation

@gcsecsey
Copy link
Contributor

@gcsecsey gcsecsey commented Feb 27, 2026

Related issues

I ran into an issue while working on Studio from multiple workspaces. Each workspace has its own node_modules, and when the PM2 daemon was started from one workspace, it retained hardcoded paths to that workspace’s node_modules. When I switched to a different workspace (or the original one was removed/changed), the stale daemon would fail to fork child processes with MODULE_NOT_FOUND errors because those paths no longer existed.

Studio’s quit handler calls site stop --all, but if no sites are actively running at quit time, the command returns early without killing the PM2 daemon. The daemon would then linger with outdated module paths baked in, breaking subsequent launches from other workspaces.

Proposed Changes

  • Always call killDaemonAndChildrenAndExitProcess() when site stop --all is invoked, even if no sites are running
  • Updated test cases to verify daemon is killed in all scenarios

Testing Instructions

  • Start Studio and start some sites
  • Click the "Stop all" button
  • Check that the pm2 daemon is killed: npx pm2 list should show an empty table
  • Also check the same when closing Studio, and choosing "Stop sites" on the modal

Pre-merge Checklist

  • TypeScript and tests pass
  • No linting errors

When no sites are running, the daemon should still be killed to prevent
stale processes from persisting, particularly in development where the
daemon may retain hardcoded paths to node_modules that become invalid.
@gcsecsey gcsecsey requested a review from a team February 27, 2026 16:40
@gcsecsey gcsecsey changed the title Always kill PM2 daemon when running site stop --all Always kill PM2 daemon when running site stop --all Feb 27, 2026
@gcsecsey gcsecsey marked this pull request as ready for review February 27, 2026 16:43
Copy link
Contributor

@epeicher epeicher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this @gcsecsey! I have been able to reproduce this on trunk by starting Studio and quickly quitting before the sites start. At that point, the pm2 processes are kept there. This is not happening on this branch, so this LGTM! :shipit:

Trunk This branch
Image Image

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change required?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like an npm 10 vs 11 difference. I suggest reverting this file.

@gcsecsey, if you use nvm to manage Node versions, the current .nvmrc file should give you Node 24 and npm 11.

Copy link
Contributor Author

@gcsecsey gcsecsey Mar 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this change required?

I pushed this by mistake, this isn't required.

@gcsecsey, if you use nvm to manage Node versions, the current .nvmrc file should give you Node 24 and npm 11.

Thanks! I also noticed that the npm version is not always 11 for me, and I found that fnm and nvm were conflicting in my zshrc to set the correct node/npm versions. I mainly use fnm, but also needed nvm installed when working on the jetpack repo, and I didn't notice the conflicts for a long time. 😄 Now .nvmrc works as expected for me too.

Image

@fredrikekelund fredrikekelund self-requested a review March 2, 2026 07:25
Copy link
Contributor

@fredrikekelund fredrikekelund left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each workspace has its own node_modules, and when the PM2 daemon was started from one workspace, it retained hardcoded paths to that workspace’s node_modules. When I switched to a different workspace (or the original one was removed/changed), the stale daemon would fail to fork child processes with MODULE_NOT_FOUND errors because those paths no longer existed.

I don't understand how to reproduce this. Could you share the steps to do so, @gcsecsey?

Regardless, I think the change makes sense 👍 site stop --all should always kill the pm2 daemon.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like an npm 10 vs 11 difference. I suggest reverting this file.

@gcsecsey, if you use nvm to manage Node versions, the current .nvmrc file should give you Node 24 and npm 11.

@wpmobilebot
Copy link
Collaborator

wpmobilebot commented Mar 2, 2026

📊 Performance Test Results

Comparing 02e5e58 vs trunk

site-editor

Metric trunk 02e5e58 Diff Change
load 1418.00 ms 1755.00 ms +337.00 ms 🔴 23.8%

site-startup

Metric trunk 02e5e58 Diff Change
siteCreation 7073.00 ms 7083.00 ms +10.00 ms ⚪ 0.0%
siteStartup 3921.00 ms 3919.00 ms -2.00 ms ⚪ 0.0%

Results are median values from multiple test runs.

Legend: 🟢 Improvement (faster) | 🔴 Regression (slower) | ⚪ No change (<50ms diff)

@gcsecsey
Copy link
Contributor Author

gcsecsey commented Mar 2, 2026

Each workspace has its own node_modules, and when the PM2 daemon was started from one workspace, it retained hardcoded paths to that workspace’s node_modules. When I switched to a different workspace (or the original one was removed/changed), the stale daemon would fail to fork child processes with MODULE_NOT_FOUND errors because those paths no longer existed.

I don't understand how to reproduce this. Could you share the steps to do so, @gcsecsey?

Regardless, I think the change makes sense 👍 site stop --all should always kill the pm2 daemon.

I can't reproduce the original issue anymore either! 🎉 I think reconciling the node version managers solved this issue for me. Now, even on trunk the daemon is consistently stopped after stopping sites, I see no processes in npx pm2 list.

I don't know under what other conditions the daemon could remain running. But I agree it'd make sense to always kill it when stopping all sites.

This was roughly the original set of steps I did when first encountering the issue:

# Create a new worktree A and build the CLI
cd path/to/studio
git worktree add .tmp/worktree-a trunk
cd .tmp/studio-worktree-a
npm install
npm run cli:build

# Start a site to spawn the PM2 daemon from worktree A
node apps/cli/dist/cli/main.js site start --path ~/Studio/my-wordpress-website

# Stop the site, keeping the daemon alive
node apps/cli/dist/cli/main.js site stop --all

# Remove worktree A
cd path/to/studio
git worktree remove .tmp/worktree-a --force

# Build the CLI in the root
npm install
npm run cli:build

# Try starting a site: the stale daemon fails to fork
node apps/cli/dist/cli/main.js site start --path ~/Studio/my-wordpress-website
# ERROR: MODULE_NOT_FOUND — the daemon still references .tmp/studio-worktree-a/node_modules/...

@epeicher
Copy link
Contributor

epeicher commented Mar 2, 2026

I can't reproduce the original issue anymore either!

@gcsecsey I don't know if it was the original issue, but in trunk if you start Studio and before the sites start, you close it with cmd+q, the pm2 processes are kept there, and that does not happen with this branch.

@gcsecsey
Copy link
Contributor Author

gcsecsey commented Mar 2, 2026

I can't reproduce the original issue anymore either!

@gcsecsey I don't know if it was the original issue, but in trunk if you start Studio and before the sites start, you close it with cmd+q, the pm2 processes are kept there, and that does not happen with this branch.

I can't reproduce that, when I hit cmd+q as the sites are starting, I get these logs about the child processed being killed, and I also don't see any lingering pm2 processes:

CleanShot 2026-03-02 at 11 59 49@2x

@epeicher
Copy link
Contributor

epeicher commented Mar 2, 2026

Please find a video below, it depends on the timing, but I have not been able to reproduce this on this branch

CleanShot.2026-03-02.at.13.05.54.mp4

@gcsecsey
Copy link
Contributor Author

gcsecsey commented Mar 2, 2026

Please find a video below, it depends on the timing, but I have not been able to reproduce this on this branch

CleanShot.2026-03-02.at.13.05.54.mp4

Thanks for sharing the video @epeicher, I think this is a more consistently reproducible issue than the one I originally shared. 👍

We also discussed above that it makes sense to kill the process daemon on stop --all, so I'll proceed with merging this.

@gcsecsey
Copy link
Contributor Author

gcsecsey commented Mar 2, 2026

The failing E2E tests are unrelated to these changes, I re-triggered the job on Buildkite.

@fredrikekelund
Copy link
Contributor

Yeah, conceptually, there's a small window where isProcessRunning could return false while the process is spawning. The change in this PR is a good safeguard against that 👍

@gcsecsey, I realize now that I thought you were referring to npm workspaces in your PR description. Makes much more sense now that I understand you were talking about git workspaces.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants