Docker image uses ~2 GB idle on hosts with unlimited nofile (ERTS port table) #47

Closed
opened 2026-10-18 18:44:36 +00:00 by jfro · 0 comments
Owner

The published v0.6.0 image (git.knope.dev/jfro/fuzzy_catalog:0.6.0, index sha256:821b85db…, same digest as 0.6 and latest) sits at about 2.18 GiB RSS while idle on Docker hosts whose containerd/dockerd unit sets LimitNOFILE=infinity (e.g. Debian's docker.io package). A second BEAM in the container (bin/fuzzy_catalog rpc / eval, the normal way to run remote commands) gets OOM-killed on a 4 GB host (exit 137, OOMKilled=true).

Not a security issue, but a large memory regression on common hosts.

Cause

On those hosts containers get ulimit -n = 1073741816. ERTS sizes its port table from the file descriptor limit (port_limit 134217727), so :erlang.memory(:system) is ~2.17 GB before the app does anything.

Repro

docker run --rm -e DATABASE_URL=x -e SECRET_KEY_BASE=x git.knope.dev/jfro/fuzzy_catalog:0.6.0 \
  /app/bin/fuzzy_catalog eval 'IO.inspect({:erlang.memory(:total), :erlang.system_info(:port_limit)})'
# {2181263920, 134217727}
  • with -e ERL_MAX_PORTS=65536 → {35542224, 65536}
  • with --ulimit nofile=65536:65536 → {34828688, 65536}

Fix

Set ENV ERL_MAX_PORTS=65536 in the Dockerfile runner stage (still overridable at runtime with -e ERL_MAX_PORTS=…) and note it in the README. Workaround for v0.6.0 until then: run with -e ERL_MAX_PORTS=65536 or --ulimit nofile=65536:65536.

Found by Nye while checking the published image.

The published v0.6.0 image (`git.knope.dev/jfro/fuzzy_catalog:0.6.0`, index `sha256:821b85db…`, same digest as `0.6` and `latest`) sits at about **2.18 GiB RSS while idle** on Docker hosts whose containerd/dockerd unit sets `LimitNOFILE=infinity` (e.g. Debian's `docker.io` package). A second BEAM in the container (`bin/fuzzy_catalog rpc` / `eval`, the normal way to run remote commands) gets OOM-killed on a 4 GB host (exit 137, `OOMKilled=true`). Not a security issue, but a large memory regression on common hosts. ### Cause On those hosts containers get `ulimit -n` = 1073741816. ERTS sizes its port table from the file descriptor limit (`port_limit` 134217727), so `:erlang.memory(:system)` is ~2.17 GB before the app does anything. ### Repro ```sh docker run --rm -e DATABASE_URL=x -e SECRET_KEY_BASE=x git.knope.dev/jfro/fuzzy_catalog:0.6.0 \ /app/bin/fuzzy_catalog eval 'IO.inspect({:erlang.memory(:total), :erlang.system_info(:port_limit)})' # {2181263920, 134217727} ``` - with `-e ERL_MAX_PORTS=65536` → `{35542224, 65536}` - with `--ulimit nofile=65536:65536` → `{34828688, 65536}` ### Fix Set `ENV ERL_MAX_PORTS=65536` in the Dockerfile runner stage (still overridable at runtime with `-e ERL_MAX_PORTS=…`) and note it in the README. Workaround for v0.6.0 until then: run with `-e ERL_MAX_PORTS=65536` or `--ulimit nofile=65536:65536`. Found by Nye while checking the published image.
jfro closed this issue 2026-10-18 18:49:36 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
jfro/fuzzy_catalog#47
No description provided.