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.ts follows container logs in an endless for (;;) loop: docker logs -f is 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 calls process.kill(-pid, "SIGTERM").
- In my case another terminal ran
supabase stop && supabase start while functions serve was running. The serve kept looping on the old container ID for hours (about one docker spawn per second, all exiting non-zero).
- The serve command itself finished a day later (the trace shows the
functions.serve span ending) but the process never exited. It sat for five more days.
- On
SIGTERM the scope finally closed and thousands of accumulated releases each ran kill(-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
-pid after exit can only ever hit a stranger.
- Each
docker logs -f retry 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, then supabase 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
Affected area
functions serveSupabase 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
SIGTERMto a long-runningfunctions serveprocess 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: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.detached: true(own process group). Its scope release for a child that already exited non-zero callsprocess.kill(-pid, "SIGTERM").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).functions.servespan ending) but the process never exited. It sat for five more days.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
-pidafter exit can only ever hit a stranger.docker logs -fretry should be scoped per iteration so handles are released as they exit.Steps to reproduce
supabase start, thensupabase functions serve my-function &.supabase stop && supabase start.kill -TERM <serve pid>.log show --last 5m --predicate 'eventMessage CONTAINS "sent by supabase"'.Related
kill(-pid)path still exists.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.