What Are Container Lifecycle Hooks in Kubernetes?

intermediate|podsdevopssrebackend developerCKACKAD
TL;DR

Kubernetes provides two container lifecycle hooks — PostStart and PreStop — that let you execute custom logic immediately after a container starts and just before it terminates.

Detailed Answer

Kubernetes exposes two lifecycle hooks on every container: PostStart and PreStop. These hooks allow you to run custom logic at critical points in a container's lifecycle without modifying the application image.

PostStart Hook

The PostStart hook fires immediately after a container is created. It runs asynchronously — there is no guarantee it completes before the container's entrypoint starts. If the PostStart hook fails, the container is killed and subject to the Pod's restart policy.

Common use cases:

  • Registering the instance with an external service registry
  • Writing a startup marker file for other containers in the Pod
  • Warming up caches or preloading data
apiVersion: v1
kind: Pod
metadata:
  name: app-with-hooks
spec:
  containers:
    - name: app
      image: myapp:2.1
      lifecycle:
        postStart:
          exec:
            command:
              - "/bin/sh"
              - "-c"
              - "echo 'Container started' > /tmp/started"
      resources:
        requests:
          cpu: "100m"
          memory: "128Mi"

PreStop Hook

The PreStop hook fires when Kubernetes decides to terminate a container — due to a scaling event, rolling update, node drain, or Pod deletion. The hook runs before SIGTERM is sent to the container process.

This is the most commonly used hook because it enables graceful shutdown:

apiVersion: v1
kind: Pod
metadata:
  name: web-server
spec:
  terminationGracePeriodSeconds: 60
  containers:
    - name: nginx
      image: nginx:1.27
      lifecycle:
        preStop:
          httpGet:
            path: /shutdown
            port: 8080
      resources:
        requests:
          cpu: "100m"
          memory: "128Mi"

The Sleep Handler (v1.29+)

Kubernetes 1.29 introduced a native sleep handler, eliminating the need for exec with sleep commands:

lifecycle:
  preStop:
    sleep:
      seconds: 15

This is cleaner and avoids requiring a shell in the container image.

Hook Handler Types

| Handler | Description | |---------|-------------| | exec | Runs a command inside the container | | httpGet | Sends an HTTP GET to a specified endpoint | | sleep | Pauses for a specified duration (v1.29+) |

Termination Sequence in Detail

Understanding the full termination sequence is critical:

  1. Pod is marked for deletion (status becomes Terminating)
  2. Pod is removed from Service endpoints (kube-proxy updates iptables/IPVS rules)
  3. PreStop hook executes (if defined)
  4. SIGTERM is sent to the container's PID 1
  5. Container has until terminationGracePeriodSeconds expires
  6. SIGKILL is sent if the container is still running

Steps 2 and 3 happen concurrently. This is why a short PreStop sleep (5-15 seconds) is a common pattern — it gives kube-proxy time to update routing rules before the container stops accepting connections.

A Production-Ready PreStop Pattern

apiVersion: apps/v1
kind: Deployment
metadata:
  name: api-server
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  template:
    metadata:
      labels:
        app: api
    spec:
      terminationGracePeriodSeconds: 45
      containers:
        - name: api
          image: myapi:3.0
          ports:
            - containerPort: 8080
          lifecycle:
            preStop:
              exec:
                command:
                  - "/bin/sh"
                  - "-c"
                  - "sleep 10 && curl -X POST http://localhost:8080/admin/drain"
          resources:
            requests:
              cpu: "250m"
              memory: "256Mi"
            limits:
              cpu: "500m"
              memory: "512Mi"

This PreStop hook waits 10 seconds for routing rules to propagate, then tells the application to drain in-flight requests.

Important Caveats

  1. Hook delivery is at-least-once: In rare cases, a hook may be called multiple times. Make your hook handlers idempotent.
  2. No logs by default: Hook output is not captured in kubectl logs. Use kubectl describe pod to see hook-related events.
  3. Resource sharing: Hooks run inside the container's resource limits. A CPU-intensive hook can starve the main process.
  4. PostStart blocks readiness: The container will not become Ready until the PostStart hook completes, even though it runs asynchronously relative to the entrypoint.

Debugging Hooks

# Check for hook-related events
kubectl describe pod app-with-hooks

# Look for FailedPostStartHook or FailedPreStopHook events
kubectl get events --field-selector involvedObject.name=app-with-hooks

Why Interviewers Ask This

Understanding lifecycle hooks shows you can implement graceful startup and shutdown patterns, which is essential for zero-downtime deployments and data integrity.

Common Follow-Up Questions

Is PostStart guaranteed to run before the container's entrypoint?
No. PostStart runs asynchronously alongside the container's entrypoint. There is no guarantee of ordering between them.
What happens if a PreStop hook takes longer than terminationGracePeriodSeconds?
Kubernetes sends SIGKILL after the grace period expires, regardless of whether the PreStop hook has completed.
Can lifecycle hooks use all three handler types?
Yes — hooks support exec (run a command), httpGet (call an HTTP endpoint), and sleep (pause for a duration, added in v1.29).

Key Takeaways

  • PostStart runs alongside the container entrypoint — use it for initialization that does not block startup.
  • PreStop runs before SIGTERM is sent and is ideal for draining connections and deregistering from service discovery.
  • If a PostStart hook fails, the container is killed and restarted according to the Pod's restart policy.

Related Questions

You Might Also Like