fix: allow disabling per-container memory/CPU caps (CONTAINER_*=0)
All checks were successful
CI / test (push) Successful in 37s

Some hosts can't enforce cgroup limits for nested containers at all
(confirmed on an OpenRC-based unprivileged LXC with no systemd to
delegate cgroup controllers) — docker run fails outright trying to
write memory.max. 0 now skips requesting that cap instead of erroring
every deploy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 04:38:45 +02:00
parent 698173c79d
commit 1019672981
3 changed files with 25 additions and 3 deletions

View File

@@ -25,7 +25,10 @@ SESSION_SECRET=change-me-too
# installation env vars and bearer tokens at rest # installation env vars and bearer tokens at rest
MASTER_KEY=change-me-to-a-random-string-of-at-least-32-characters MASTER_KEY=change-me-to-a-random-string-of-at-least-32-characters
# Resource caps applied to every installed MCP container (same limit for all, no per-installation override in V1) # Resource caps applied to every installed MCP container (same limit for all, no per-installation
# override in V1). Set either to 0 to disable that cap — needed on hosts that can't enforce cgroup
# limits for nested containers (e.g. an unprivileged, OpenRC-based LXC with no systemd to delegate
# cgroup controllers; `docker run` fails outright there if a limit is requested).
CONTAINER_MEMORY_MB=512 CONTAINER_MEMORY_MB=512
CONTAINER_CPUS=1 CONTAINER_CPUS=1

View File

@@ -30,6 +30,21 @@ build failed with "BuildKit is enabled but the buildx component is missing or br
confirmed on the first real build attempt. Fixed by also copying confirmed on the first real build attempt. Fixed by also copying
`/usr/local/libexec/docker/cli-plugins/docker-buildx` from the same `docker:27-cli` image. `/usr/local/libexec/docker/cli-plugins/docker-buildx` from the same `docker:27-cli` image.
## Per-container memory/CPU cgroup limits need the host to delegate controllers
`docker run --memory=...` fails outright ("error setting cgroup config... memory.max: no
such file or directory") on a host where `cgroup.subtree_control` isn't populated down to
where the container's cgroup lives. Confirmed on an unprivileged, OpenRC-based nested LXC
(Alpine, no systemd) with `nesting=1`: systemd normally does this delegation automatically
at boot; OpenRC doesn't, so `cgroup.subtree_control` stays empty at the LXC's own root and
no controller is ever available to nested Docker containers. `CONTAINER_MEMORY_MB`/
`CONTAINER_CPUS` can both be set to `0` to skip asking for a cap at all — on a host like this
the outer LXC's own memory limit (if Proxmox/systemd-nspawn/etc. sets one) is the only
enforced ceiling either way. Properly fixing the delegation means moving every process
already in the LXC's root cgroup into a leaf scope and then writing the wanted controllers
to the root's `cgroup.subtree_control` — not done here, since it's a live production LXC and
restructuring its cgroup tree wasn't something to risk without asking first.
## Nixpacks silently picks its own default runtime version ## Nixpacks silently picks its own default runtime version
Nixpacks does not fail when a repo's `engines`/version requirement isn't met — it silently falls back to its own default (e.g. picked Node 18 for a repo requiring Node >=20 in `Social-MCP`). The build goes green, then the container crashes at runtime (`crypto is not defined`, missing globals, etc.). Nixpacks does not fail when a repo's `engines`/version requirement isn't met — it silently falls back to its own default (e.g. picked Node 18 for a repo requiring Node >=20 in `Social-MCP`). The build goes green, then the container crashes at runtime (`crypto is not defined`, missing globals, etc.).

View File

@@ -39,8 +39,12 @@ export async function runInstallationContainer(installation: Installation): Prom
HostConfig: { HostConfig: {
RestartPolicy: { Name: "unless-stopped" }, RestartPolicy: { Name: "unless-stopped" },
NetworkMode: config.mcpNetworkName, NetworkMode: config.mcpNetworkName,
Memory: config.containerMemoryMb * 1024 * 1024, // 0 disables the cap: some hosts (e.g. an unprivileged, OpenRC-based nested LXC with no
NanoCpus: Math.round(config.containerCpus * 1e9), // systemd to delegate cgroup controllers) can't enforce per-container memory/CPU limits
// at all — `docker run` there fails outright trying to write memory.max. Confirmed on a
// real deploy; the outer host's own cap (if any) is the only ceiling in that case.
...(config.containerMemoryMb > 0 ? { Memory: config.containerMemoryMb * 1024 * 1024 } : {}),
...(config.containerCpus > 0 ? { NanoCpus: Math.round(config.containerCpus * 1e9) } : {}),
}, },
}); });