What are the Kubernetes container lifecycle hooks postStart and preStop, when does the kubelet run each, and what guarantees do they give?
answer
- only two hooks: postStart, preStop
- postStart races the entrypoint, blocks Running
- preStop before SIGTERM, eats the grace budget
- handlers: exec, httpGet, sleep - no TCP
- at-least-once; failures appear as events
basics
~20 sThey are per-container callbacks run by the kubelet: postStart fires right after container creation, concurrently with the entrypoint, and blocks the container from being Running until it returns; preStop fires just before SIGTERM on shutdown. Handlers are exec, httpGet or sleep, delivered at least once.
solid answer
~50 s`lifecycle.postStart` and `lifecycle.preStop` are declared per container and executed by the **kubelet**, not by the application. - **postStart** fires right after the container is created, but **concurrently with the entrypoint** - there is no guarantee it runs before or after your main process starts. The container is not considered Running or Ready until the hook returns, and if the hook fails the container is killed and the restartPolicy applies. That makes it a poor place for real initialisation (init containers are the proper tool). - **preStop** fires at the start of graceful shutdown, *before* SIGTERM, and its runtime counts against `terminationGracePeriodSeconds`. Typical use: sleep so endpoint removal can propagate, or deregister from an external registry. Handlers: `exec` (runs a command in the container, so the binary must exist in the image), `httpGet` (the kubelet calls the container), and `sleep` (native, no shell required). Failures surface only as Pod events, and hooks may be delivered more than once - make them idempotent.
code
yaml · 11 linescontainers:
- name: api
image: registry.example.com/api:2.3.1
lifecycle:
postStart:
httpGet:
path: /internal/registered
port: 8080
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]go deeper
Name the two hooks and when each fires: postStart after container creation, preStop before the termination signal.
Add the semantics that matter - postStart races the entrypoint and blocks the Running state, preStop consumes the grace budget - and list the exec, httpGet and sleep handlers.
Use preStop deliberately for connection draining, size it against terminationGracePeriodSeconds, and know that failures appear only as events and delivery is at-least-once.
Set the default hook and grace-period pattern in the platform chart, and rule on where each concern belongs: init containers for ordered setup, probes for health, hooks for boundary side effects.
## What hooks are A lifecycle hook is a callback the **kubelet** invokes around a container's life, declared inside the container spec under `lifecycle`. There are exactly two: `postStart` and `preStop`. They exist so you can attach behaviour to the container boundary without baking it into the image's entrypoint. ## postStart The kubelet fires `postStart` immediately after the container is created. Two properties define its semantics: - **It is concurrent with the entrypoint.** Kubernetes gives no ordering guarantee between the hook and the main process. Code that assumes the app is already listening - or that it has not started yet - is racy. - **It blocks the container's state.** The container is not transitioned to Running, and therefore never becomes Ready, until the hook returns. If the hook exits non-zero or cannot run, the kubelet kills the container and the Pod's `restartPolicy` decides what happens next. Because of that, postStart is *not* the place for setup that must complete before the app starts: use an **init container** for ordered, blocking preparation. Legitimate postStart uses are narrow - registering with an external system that must know the container exists, or writing a marker file. ## preStop `preStop` runs at the beginning of graceful termination, **before** the kubelet sends SIGTERM. The kubelet waits for it to return, or for the grace period to expire, and only then signals the process. Two rules follow: - Its duration comes out of `terminationGracePeriodSeconds`. A 20-second preStop under a 30-second grace leaves only 10 seconds for actual draining. - If the grace period expires while the hook is still running, the container is SIGKILLed regardless. The dominant real-world use is buying time: sleeping a few seconds so removal from the Service's EndpointSlices propagates to every proxy before the app stops accepting connections. Other uses include deregistering from an external load balancer or checkpointing state. Note that preStop runs on **graceful** termination only - deletion, eviction, rolling update, drain. It does not run when a node dies abruptly, and it does not run for a container that crashes on its own. ## Handler types - **`exec`** - runs a command inside the container, sharing its filesystem and namespaces. The binary must exist in the image, which is a trap on distroless or scratch images with no shell. - **`httpGet`** - the kubelet makes an HTTP request to the container. Useful when the app exposes a prepare/shutdown endpoint, and it needs nothing extra in the image. - **`sleep`** - a native pause with no process spawned; generally available since Kubernetes 1.30 and the cleanest way to express a drain delay in minimal images. There is no TCP handler, unlike probes. ## Guarantees and failure behaviour - Delivery is **at least once**: a hook may fire more than once for a container, so handlers must be idempotent. - Hook failures do not produce nice status fields. They surface as Pod **events** (`FailedPostStartHook`, `FailedPreStopHook`), which is why a mysterious failure can be invisible in logs. - Hook output is not captured in container logs; log from inside the app or write to a mounted volume if you need forensics. - Hooks are per container. In a multi-container Pod each container's preStop runs during that container's shutdown. ## Choosing the right tool Interviewers usually probe whether you know the boundaries: ordered pre-start work belongs in an **init container**; "is the app up yet?" belongs in a **probe**; "do something at the container boundary" belongs in a **hook**. Using postStart as a pseudo-init container, or a very long preStop instead of raising the grace period, are the two classic misuses.
- Why is postStart a bad substitute for an init container?postStart runs concurrently with the entrypoint, so it cannot guarantee that preparation finishes before the application starts using it. Init containers run to completion, in order, before any app container starts, which is exactly the guarantee setup work needs.
- A preStop hook using exec with /bin/sh fails on a distroless image. What do you do?Distroless and scratch images have no shell, so the exec handler cannot run. Use the native sleep action, or an httpGet handler against an endpoint the app already exposes, or ship a static binary in the image for the hook to invoke.
saying these in an interview costs you the question
- Claiming postStart is guaranteed to run before the container's entrypoint
- Thinking preStop runs after SIGTERM, or that it gets time beyond the grace period
- Expecting hook failures to appear in container logs rather than Pod events
- Assuming preStop runs when a node crashes or when the container itself crashes
- Believing hooks support a tcpSocket handler like probes do