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
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_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
`/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 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: {
RestartPolicy: { Name: "unless-stopped" },
NetworkMode: config.mcpNetworkName,
Memory: config.containerMemoryMb * 1024 * 1024,
NanoCpus: Math.round(config.containerCpus * 1e9),
// 0 disables the cap: some hosts (e.g. an unprivileged, OpenRC-based nested LXC with no
// 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) } : {}),
},
});