skip to content

Why does a Docker container normally run a single foreground process rather than several?

level: juniorimportance: must knowfreq 72%

answer

  1. Ask what the engine is actually watching
  2. Everything reported hangs off one process
  3. One concern, not literally one PID
  4. Logs, exit code, restart policy, limits
  5. A second service is invisible to Docker

basics

~10 s

Docker Engine supervises only PID 1. With one foreground process per container, the container's logs, exit code, restart policy and resource limits all describe that process; anything else running inside is governed by nothing.

solid answer

~50 s

The rule is really **one concern per container**, not literally one PID. A container is a namespace-and-cgroup wrapper around a process tree whose root is PID 1, and every surface Docker gives you is anchored on PID 1: `docker logs` replays PID 1's stdout and stderr, `State.ExitCode` is PID 1's status, `--restart` is only evaluated when PID 1 exits, and `docker stop` signals PID 1 alone. Put two independent services behind one PID 1 and the engine cannot tell you which one died, cannot restart just the broken one, and caps them jointly with one `--memory`/`--cpus` budget. Split them and each gets its own image, logs, exit code, restart policy, limits and scaling. A process that forks its own workers -- a web server master and workers, a runtime's thread pool -- is still one concern and perfectly normal.

code

bash · 4 lines
bash
docker run -d --name reranker --restart on-failure --memory 512m myorg/reranker:2026.09.1
docker inspect -f '{{.State.Status}} {{.State.ExitCode}}' reranker
docker logs --tail 20 reranker
docker top reranker

go deeper

for a junior

Be ready to state the rule as one concern per container and name two things the engine anchors on PID 1, such as docker logs and the exit code. Knowing that forked workers are still fine keeps you out of the usual trap.

for a middle

Explain the mechanics: the container lives as long as PID 1, docker stop signals PID 1, State.ExitCode is PID 1's, and restart policies are evaluated on container exit. Then show what each of those loses when a second service hides inside.

for a senior

Show the operational consequence with a real example -- a container reporting Up while the service inside is dead, no restart, no exit code, no logs -- and describe how splitting restores per-service restart, limits and alerting.

for a principal

Own the position that the rule is about who owns supervision. Be ready to say when you would grant an exception, what you demand of it in return, and what a fleet loses when every team invents its own in-container process manager.

### Where the rule comes from A container is not a small virtual machine. It is one process tree that the engine starts inside a fresh set of namespaces and a cgroup, and the root of that tree is PID 1 inside the container's own PID namespace. Every management surface Docker Engine gives you is anchored on that one process. The rule "one process per container" is not an aesthetic preference; it is a statement about what the engine can and cannot see. Walk the surfaces one at a time. **Lifecycle.** The container is `running` exactly as long as PID 1 is alive. When PID 1 returns, the container becomes `exited` and everything else left in the namespace is killed with it. `docker stop` sends its signal to PID 1 and to nobody else. So whichever process you make PID 1 is the only one whose start and stop the engine actually drives; everything else lives and dies at that process's mercy. **Exit status.** `docker inspect` reports `State.ExitCode`, and that value is PID 1's. If the service you actually care about is a grandchild of some other PID 1, its failure is translated -- or swallowed -- before the engine ever records anything. **Restart policy.** `--restart on-failure` and `--restart always` are evaluated when the *container* exits, which means when PID 1 exits. A crashed child inside a container whose PID 1 is still running is not a container exit, so no restart policy fires, no matter how dead the application is. **Logs.** `docker logs` replays the stdout and stderr streams the engine attached to PID 1 when it created the container. A second process that writes to a file inside the container, or to a private log of its own, produces nothing there. Central log collection, which reads those same streams, sees nothing either. **Limits and accounting.** `--memory`, `--cpus` and `--pids-limit` apply to the container's cgroup as a whole, and `docker stats` reports that cgroup's totals in one row. Two independent services sharing one container share one budget and one number, and when the kernel's out-of-memory killer fires inside that cgroup it picks its victim by its own heuristics -- not by which of your two services you would rather lose. **Deploy unit.** One image is one version, one build, one rollback. Two services in one image cannot be released, rolled back or scaled apart from each other. ### What the rule does *not* say It does not say "one PID". A web server with a master process and a pool of workers, an application server that pre-forks, a language runtime whose scheduler runs many OS threads, a process that shells out briefly -- all of these are one *concern*, and they are entirely normal in a container. The count that matters is concerns, not entries in `docker top`. The practical test is: if this thing died, would I want the container to die too? If yes, it belongs to the same concern. If I would want to restart it on its own, redeploy it on its own, or give it its own memory limit, it is a second concern. ### What splitting buys you Give each concern its own container and each one gets: its own image and version, its own stdout stream, its own exit code, its own restart policy, its own CPU and memory limits, its own scaling factor, and its own attack surface. The two containers talk over a user-defined bridge network -- where the engine resolves container names to addresses -- or through a shared volume when they genuinely need a common file or a unix socket. ### A concrete example A recommendation re-ranker written in Rust is compiled in a builder stage and copied into a slim runtime image; the whole build takes about 11 minutes, so the team is reluctant to make two images out of it. To keep a locally-cached model file fresh they add a small warmer process next to the re-ranker and start both from one parent process. Six days later the container still shows `Up 6 days` in `docker ps`, but the re-ranker itself died 41 hours in: the parent is alive, so the container never exited, so `--restart on-failure` never fired, so nothing paged anyone. The exit code that would have named the failure was never recorded, and the re-ranker's panic message went to a file nobody reads. Two images, built once each from the same source tree, would have produced a container that exited, restarted, and logged. ### The exceptions exist, and they are narrow Some workloads genuinely co-locate processes: a legacy application whose vendor ships its own supervisor, two processes that must share a namespace a container boundary would break, or an ephemeral build or test container that is not a long-running service at all. Those are decisions to make deliberately, with extra work to restore what you lost -- signals forwarded, a parent that exits when a child fails, all logs on stdout. They are not the default. ### Saying it in an interview The compact answer is: "one concern per container, because the engine only watches PID 1 -- its logs, its exit code, its restart policy and its limits all describe that one process, so a second service inside is governed by nothing." Then give one consequence and the exception about forked workers.

  • Does the rule mean a container may only ever contain one PID?
    No. It means one concern. A web server master with worker processes, a pre-forking application server, a runtime whose scheduler runs many OS threads, or an app that briefly shells out are all one concern and entirely normal. The test is whether you would ever want to restart, redeploy, scale or limit the second thing on its own; if you would, it is a second concern and wants its own container.
  • Two processes must talk over a unix socket on the local filesystem. Does that force them into one container?
    Usually not. Put the socket's directory on a volume mounted into both containers and they can still talk, while keeping separate images, logs, exit codes, restart policies and limits. Co-location is only forced when the coupling is something a container boundary genuinely breaks -- for example one process needing to see the other's process table.
  • How do you decide whether two things are one concern or two?
    Ask whether they share a lifetime and a failure domain. If the helper is meaningless without the app and you would always replace the pair together, it is one concern. If you would ever restart it alone, release it alone, scale it differently, or give it its own memory limit, it is two -- and the engine can only express that as two containers.

Docker is a watchman told to watch exactly one door. Anything else you move into the building is not forbidden -- it is simply invisible to him, so nobody notices when it stops.

saying these in an interview costs you the question

  • Treats a container as a small VM to fill with services
  • Thinks the rule forbids a process forking worker processes
  • Expects docker logs to show a second process's log file
  • Assumes --restart restarts a crashed process inside the container
  • Believes --memory can be applied per service inside one container
  • Says splitting is only about image size

context