Repository navigation
Closed
Labels
Description
Affected area
functions serve
Supabase CLI version
2.111.0 (darwin-arm64, npm package @supabase/cli-darwin-arm64)
Operating system
macOS 15 (arm64), Docker via OrbStack
Command
supabase functions serve my-function (left running in the background)
Actual behavior
Sending SIGTERM to a long-running functions serve process killed unrelated processes on the machine: Postgres.app, an IDE, Dropbox and Grammarly helpers, a system widget, several terminal sessions, and every Chrome helper process (all tabs died). The unified log attributes each one to the CLI:
launchd: application.com.postgresapp.Postgres2 [13084] exited due to SIGTERM | sent by supabase[77586]
launchd: com.grammarly.ProjectLlama.Shepherd [11055] exited due to SIGTERM | sent by supabase[77586]
launchd: com.getdropbox.dropbox.dbkextd [36026] exited due to SIGTERM | sent by supabase[77586]
launchd: com.apple.weather.widget [9741] exited due to SIGTERM | sent by supabase[77586]
All kills happened within 100 ms of the SIGTERM.
What I think is happening
serve.tsfollows container logs in an endlessfor (;;)loop:docker logs -fis spawned, and when it fails with a retriable error (no such container,conflict, ...) it sleeps 400 ms and spawns again. Every spawn happens in the same Effect scope, so the child handles are never released until the whole scope closes.- The Node/Bun child-process spawner starts each child with
detached: true(own process group). Its scope release for a child that already exited non-zero callsprocess.kill(-pid, "SIGTERM"). - In my case another terminal ran
supabase stop && supabase startwhilefunctions servewas running. The serve kept looping on the old container ID for hours (about onedockerspawn per second, all exiting non-zero). - The serve command itself finished a day later (the trace shows the
functions.servespan ending) but the process never exited. It sat for five more days. - On
SIGTERMthe scope finally closed and thousands of accumulated releases each rankill(-pid)against a PID number that had long since been reused. Every process-group leader started after the loop began that happened to land on one of those PIDs was signalled.
Only processes that predated the serve survived, which matches PID reuse exactly.
Expected behavior
- A release for a child that has already exited must not send any signal. Killing by
-pidafter exit can only ever hit a stranger. - Each
docker logs -fretry should be scoped per iteration so handles are released as they exit. - The process should exit when the command completes instead of hanging.
Steps to reproduce
supabase start, thensupabase functions serve my-function &.- In another shell:
supabase stop && supabase start. - Leave it for a long time (hours to days) so the PID counter wraps.
kill -TERM <serve pid>.- Check
log show --last 5m --predicate 'eventMessage CONTAINS "sent by supabase"'.
Related
- fix(cli): report functions serve outcomes from real container state (CLI-2426) #6594, fix(cli): report a killed edge runtime container instead of a lost log stream #6615 and fix(cli): classify an out-of-memory kill when the docker logs stream errors #6714 changed how the log stream reacts to a lost container, but the release-on-exit
kill(-pid)path still exists. - 2.118.0-beta.72 (darwin-arm64):
supabase --versionblocks forever in open(2), and one stuck process blocks every later invocation #6771 is another report of a CLI process that never exits.
Activity
7ttp commentedon Sep 28, 2026
hey @rofreytag super thanks for reporting! 💚
imo this was fixed in v2.118.0, so upgrading should sort it:
while digging i spotted a smol similar thingy in the stack runtime that was still reproducible on the latest version, so am patching it off here:
fix(stack): release log followers on exit (CLI-2521) (supabase#6861)