Skip to content

fix(cli): clamp edge-runtime nofile ulimit to the host hard limit - #6284

Merged
avallete merged 3 commits into
developfrom
avallete/edge-runtime-nofile-limits-e14092
Aug 20, 2026
Merged

fix(cli): clamp edge-runtime nofile ulimit to the host hard limit#6284
avallete merged 3 commits into
developfrom
avallete/edge-runtime-nofile-limits-e14092

Conversation

@avallete

@avalleteavallete commented Aug 20, 2026

Copy link
Copy Markdown
Member

Summary

The Edge Runtime container was always created with --ulimit nofile=65536:65536 — a raise inherited from the Go CLI so many concurrent Deno isolates can run (#5151). Sandboxed hosts cap the hard nofile limit lower (e.g. 20,000 in the Claude Code sandbox), their docker daemon shares that cap, and requesting more than the daemon can grant fails the container start outright.

This adds edgeRuntimeNofileUlimit(platformOs) in @supabase/stack and uses it at both docker call sites (stack service defs for the next shell, and shared/functions/serve.ts for legacy functions serve/start):

  • On Linux, the requested value is clamped to the process's own hard limit, read via process.report.getReport().userLimits.open_files (the standard runtime API, implemented by both Bun and Node — verified under Bun in a Linux container with a constrained --ulimit).
  • The clamp only ever lowers the request, so the worst case on an exotic setup (client more constrained than a remote/rootful daemon) is a smaller fd budget, never a failed start.
  • When the clamp lowers the request, the CLI emits a warning naming the reduced limit ("Edge Runtime file descriptor limit lowered to N: …") so the smaller fd budget is visible instead of silent. It surfaces through Output.warn in startEdgeRuntimeContainer, covering both functions serve and legacy start; the stack ServiceDef builder is pure with no output channel and stays silent.
  • Off Linux the full 65536 raise is kept, since the daemon runs in a VM with its own limits.

The Go-parity divergence is documented in apps/cli/docs/go-cli-divergences.md.

Reviewer notes: the helper stays a plain sync leaf (no failure modes/retries/resources) per the repo's Effect-native carve-out — both call sites are sync ServiceDef builders — and takes platformOs as input to match how the stack threads platform.os instead of reading process.platform ambiently. It now returns { arg, limit, clampWarning? } with the host hard limit injectable (defaulting to the real process.report probe), so the clamp decision and warning text carry deterministic unit coverage alongside the pure seams (hardNofileLimitFromReport, clampNofileLimit), including the 20,000-cap case.

Linked issue

Closes CLI-2220 (Linear)

  • The linked issue is open and carries the open-for-contribution label (or I'm a Supabase maintainer).

Checklist

  • The PR title follows Conventional Commits (e.g. fix(cli): …).
  • Tests added or updated for the change.
  • pnpm check:all and pnpm test pass for the workspace(s) I touched.

🤖 Generated with Claude Code

The Edge Runtime container was always created with --ulimit
nofile=65536:65536 (raised for many concurrent Deno isolates,
#5151). Sandboxed hosts cap the hard nofile limit lower
(e.g. 20,000 in Claude Code) and their docker daemon shares that cap,
so requesting more failed the container start outright (CLI-2220).
On Linux, clamp the requested value to the process's own hard limit
(process.report userLimits) — downward only, so a constrained client
gets a smaller fd budget instead of a failed start. Elsewhere the
daemon runs in a VM with its own limits, so the full 65536 raise is
kept. One helper in @supabase/stack serves both docker call sites
(stack service defs and legacy functions serve/start).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@avallete
avallete requested a review from a team as a code ownerAugust 20, 2026 16:03
@github-actions

github-actionsBot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Supabase CLI preview

npx --yes https://pkg.pr.new/supabase/cli/supabase@0bda9c4f5a12f9a0266a422006244db895b3c4d3

Preview package for commit 0bda9c4.

edgeRuntimeNofileUlimit now returns { arg, limit, clampWarning? } instead
of the bare --ulimit string, with the warning present exactly when the
host's hard cap forced the request below the 65536 raise. The host hard
limit is injectable (defaulting to the real process.report probe) so the
clamp decision and message are deterministically unit-testable.
startEdgeRuntimeContainer emits the warning through Output.warn, covering
both `functions serve` and legacy `start`. The stack ServiceDef builder is
pure with no output channel, so that call site just consumes .arg.
Verified end-to-end in a 20000-hard-cap sandbox: the bring-up warns
"Edge Runtime file descriptor limit lowered to 20000: ..." and the
container starts with nofile=20000:20000.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0196hzWc16Jr2cYoYopWkPbo

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:05827e1c4a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadpackages/stack/src/services/edge-runtime.ts
Only the legacy functions serve/start path has an Output channel at
bring-up time; the @supabase/stack ServiceDef builder is pure and, in
managed mode, runs inside the daemon process with no user terminal, so
it applies the clamp silently until a BuildResult diagnostics channel
exists.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@avallete
avallete enabled auto-merge August 20, 2026 17:08
@avallete
avallete added this pull request to the merge queueAug 20, 2026
Merged via the queue into develop with commit 3e2df92Aug 20, 2026
30 checks passed
@avallete
avallete deleted the avallete/edge-runtime-nofile-limits-e14092 branch August 20, 2026 19:27
Sign up for freeto 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.

3 participants

@avallete@jgoux@claude