Memory leak in Docker container when using open_port for image conversion

Thanks for the clarification! If you’re already draining all port messages properly (stdout, stderr, exit_status) and closing the port reliably, then you’re right - the difference in behavior between Docker and bare metal might stem from:

Zombie processes inside the container
If sh spawns subprocesses (e.g., calling convert), and those aren’t properly waited on by the shell, they might become zombies. Even though port_close/1 is called, the process might linger in the container and keep holding memory. You might want to inspect with:

docker exec <container> ps aux | grep defunct

Missing exec in the shell command
If you’re spawning via something like open_port({spawn, "/bin/sh -c ..."}), but don’t prefix the actual command with exec, the shell might stay alive after the actual conversion tool exits. Try:

CommandStr = io_lib:format("exec convert ~s ~s", [InputPath, OutputPath]),
ShellCmd = io_lib:format("/bin/sh -c '~s'", [CommandStr]),
Port = open_port({spawn, ShellCmd}, [exit_status, {line, 1024}]).

Docker memory reporting quirks:
The container might appear to grow in memory because the BEAM doesn’t return freed memory to the OS immediately. erlang:memory/0 might show flat usage even when docker stats shows growth. Use recon_alloc from recon:

1> erlang:memory().
2> recon_alloc:memory(allocated_types).

Or top/htop inside the container.

Also worth noting - in practice, each situation can differ subtly. Even when the code pattern looks correct, subtle differences in environment (Docker cgroup limits, filesystem latency, shell behavior, memory allocators, etc.) can cause surprising side effects. In many real-world cases, narrow leaks or zombie processes are hard to trace without access to the specific project, logs, or ability to reproduce the issue under controlled conditions.