Repository navigation
Conversation
Adds a per-server (and local web server) setting that is passed to docker build --cgroup-parent, so hosts can cap the CPU/memory that build steps may take away from running services, e.g. via a systemd slice with CPUQuota. Applies to Dockerfile, static and Nixpacks static builds. Claude-Session: https://claude.ai/code/session_018Sb7ATyKb8pduVLBUZH4Ac
| } | ||
| docker rm ${buildContainerId} | ||
| ${getStaticCommand(application)} | ||
| ${getStaticCommand(application, options)} |
There was a problem hiding this comment.
Nixpacks compilation remains unconstrained
For a Nixpacks application with publishDirectory, the resource-intensive source compilation runs in the earlier nixpacks build command. The cgroup option is forwarded only to the final Nginx packaging build, so compilation remains unconstrained and the setting does not provide the advertised resource isolation for Nixpacks-static builds.
Knowledge Base Used: Build and Compose workflows
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Agreed. nixpacks has no --cgroup-parent (only --cpu-quota/--memory), so limiting only the nginx packaging step was misleading. Dropped the pass-through for Nixpacks in 91cbfdd and narrowed the UI text and PR description to Dockerfile and static builds. Wiring nixpacks --cpu-quota/--memory could be a follow-up.
| const cgroupParent = await resolveBuildCgroupParent( | ||
| application.buildServerId || application.serverId, | ||
| ); |
There was a problem hiding this comment.
Preview builds use wrong settings
When an application has a dedicated build server, preview builds still execute on application.serverId, but this code resolves the cgroup parent from buildServerId. The preview can therefore use a cgroup belonging to another host, causing Docker to reject the build if that cgroup is absent or leaving the actual build host's configured limit unused.
Knowledge Base Used:
There was a problem hiding this comment.
Good catch. getBuildCommand now takes the executing server explicitly (buildServerId || serverId by default) and both preview paths pass application.serverId, matching where the command actually runs. Fixed in 91cbfdd.
…tual build host Nixpacks has no --cgroup-parent, so passing it only to the final nginx packaging build was misleading; drop it there and document the scope. Preview deployments always build on the deploy server, so resolve the cgroup parent for that host instead of the assigned build server. Claude-Session: https://claude.ai/code/session_018Sb7ATyKb8pduVLBUZH4Ac
The built-in BuildKit executor runs runc with the cgroupfs manager, so systemd slice notation is created as a literal directory instead of a scope under the slice. An absolute path such as /builds ends up as /sys/fs/cgroup/builds/buildkit/<id>, where a cpu.max on /builds caps all builds together (verified on Docker with the systemd cgroup driver). Claude-Session: https://claude.ai/code/session_018Sb7ATyKb8pduVLBUZH4Ac
|
Corrected the usage guidance in cafacb1: the built-in BuildKit executor uses runc's cgroupfs manager, so systemd slice notation is created as a literal directory and does not inherit a slice's limits. An absolute cgroupfs path such as /builds works as intended (step cgroups end up as /sys/fs/cgroup/builds/buildkit/, verified on Docker 29 with the systemd cgroup driver). Placeholder, UI hint and PR description now recommend that form. |
| import { prepareEnvironmentVariablesForShell } from "../docker/utils"; | ||
| import { getBuildAppDirectory } from "../filesystem/directory"; | ||
| import type { ApplicationNested } from "."; | ||
| import type { DockerBuildOptions } from "./docker-file"; |
There was a problem hiding this comment.
Removing the Nixpacks cgroup-options parameter left DockerBuildOptions unused. The repository config treats unused imports as errors, so pnpm format-and-lint reports this file until the import is removed.
| import type { DockerBuildOptions } from "./docker-file"; |
What is this PR about?
Builds currently run unconstrained on the host. A single Next.js build saturates every core (Next.js scales its workers to
os.cpus()), and with the systemd cgroup driver every BuildKitRUNstep lands as adocker-<id>.scopedirectly insystem.slice, indistinguishable from the running services. There is no common cgroup to put a quota on, so builds degrade production latency on the same machine. Related reports: #2914, #4461.Docker already has the hook for this:
docker build --cgroup-parent. This PR exposes it as a setting.buildCgroupParentonwebServerSettings(local builds) andserver(remote/build servers), with a migration.getBuildCommandresolves the value for the host that runs the build (buildServerId || serverId) and passes--cgroup-parenttodocker buildfor Dockerfile and static builds.settings.updateBuildCgroupParentandserver.updateBuildCgroupParent.[A-Za-z0-9_.:/-]and shell-quoted; empty clears the setting.Typical use: enter an absolute cgroupfs path such as
/builds. BuildKit then creates its step cgroups as/sys/fs/cgroup/builds/buildkit/<id>, and a limit written to/sys/fs/cgroup/builds/cpu.max(e.g.2400000 100000for 24 cores) ormemory.maxcaps all builds on that host together, while running services stay unaffected. Verified on Docker 29 with the systemd cgroup driver. systemd slice notation (builds.slice:docker:) does not work with the built-in BuildKit executor: it runs runc with the cgroupfs manager, so the string is created as a literal directory under the daemon's cgroup instead of a scope under the slice.Not covered: Nixpacks (no
--cgroup-parentin the nixpacks CLI, only--cpu-quota/--memory), Heroku/Paketo (pack), Railpack (own buildx builder container) and Compose builds. The setting is a no-op for those. Preview deployments resolve the value for the deploy server, since they always build there. Not shown in cloud.Verification:
pnpm typecheck, biome on the changed files, and the vitest suite (newbuild-cgroup-parent.test.tsplus the existing suite). The four failures inapplication.real.test.tsfail identically oncanaryin my sandbox because network git clones are blocked there.openapi.jsonwas not regenerated on purpose:generate:openapicurrently produces a ~29k-line unrelated diff against the checked-in file.Checklist
canarybranch.Issues related (if applicable)
Related to #2914 and #4461 (no full fix for the stuck-job part of #4461).
https://claude.ai/code/session_018Sb7ATyKb8pduVLBUZH4Ac
The PR appears safe to merge after addressing the non-blocking unused import.
Summary
docker build.Reviews (2) · Last reviewed commit: "docs: recommend cgroupfs paths for the b..."