15.2.1.5 Container Process Count
A focused guide to Container Process Count, connecting core concepts with practical Docker and container operations.
Container process count is the number of processes and threads running inside a container's PID namespace at a given moment, a metric that is easy to overlook compared to CPU, memory, and network usage but that directly determines whether a container is approaching a hard limit on process creation, and that often reveals process leaks, runaway forking, or zombie process accumulation well before those problems manifest as a more dramatic resource exhaustion event.
Viewing process count directly
Docker's stats interface includes a PID count alongside its other resource metrics, reflecting the number of processes and threads currently running inside the container's PID namespace:
docker stats my-api --no-stream
CONTAINER ID NAME PIDS
3f29a8c1d8e2 my-api 24
docker top my-api
docker top provides the actual process listing behind that count, which is the more useful view when investigating what is specifically contributing to an unexpectedly high number rather than just the raw figure.
Setting a PID limit
Docker supports capping the maximum number of processes a container can create, which protects the host against a fork bomb or a runaway process-spawning bug inside any single container consuming the host's entire process table, a resource shared across every container and process on the host regardless of individual container memory or CPU limits:
docker run -d --pids-limit=100 my-api
docker inspect my-api --format '{{.HostConfig.PidsLimit}}'
A host's total process ID space is finite and shared across the entire system; without a per-container limit, a single misbehaving container can exhaust it, which would affect every other container and process on the host, not just the one responsible for the runaway forking.
Diagnosing a rising process count
A process count that climbs steadily over time, rather than remaining roughly stable around an expected baseline for a given workload, is a strong signal of either a leak in how the application manages child processes or an accumulation of zombie processes that have exited but have not been properly reaped:
docker exec my-api ps aux | grep -c .
docker exec my-api ps aux | grep 'Z'
A growing number of entries in the Z (zombie) state specifically points to a parent process that is not calling wait() on its terminated children, which is a common issue for applications that spawn subprocesses without correctly handling their exit, and is part of why an init process like tini is recommended as PID 1: a proper init process reaps orphaned and zombie processes automatically.
PID limit failures and their symptoms
When a container hits its configured PID limit, new process or thread creation fails, which can manifest in confusing, indirect ways depending on what the application was attempting to do at the time, a failed fork for a worker process, an inability to spawn a subprocess, or a thread pool that cannot grow as expected:
docker logs my-api | grep -i "resource temporarily unavailable"
Error: spawn EAGAIN
An error like EAGAIN or "resource temporarily unavailable" during process or thread creation, occurring specifically once a container's process count is already near its configured limit, is a recognizable signature worth checking for directly when an application reports a vague spawning or threading failure under load.
Process count and thread-heavy runtimes
For runtimes that map application-level concurrency onto OS threads, a high degree of concurrency in the application can translate directly into a high process and thread count at the container level, which is expected behavior rather than a leak, but still needs to be accounted for when setting an appropriate PID limit:
docker exec my-api ls /proc/1/task | wc -l
A Java application using a large thread pool, for instance, may legitimately show a process count in the hundreds under normal, healthy operation, which means the PID limit for that specific container needs to be set with this expected baseline in mind, rather than using a low, generic default appropriate for a typically single-threaded or low-concurrency service.
Setting an appropriate limit based on observed behavior
As with CPU and memory limits, the right PID limit depends on the specific application's actual, expected process and thread usage pattern under normal and peak load, rather than an arbitrary default applied uniformly across very different kinds of workloads:
docker stats --no-stream --format "{{.PIDs}}" my-api
docker update --pids-limit=200 my-api
Observing the actual peak process count over a representative period of normal operation, then setting the limit with reasonable headroom above that peak, avoids both an overly restrictive limit that fails during legitimate load and an effectively unbounded one that provides no real protection.
Monitoring process count over time
As with other resource metrics, exporting process count to a persistent metrics system rather than relying only on point-in-time docker stats checks reveals the difference between a stable, expected baseline and a slow, continuous climb characteristic of a leak:
container_processes{name="my-api"}
A graph that shows process count climbing steadily over hours or days, without a corresponding increase in actual request or workload volume, is worth investigating directly as a likely process or thread leak well before it reaches the configured PID limit and starts causing visible spawning failures.
Common mistakes
- Running containers with no PID limit configured at all, leaving the host's shared process table vulnerable to exhaustion from any single misbehaving container.
- Setting a PID limit without accounting for a legitimately thread-heavy runtime's normal, healthy baseline, causing spawning failures during ordinary operation.
- Not running an init process capable of reaping zombie processes, allowing them to accumulate silently until they contribute to an otherwise unexplained rising process count.
- Diagnosing spawning or threading failures as application bugs without first checking whether the container's PID limit had already been reached.
- Reacting only to point-in-time process count readings rather than tracking the trend over time, missing a slow leak until it manifests as an actual limit-related failure.
Container process count is a frequently overlooked metric that directly protects shared host resources through PID limiting, and tracking it over time, alongside running a proper init process to reap zombies, catches process and thread leaks well before they escalate into spawning failures or, in the absence of any limit at all, host-wide process table exhaustion.