Skip to content

feat(cli): container cpu and memory limits for local stack services - #7604

Open
jgoux wants to merge 2 commits into
developfrom
juliengoux/cli-2979-managed-local-stack-cpu-and-memory
Open

jgoux wants to merge 2 commits into
developfrom
juliengoux/cli-2979-managed-local-stack-cpu-and-memory

Conversation

@jgoux

@jgoux jgoux commented Nov 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Local stack services can now be given container resource limits in config.toml:

[local.services.database.container]
memory = "2GiB"
cpus = 1.5
  • memory is a 1024-based size (512MiB, 1GiB; a bare number is bytes). Swap is capped at the same amount, so the limit is not silently doubled.
  • cpus is a positive number of CPUs; fractions are allowed.
  • Limits apply to every container launch, so they survive stack stop and stack start. Editing them shows as configuration drift in stack status until the stack is stopped and started again.
  • The table describes containers only: the native runtime ignores it, so one config.toml keeps working with both runtimes.
  • The out-of-memory hint now points to the new memory setting.

Adds a `[local.services.<service>.container]` table with `memory` and
`cpus`. Container runtimes apply them as `--memory`, `--memory-swap`,
and `--cpus` on every launch, so limits survive stop and start; the
native runtime ignores the table. Limit edits show as configuration
drift in `stack status` until the next stop and start.
@jgoux
jgoux requested a review from a team as a code owner November 12, 2026 15:38
@jgoux
jgoux requested a review from avallete November 12, 2026 15:38
@jgoux jgoux self-assigned this Nov 12, 2026
…managed-local-stack-cpu-and-memory

# Conflicts:
#	apps/cli/src/config/cli-config-key.ts
@github-actions

Copy link
Copy Markdown
Contributor

CLI benchmark (Lightweight Commands): no changes found

Commit e0a2115 against develop at d8f66a0 · 10 runs each · report only, never blocks merging

Command Workload Before After Change
supabase --version version 222 ms 228 ms no real change
supabase --help help 213 ms 212 ms no real change
supabase init init 223 ms 221 ms no real change
supabase migration new bench migration-new 210 ms 217 ms no real change
supabase config convert config-convert 219 ms 230 ms no real change (+5%, within normal variation)
supabase gen types --lang typescript --project-id replayfixtureprojref gen-types 247 ms 265 ms no real change (+7%, within normal variation)
supabase projects list projects-list 237 ms 237 ms no real change
supabase functions list --project-ref replayfixtureprojref functions-list 272 ms 248 ms no real change (-9%, within normal variation)

API calls: unchanged for all 8 commands.

Binary size: 134.8 MB, up 8.0 KB from 134.8 MB.

Details: timing ranges, API calls per command, slowest phases

Timing ranges

Command Workload Before range After range Change Verdict
supabase --version version 205-243 ms 212-252 ms +7 ms (+3%) within noise
supabase --help help 198-250 ms 199-246 ms -1 ms (-1%) within noise
supabase init init 214-235 ms 197-239 ms -1 ms (-1%) within noise
supabase migration new bench migration-new 203-232 ms 199-240 ms +7 ms (+3%) within noise
supabase config convert config-convert 198-240 ms 209-250 ms +11 ms (+5%) within noise
supabase gen types --lang typescript --project-id replayfixtureprojref gen-types 237-285 ms 232-290 ms +18 ms (+7%) within noise
supabase projects list projects-list 200-267 ms 216-274 ms +0 ms (+0%) within noise
supabase functions list --project-ref replayfixtureprojref functions-list 219-332 ms 233-276 ms -24 ms (-9%) within noise

API calls per command

Command Workload Before After Endpoint changes
supabase --version version 0 0
supabase --help help 0 0
supabase init init 0 0
supabase migration new bench migration-new 0 0
supabase config convert config-convert 0 0
supabase gen types --lang typescript --project-id replayfixtureprojref gen-types 2 2
supabase projects list projects-list 1 1
supabase functions list --project-ref replayfixtureprojref functions-list 2 2

Slowest phases

No command moved beyond noise, so there are no phase deltas.

Workflow run

Comment /bench to run the lightweight benchmark (this comment) again on the latest commit, or /bench full to also run the integration-heavy benchmark, which posts in its own comment.

@7ttp 7ttp left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

gave this a spin and it works nicely! limits stick across stop and start 😄

tho i've left a few smol nits inline:

pin: creation.pin,
endpoints: withSharedApiPort(creation, sharedPort),
config,
container: creation.container,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

a limit edit on a native stack still shows up as drift, even though native ignores limits

ig leave container out of the comparison when the stack runs natively?

Comment on lines +649 to +661
const withContainerLimits = (creation: ServiceCreationType): ServiceCreationType => {
const container = validatedConfig.local?.services?.[creation.service]?.container;
return container === undefined
? creation
: {
...creation,
container: {
...(container.memory === undefined
? {}
: { memoryBytes: ramInBytes(container.memory) }),
...(container.cpus === undefined ? {} : { cpus: container.cpus }),
},
};

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: an empty [local.services.auth.container] turns into container: {}, so a running stack reports it as drift

could it just return creation when neither memory nor cpus is set?

Comment on lines +50 to +63
Schema.makeFilter((value: string) => {
const bytes = sizeInBytes(value);
return bytes === undefined || (bytes >= 1 && Number.isSafeInteger(bytes))
? undefined
: "must be at least 1 byte and less than 8 PiB";
}),
);

const cpus = Schema.Finite.annotate({
description:
"CPU limit for the service container, in CPUs; fractions are allowed (for example `0.5`).",
examples: [0.5, 1, 2],
tags,
}).check(Schema.isGreaterThan(0, { message: "must be greater than 0" }));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

small one: memory = "1" and cpus = 0.001 pass here, but docker needs at least 6MiB and 0.01 cpus so stack start fails, prob worth rejecting those here so it fails at config load?


const memory = Schema.String.annotate({
description:
"Memory limit for the service container, as a size such as `512MiB` or `1GiB` (1024-based, case-insensitive; a bare number is bytes). Swap is limited to the same amount.",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this says swap is limited to the same amount, but --memory-swap equal to --memory means no swap at all

how about js "Swap is disabled"?

Comment on lines 12 to +13
export const SUGGEST_CONTAINER_MEMORY_LIMIT =
"The container ran out of memory and was killed. Raise the memory allocated to your container runtime (Docker Desktop: Settings > Resources), or free up memory on the host, and try again.";
"The container ran out of memory and was killed. Raise the memory allocated to your container runtime (Docker Desktop: Settings > Resources), raise `memory` in `[local.services.<service>.container]` if you set one, or free up memory on the host, and try again.";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

maybe for later: this hint only shows in legacy functions serve, which ignores the table, and a stack service over its limit just exits with 137 and no hint

what if legacy kept its old hint and the memory one went on that stack error?

This branch has not been deployed

No deployments
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.

2 participants