Guides / [CRITICAL] WORKER TIMEOUT in gunicorn (Django)

[CRITICAL] WORKER TIMEOUT in gunicorn (Django)

gunicorn logs [CRITICAL] WORKER TIMEOUT (pid:42) and a request fails with a 500, 502 or 504. A worker didn't report back within 30 seconds (the default), so gunicorn killed it. The timeout is a symptom. Find what made the request slow.

[2026-01-12 10:31:07 +0000] [1] [CRITICAL] WORKER TIMEOUT (pid:42)
[2026-01-12 10:31:08 +0000] [1] [ERROR] Worker (pid:42) was sent SIGKILL! Perhaps out of memory?

What it means

With the default synchronous workers, each worker handles one request at a time and signals to gunicorn that it is alive. If a request keeps the worker busy longer than --timeout, gunicorn assumes it is stuck and kills it. The "Perhaps out of memory?" line is a guess; it's printed for every kill.

Why it happens

  1. An outgoing call with no timeout. requests.get(url) waits forever for a slow or dead API, payment provider or email server.
  2. A slow database query, often a missing index, or a lock held by another transaction.
  3. Too much work in one request: a big export, image processing, a large file upload or an import.
  4. Too few workers for the traffic, so requests queue behind slow ones.
  5. A starved server. Not enough CPU or memory, so everything is slow, or the process really did run out of memory.
  6. Docker's worker heartbeat file is on a slow disk (gunicorn writes to a temp file, which on some container filesystems blocks).

The fix

1. Find the slow request

Turn on logging of access and errors to stdout, with timing:

gunicorn myproject.wsgi:application \
  --access-logfile - --error-logfile - --capture-output \
  --access-logformat '%(h)s "%(r)s" %(s)s %(L)ss'

%(L)s is the request time in seconds. The path that repeats before each timeout is your culprit.

2. Always set timeouts on outgoing calls

response = requests.get(url, timeout=(3.05, 10))  # connect, read

For the database, add a statement timeout so one bad query can't hold a worker: DATABASES["default"]["OPTIONS"] = {"options": "-c statement_timeout=15000"} (milliseconds, Postgres).

3. Give gunicorn the right shape

gunicorn myproject.wsgi:application \
  --workers 3 --worker-class gthread --threads 4 \
  --timeout 60 --worker-tmp-dir /dev/shm

4. Take long work out of the request

If a view legitimately takes a minute, don't make the browser wait for it. Break it up, run it as a separate process, or stream the response. Timeouts of a few seconds on a web request are a feature.

Making it stick in production

Or skip it

Django Cloud lets you pick a server size from Nano to XLarge, run 1 to 20 instances and scale from the dashboard or with djangocloud scale. CPU and memory charts and alerts show when you need more.

pip install djangocloud-cli
djangocloud deploy

Get startedSee pricing

Free plans are available for students, AWS Activate startups, open-source, non-profit and research projects. See how Django Cloud compares with other ways to host Django.

Django is a registered trademark of the Django Software Foundation. Django Cloud is not affiliated with or endorsed by it. Found a mistake in this guide? Tell us and we will fix it.