cswork / libraryOpen practice workspace

Articles

Container Shutdown Interview Questions: PID 1, Signals, and Graceful Termination

Practice container shutdown interview questions with Docker stop signals, PID 1 wrappers, request draining, force-kill deadlines, and queue retries.

An API container is removed during a rollout. The process logs “starting shutdown,” yet clients see reset connections and queued jobs run twice. The interviewer asks whether Docker failed to send a signal. A strong answer starts earlier: which process is PID 1, which signal reaches it, who stops accepting work, and how much time exists before a force kill? Graceful termination is a sequence you can test, not a property bestowed by adding a SIGTERM handler.

This guide uses Docker's stop reference and Dockerfile ENTRYPOINT guidance for platform behavior. The service, timings, and interview prompts are original practice scenarios, not candidate reports or claims about a named employer's interview. For related operational practice, Troubleshoot a production server outage tests how you verify the actual user impact.

Question 1: What happens when you run docker stop?

Docker sends a stop signal to the container's main process and waits for a grace period. The default first signal is SIGTERM unless the image's STOPSIGNAL or a runtime option selects another. If the container is still running after the configured timeout, Docker sends SIGKILL. On Linux, Docker's default stop timeout is ten seconds when no other timeout is specified. That number is Docker-specific; do not carry it into Kubernetes or another orchestrator without checking its termination configuration.

The first interview move is to inspect the actual image and invocation: ENTRYPOINT, CMD, STOPSIGNAL, docker stop --time, and any orchestrator grace period. Then look at the process tree inside the container. Docker signals the main process. If that process is a shell wrapper which does not forward the signal, the server may never start its drain. A log from a wrapper that says “shutdown” does not prove that the child server received SIGTERM or finished work.

SIGKILL cannot be handled by the application. The force-kill deadline is a hard boundary for in-flight operations and cleanup. Extending it can give work more time, but it does not repair a process that never gets the first signal, a listener that keeps accepting requests, or a queue consumer that acknowledges messages too early.

Question 2: Why does PID 1 matter in a container?

The container's main process is the signal target and determines when the container ends. Docker's Dockerfile reference warns that the shell form of ENTRYPOINT runs through /bin/sh -c, which does not pass signals to the intended executable as the exec form does. A shell script can be acceptable if it ends with exec "$@" or otherwise explicitly forwards signals and waits for children, but the exact script matters. Inspect it; do not assume every shell wrapper is broken or every exec-form command is sufficient.

PID 1 has an additional process-management responsibility in a container: orphaned child processes need reaping. Docker documents a small init process available through --init for containers with multiple processes. It can forward signals and reap processes. It is not a substitute for an application's own shutdown logic, and it does not magically finish requests or commit queue offsets. A good candidate separates signal delivery, child process management, and business-level completion.

Suppose an image uses ENTRYPOINT sh -c "node server.js". The shell is PID 1, while Node is its child. Docker may send SIGTERM to the shell, and the Node process may not receive it in the manner the developer expected. Prefer an exec-form entrypoint such as ENTRYPOINT ["node", "server.js"] when appropriate. If setup code must run first, use an entrypoint script that ultimately execs the server and test the process tree. If multiple long-lived processes are required, choose an explicit supervisor or init strategy and prove signal fan-out.

Question 3: What does a graceful HTTP shutdown actually do?

It first stops new work from entering this instance, then lets accepted work finish within a deadline, then closes resources and exits. The order matters. If you close a database connection pool while handlers still use it, requests fail during a supposedly graceful drain. If you stop the listener but leave load-balancer routing unchanged for a moment, clients may still try a dead endpoint. If you mark the service unready but keep a long-lived WebSocket or streaming RPC open indefinitely, the grace period can expire before the stream exits.

A sensible sequence is to react to the stop signal, mark the instance unavailable to new traffic through the platform's readiness or routing mechanism, stop accepting new connections, allow in-flight requests to complete, bound long-lived streams, flush or commit necessary state, and exit before the force-kill deadline. The exact routing propagation and readiness semantics depend on the environment. In plain Docker, there may be no Kubernetes readiness gate at all; the load balancer or supervisor must have its own drain behavior.

An interview answer should make the deadline numerical. If the stop timeout is ten seconds and routing changes take two seconds to propagate, a request with a twelve-second server timeout cannot be promised completion. Either the operation must be safely interruptible, the grace window must be adjusted and coordinated, or the client must retry idempotently. “We wait for all requests” can become “we never exit” when a stream stalls.

A termination timeline to debug

This illustrative timeline shows how to reason from observations. It is not a measured Docker guarantee; scheduler timing, routing, runtime settings, and application code can change it.

Time and observationQuestion to askEvidence to collect
0s: stop requestedWhich signal and which PID receive it?Image configuration, process tree, signal log
1s: new requests still arriveDid routing or readiness stop them?Load-balancer target state and access logs
3s: old request still runningIs it progressing, draining, or blocked?Request ID, handler trace, active-connection count
9s: child process still aliveDid PID 1 forward and wait?Parent/child state and open connections
10s: process disappearsWas this clean exit or SIGKILL?Exit code, stop timeout, final log and runtime event

The useful metric is not simply “the process exited.” Check client-visible failures, lost or duplicated work, and whether active requests reached zero before the deadline. A process can exit with code zero while a queue acknowledgment was lost. Conversely, a nonzero exit may be an intentional signal-related code after successful handoff. Correlate the process event with work outcomes.

Question 4: What changes for queue consumers and background workers?

The worker should stop fetching new messages, finish or checkpoint work already leased, and let the broker redeliver unfinished work according to its contract. Acknowledgment timing matters. If it acknowledges before a side effect commits, a force kill can lose work. If it commits a side effect and is killed before acknowledging, redelivery can repeat the side effect. That is why a robust shutdown answer includes idempotency or deduplication, not just a longer grace period.

Suppose a payment-reconciliation worker has three jobs in flight. On SIGTERM, it pauses intake and records each job's state. Two finish and acknowledge. The third cannot finish before the deadline, so it must become safely retryable after its lease expires. If the external side effect is irreversible, use an idempotency key or ledger before claiming retries are safe. This is an original systems-design inference using common queue semantics; the exact broker acknowledgment behavior must be checked for the system in question.

Child processes complicate this. A parent worker that exits while its children keep running may leave untracked side effects. A parent that waits forever can be force-killed anyway. Record a bounded wait, a policy for incomplete children, and tests for the boundary just before the deadline. The candidate should state what is guaranteed and what is only best effort.

Question 5: How would you reproduce the failure rather than debate it?

Build a tiny disposable container with a request endpoint that sleeps for several seconds, a signal handler that timestamps events, and a process-tree printout. Start a request, invoke docker stop with a known timeout, and capture client outcome, container events, and logs. Repeat with shell-form and exec-form entrypoints. Do not infer signal forwarding from a single log line; show the server PID receiving the signal and active requests draining or failing.

Then vary one factor at a time: timeout, slow request duration, child process, streaming connection, load-balancer drain delay, or queue lease. A good test includes a request that ends just before the deadline and one that exceeds it. For each run, document whether the client completed, whether the process exited before force kill, and whether work was duplicated after restart. These observations turn a hand-wavy answer into an operational model.

If Kubernetes owns the container, add its Pod termination sequence and grace period to the experiment. Kubernetes can run a preStop hook, request endpoint removal, and ask the runtime to stop containers within the Pod's termination grace period. Do not assume Docker's default ten seconds applies. Read the Kubernetes Pod termination documentation for that environment and test the actual ingress and client behavior; endpoint propagation is not instantaneous by fiat.

Question 6: What should a two-minute answer include?

“I would inspect the image entrypoint and process tree first. docker stop signals the main process, then force-kills it after the configured grace period. A shell-form entrypoint may absorb the signal instead of forwarding it to the server. I would make the server receive the signal, stop new work, drain active requests with a bounded deadline, and test both client outcomes and exit events. For a queue worker I would also make incomplete work retry-safe, because termination can occur between a side effect and its acknowledgment.”

The answer names a mechanism, an observation, and the data-integrity limit. Avoid promising every request will finish, that SIGTERM reaches every child automatically, or that an orchestrator uses Docker's standalone defaults.

If the interviewer changes the stop timeout, recalculate the available drain window and explain which requests become retries rather than silently assuming completion.

Practice questionConnection to this scenario
Explain Linux Command Execution, Filesystems, and IsolationIdentify PID, child process, and signal boundaries.
Explain OS processes, threads, and memoryDescribe process exit and child cleanup.
Troubleshoot a production server outageConnect termination events to user impact.
Explain CI/CD, Kubernetes delivery, and troubleshootingTie a rollout to application drain behavior.
Discuss an Interesting Project and a Deployment MistakeExplain a verified shutdown lesson from your own work.

Continue with DevOps engineer interview questions, then run the signal experiment with one in-flight request and one background job.

Sources and further reading