Raise Docker open file limit at startup

Raise the runtime container soft nofile limit before supervisord starts so WebUI and managed services inherit a larger descriptor allowance. Add an explicit compose nofile example, document the startup contract, and cover the limit raise and hard-limit cap with focused regression tests.

Alessandro committed Jun 24, 2026 at 11:00 UTC 68c31b51e1a786eb870700c8320628415c0ef8e0
4 files changed +107
docker/run/AGENTS.md
+1
@@ -21,6 +21,7 @@
21 - Keep the two-runtime Python model aligned with the root contract.
22 - Do not bake secrets, local `.env` values, or user data into the image.
23 - Runtime startup must ensure `/a0/usr/uploads` exists before supervised services start.
24 +- Runtime startup raises the soft open-file limit toward `A0_NOFILE_LIMIT` (default `65535`) before supervisord starts, bounded by the container hard limit.
25
26 ## Work Guidance
27
docker/run/docker-compose.yml
+4
@@ -6,5 +6,9 @@ services:
6 - ./agent-zero:/a0
7 ports:
8 - "50080:80"
9 + ulimits:
10 + nofile:
11 + soft: 65535
12 + hard: 65535
13 extra_hosts:
14 - "host.docker.internal:host-gateway"
docker/run/fs/exe/initialize.sh
+37
@@ -9,6 +9,43 @@ if [ -z "$1" ]; then
9 fi
10 BRANCH="$1"
11
12 +raise_open_file_limit() {
13 + local requested="${A0_NOFILE_LIMIT:-65535}"
14 + local soft
15 + local hard
16 + local target
17 +
18 + if ! [[ "$requested" =~ ^[0-9]+$ ]] || [ "$requested" -lt 1 ]; then
19 + echo "Warning: invalid A0_NOFILE_LIMIT='$requested'; keeping open file limit at $(ulimit -S -n)." >&2
20 + return
21 + fi
22 +
23 + soft="$(ulimit -S -n)"
24 + hard="$(ulimit -H -n)"
25 +
26 + if [ "$soft" = "unlimited" ]; then
27 + echo "Open file limit is already unlimited."
28 + return
29 + fi
30 +
31 + target="$requested"
32 + if [ "$hard" != "unlimited" ] && [ "$target" -gt "$hard" ]; then
33 + target="$hard"
34 + fi
35 +
36 + if [ "$target" -gt "$soft" ]; then
37 + if ulimit -S -n "$target"; then
38 + echo "Raised open file soft limit from $soft to $(ulimit -S -n) (hard: $hard)."
39 + else
40 + echo "Warning: failed to raise open file soft limit from $soft to $target (hard: $hard)." >&2
41 + fi
42 + else
43 + echo "Open file soft limit is $soft (target: $requested, hard: $hard)."
44 + fi
45 +}
46 +
47 +raise_open_file_limit
48 +
49 # Copy all contents from persistent /per to root directory (/) without overwriting
50 cp -r --no-preserve=ownership,mode /per/* /
51
tests/test_docker_initialize_limits.py new
+65
@@ -0,0 +1,65 @@
1 +import re
2 +import resource
3 +import subprocess
4 +from pathlib import Path
5 +
6 +import pytest
7 +
8 +
9 +REPO_ROOT = Path(__file__).resolve().parents[1]
10 +INITIALIZE_SCRIPT = REPO_ROOT / "docker" / "run" / "fs" / "exe" / "initialize.sh"
11 +
12 +
13 +def _raise_limit_function() -> str:
14 + text = INITIALIZE_SCRIPT.read_text(encoding="utf-8")
15 + match = re.search(r"^raise_open_file_limit\(\) \{\n.*?^\}\n", text, re.M | re.S)
16 + assert match, "initialize.sh must define raise_open_file_limit"
17 + return match.group(0)
18 +
19 +
20 +def _run_bash(script: str) -> subprocess.CompletedProcess[str]:
21 + return subprocess.run(
22 + ["bash", "-c", script],
23 + check=True,
24 + text=True,
25 + capture_output=True,
26 + )
27 +
28 +
29 +def test_initialize_raises_soft_open_file_limit_to_requested_target():
30 + function = _raise_limit_function()
31 +
32 + result = _run_bash(
33 + f"""
34 + set -euo pipefail
35 + {function}
36 + ulimit -S -n 1024
37 + A0_NOFILE_LIMIT=4096
38 + raise_open_file_limit
39 + test "$(ulimit -S -n)" = "4096"
40 + """
41 + )
42 +
43 + assert "Raised open file soft limit from 1024 to 4096" in result.stdout
44 +
45 +
46 +def test_initialize_caps_open_file_limit_at_hard_limit():
47 + _soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
48 + if hard != resource.RLIM_INFINITY and hard < 2048:
49 + pytest.skip("host hard open-file limit is too low for this regression test")
50 +
51 + function = _raise_limit_function()
52 +
53 + result = _run_bash(
54 + f"""
55 + set -euo pipefail
56 + {function}
57 + ulimit -S -n 1024
58 + ulimit -H -n 2048
59 + A0_NOFILE_LIMIT=65535
60 + raise_open_file_limit
61 + test "$(ulimit -S -n)" = "2048"
62 + """
63 + )
64 +
65 + assert "Raised open file soft limit from 1024 to 2048" in result.stdout