fix(cloudflare): kill cloudflared properly on dev exit#1416
Open
Mkassabov wants to merge 1 commit into
Open
Conversation
When `alchemy dev` is restarted (e.g. switching Cloudflare profiles), the previous run's `cloudflared` process would orphan on Windows. The stored PID in `.alchemy/pids/tunnel.pid.json` was the `cmd.exe` shell wrapper, so cleanup killed the wrapper but left `cloudflared.exe` running. The next dev session then either reused a stale trycloudflare URL or raced against the orphan, baking a dead `TUNNEL_HOST` into the deployed tunnel-proxy worker and producing a 530 on the new profile. - Add a `shell` option to `idempotentSpawn` (default `true` to preserve current behavior for user-provided dev commands that rely on `.cmd` resolution and shell features). When `false`, parse `cmd` into argv and spawn the program directly so the stored PID is the program's. - Pass `shell: false` from the cloudflared spawn sites (miniflare tunnel + quick-tunnel) since those are direct executables. - On Windows, fall back to `taskkill /T /F /PID` in `kill()` so that the existing `shell: true` callers (website / bun-spa dev commands) also tear down their child trees instead of orphaning them.
commit: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a
530error when restartingalchemy devwithdev: { tunnel: true }after switching Cloudflare profiles (or just restarting the dev server) on Windows.Root cause
idempotentSpawnruns the child withshell: true, so the PID it records in.alchemy/pids/tunnel.pid.jsonis thecmd.exewrapper, notcloudflared.exe. On cleanup weprocess.killthe wrapper, but Windows doesn't auto-kill its children — socloudflared.exeorphans.On the next
alchemy devrun, one of two things happens:cloudflared. Whichever loses the race produces a staletrycloudflare.comhost that gets baked into the deployedtunnel-proxyworker'sTUNNEL_HOSTbinding. Requests forward to a dead tunnel → 530.process.kill(pid, 0)is lenient) andidempotentSpawnreuses it, skipping the new spawn entirely — so the deployedTUNNEL_HOSTpoints at a long-dead trycloudflare URL.It worked on the first profile and broke on the second because no orphan exists before the very first dev run. It worked with
tunnel: falsebecause thecloudflaredcodepath is bypassed entirely.Reproduced with two profiles + the playground worker;
Get-Process cloudflaredshowed PID54172whiletunnel.pid.jsonstored PID33132(the deadcmd.exe), confirming the orphan.Fix
shell?: booleanoption toidempotentSpawn, defaulttrue(preserves current behavior for user-provided dev commands that rely on.cmdresolution / shell features). Whenfalse, parsecmdinto argv and spawn directly — the stored PID is then the program's own PID.shell: falsefrom the two cloudflared spawn sites (cloudflare/miniflare/tunnel.tsandcloudflare/quick-tunnel.ts); both runcloudflared tunnel --url <url>, which needs no shell features.shell: truecallers (website / bun-spa dev commands), usetaskkill /T /F /PIDinkill()on Windows so the entire process tree is torn down instead of just the shell.Test plan
Get-Process cloudflaredafter Ctrl+C now shows no orphanshell: falsecallers (cloudflared is onPATH, no shell features used)npm run dev, etc.) still work — they remainshell: true