Python Graceful Shutdown: Handling SIGTERM Correctly

When Kubernetes stops a pod it sends SIGTERM first - if your Python worker ignores it, the runtime eventually sends SIGKILL and whatever job was in flight is lost. A one-time signal handler fixes it.

DevOps Engineerpythonflasksignals

What SIGTERM means in Kubernetes

When you roll out a new version or scale down a deployment, Kubernetes sends SIGTERM to each pod's main process and waits up to terminationGracePeriodSeconds (default 30 s) for the process to exit on its own. Only after that deadline does it send SIGKILL. A worker that ignores SIGTERM runs until the hard kill - any job it was processing is dropped mid-flight.

The broken pattern

# worker.py - BUG: no SIGTERM handler
import time, os

RUNNING = True   # never set to False

def process_job(job_id):
    print(f"processing job {job_id}")
    time.sleep(5)   # simulates real work
    print(f"done job {job_id}")

while RUNNING:
    job = fetch_next_job()
    process_job(job)

Send SIGTERM to this process and it keeps running. The only way to stop it is SIGKILL, which gives it no chance to finish the current job.

The fix: register a SIGTERM handler

import signal, time, os

RUNNING = True

def _on_sigterm(signum, frame):
    global RUNNING
    print("SIGTERM received - finishing current job, then exiting", flush=True)
    RUNNING = False

signal.signal(signal.SIGTERM, _on_sigterm)

def process_job(job_id):
    print(f"processing job {job_id}")
    time.sleep(5)
    print(f"done job {job_id}")

while RUNNING:
    job = fetch_next_job()
    process_job(job)   # current job runs to completion
    # RUNNING is checked here, between jobs - not mid-job

The handler sets RUNNING = False immediately when the signal arrives, but the while RUNNING: check only fires between jobs, so the in-flight job always finishes. New jobs are not started after the flag flips.

Verify it works

python3 worker.py &
PID=$!
sleep 2
kill -TERM $PID          # send SIGTERM
# worker logs the message, finishes current job, exits with code 0
wait $PID && echo "clean exit"

Compare that to the old behavior where ps -p $PID still showed the process alive after kill -TERM and kill -9 was the only escape.

Production additions

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

Why does Python ignore SIGTERM by default?

Python does not ignore SIGTERM - it uses the default OS action, which is to terminate the process immediately. A worker loop that doesn't register a handler gets killed mid-job with no chance to finish. Registering a handler with signal.signal() gives you control over when the exit happens.

How do I handle SIGTERM in a Python worker loop?

Set a module-level RUNNING flag to True, register a signal.signal(signal.SIGTERM, handler) where the handler sets RUNNING = False, and check RUNNING at the top of the loop. The current job finishes; the loop exits cleanly on the next iteration.

How does Kubernetes stop a pod gracefully?

Kubernetes sends SIGTERM to the container's PID 1 and waits terminationGracePeriodSeconds (default 30 s). If the process exits before the deadline, Kubernetes calls it a clean stop. If not, it sends SIGKILL - so your process must react to SIGTERM and exit within that window.

Keep learning

Fix the Nginx 502 Bad GatewayDevOps projectRecover a Crashed Linux ServiceDevOps projectCorrect a Postgres Port MismatchDevOps projectDevOps roadmapStep by step to hiredDevOps interview questionsSTAR answersAll DevOps projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →