From e2eb39a02e4e945cd63b4a787c6454590f9f66ef Mon Sep 17 00:00:00 2001 From: Ahmed Allam <49919286+0xallam@users.noreply.github.com> Date: Thu, 16 Jul 2026 09:17:46 -0700 Subject: [PATCH] fix(runtime): cap sandbox container logs to prevent host disk exhaustion (#785) --- strix/runtime/docker_client.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/strix/runtime/docker_client.py b/strix/runtime/docker_client.py index 4dfacf6d..11cdff57 100644 --- a/strix/runtime/docker_client.py +++ b/strix/runtime/docker_client.py @@ -42,6 +42,7 @@ from agents.sandbox.session.sandbox_session import SandboxSession from agents.sandbox.types import ExposedPortEndpoint from docker import errors as docker_errors # type: ignore[import-untyped, unused-ignore] from docker.models.containers import Container # type: ignore[import-untyped, unused-ignore] +from docker.types import LogConfig # type: ignore[import-untyped, unused-ignore] from docker.types import Mount as DockerSDKMount # type: ignore[import-untyped, unused-ignore] from docker.utils import parse_repository_tag # type: ignore[import-untyped, unused-ignore] from requests.exceptions import RequestException @@ -89,6 +90,26 @@ def _apply_resource_limits(create_kwargs: dict[str, Any]) -> None: create_kwargs["pids_limit"] = int(pids_limit) +def _apply_log_limits(create_kwargs: dict[str, Any]) -> None: + """Bound the container's json-file log so a runaway process in the sandbox + (e.g. a tool that busy-loops writing to stdout) cannot fill the host disk + and take the Docker daemon down with it. + + Unlike the cgroup caps above, this defaults **on** — docker's own default + is an unbounded json-file, which is unsafe for an autonomous agent that + executes arbitrary commands. ``max-file`` rotation means the on-disk cap is + ``max-size * max-file``. Set ``STRIX_SANDBOX_LOG_MAX_SIZE`` to ``0``/``off`` + to opt back out to docker's default.""" + max_size = os.environ.get("STRIX_SANDBOX_LOG_MAX_SIZE", "50m").strip() + if max_size.lower() in ("0", "off", "none", "unlimited"): + return + max_file = os.environ.get("STRIX_SANDBOX_LOG_MAX_FILE", "3").strip() or "3" + create_kwargs["log_config"] = LogConfig( + type=LogConfig.types.JSON, + config={"max-size": max_size, "max-file": max_file}, + ) + + class StrixDockerSandboxSession(DockerSandboxSession): sandbox_network: str = "" @@ -200,6 +221,7 @@ class StrixDockerSandboxClient(DockerSandboxClient): _apply_sandbox_network(create_kwargs) _apply_resource_limits(create_kwargs) + _apply_log_limits(create_kwargs) # Strix injection: host bind mounts (e.g. large repos passed via --mount) # that bypass the SDK's file-by-file LocalDir copy.