Topic · Operations

Performance & scaling for Django in production

Finding what is slow, sizing servers and workers, and scaling out without surprises.

find the slow part before scalingone request, 800 msSQL queriesPython codequeueingafter the fixes, 150 msindexes, select_related, caching, right-sized workersIllustrative split: queries are usually the biggest slice

Most slow Django sites are slow in the database, then in Python, and only then because of too few servers. Measuring first saves a great deal of money and effort.

The guides cover profiling, query fixes, caching, worker sizing, compression, load testing, capacity planning, and scaling out when one server is no longer enough.

3Set it up
50Run it well
3Check it
20Fix it

Set it up

Guides that take this from nothing to running in production.

  • Set Up PgBouncer for Django

    Put PgBouncer between Django and PostgreSQL: transaction pooling, SCRAM auth, pool sizing, compatible Django settings, and monitoring.

  • Load Balance Django With HAProxy

    Put HAProxy in front of several Django app servers: active health checks on a /health/ endpoint, TLS termination, X-Forwarded-Proto, connection limits per server, rolling deploys with drain, and stick tables for basic rate limiting.

  • Serve a Django Site Through Fastly

    Put Fastly in front of Django: configure the origin and host header, cache anonymous pages with Surrogate-Control and surrogate keys, bypass logged-in users, purge by key from Django signals, and set shielding, TLS, and compression for static files.

Run it well

Tuning, reliability, and operations once it works.

  • How to Load Test a Django App Before Release

    Pre-release load testing answers a practical deployment question: will this Django release survive real traffic on your actual production stack.

  • Find and Fix N+1 Queries in Django

    Find and fix N+1 queries in Django: detect them in tests and logs, use select_related, prefetch_related, Prefetch objects, and annotations correctly.

  • Enable Gzip and Brotli Compression for Django with Nginx

    Compress Django responses and static files correctly: Nginx gzip settings, precompressed static assets, Brotli support, BREACH caveats, and checks.

  • Scale Django Horizontally Behind a Load Balancer

    Prepare Django to run on multiple servers behind a load balancer: shared sessions, media, caches, migrations, scheduled jobs, health checks, and deploys.

  • Size a Server for a Django App: Capacity Planning

    Estimate CPU, memory, and database capacity for Django: measure per-request costs, calculate worker counts, plan headroom, load test, and scale.

  • Profile Slow Django Endpoints in Production with py-spy

    Find why Django endpoints are slow in production: sample live Gunicorn workers with py-spy, record flame graphs, and dump stuck workers safely.

  • Find Slow Django Queries with pg_stat_statements

    Enable pg_stat_statements for a Django app, rank queries by total time, trace them back to ORM code, confirm plans with EXPLAIN ANALYZE, and fix them with indexes and better querysets.

  • Reduce Django Hosting Costs Without Losing Reliability

    Cut the cost of running Django in production: measure before resizing, right-size servers and databases, trim idle environments, control egress, NAT, logs, and backups, and commit only to steady usage.

  • Set Cache-Control Headers in Django for CDNs and Browsers

    Control how browsers and CDNs cache Django responses: cache_control and never_cache decorators, public versus private, max-age versus s-maxage, Vary headers from sessions and cookies, ETags, and safe defaults for logged-in pages.

  • Cache Django Pages at Cloudflare's Edge

    Cache Django HTML at Cloudflare’s edge safely: Cache Rules for anonymous pages, bypassing cache when session or CSRF cookies exist, respecting origin Cache-Control, purging on deploy or content change, and avoiding cached private pages.

  • Reduce Django Startup and Import Time

    Make Django processes start faster: measure import time with python -X importtime, defer heavy imports, avoid database queries at import, trim INSTALLED_APPS, and why startup matters for serverless, autoscaling, deploys, and management commands.

  • Export Large CSV Files From Django Without Timeouts

    Export large datasets from Django as CSV without timeouts or memory spikes: StreamingHttpResponse with iterator(), values_list to skip model instances, Nginx buffering, background exports to object storage for very large files, and Excel compatibility.

  • Use ETags and Conditional Requests in Django

    Cut bandwidth and server work with HTTP conditional requests in Django: ConditionalGetMiddleware, the condition, etag, and last_modified decorators, cheap ETag functions, 304 Not Modified responses for APIs, and how ETags interact with CDNs and compression.

  • Speed Up Django Template Rendering With the Cached Loader

    Make Django templates render faster in production: confirm the cached template loader is active, avoid disabling it with custom loaders or APP_DIRS mistakes, reduce expensive template work, use fragment caching, and measure template time per view.

  • Reduce Django Memory Usage per Worker

    Lower the memory footprint of Django processes: measure RSS per Gunicorn and Celery worker, trim imports and INSTALLED_APPS, stream large querysets and responses, recycle workers with max-requests, try jemalloc, and size worker counts to fit the server.

  • Deploy Django Across Multiple Regions

    Plan a multi-region Django deployment: decide between active-passive and active-active, route users with latency or geo DNS, place PostgreSQL primaries and read replicas, handle sessions, caches, Celery, and media per region, and test regional failover.

  • Continuous Profiling for Django With Pyroscope

    Add always-on continuous profiling to Django and Celery with Grafana Pyroscope: install the Python SDK or use eBPF, tag profiles by view and task, read flame graphs to find hot code paths, compare releases, and keep overhead low in production.

  • How to Optimize Gunicorn Workers for Django

    If you want to optimize Gunicorn workers for Django, the main production risk is getting concurrency wrong.

  • Django Caching Strategy for Production

    Choose what to cache in a Django app: HTTP caching, per-view and fragment caches, low-level caching, invalidation, stampede protection, and hit rates.

  • Configure Persistent Database Connections in Django

    Use CONN_MAX_AGE and CONN_HEALTH_CHECKS correctly: when persistent connections help, connection math, idle timeouts, ASGI caveats, and measuring gains.

  • Use Django's Built-In PostgreSQL Connection Pool

    Enable Django's native psycopg 3 connection pool: sizing per process, CONN_MAX_AGE rules, health checks, Gunicorn and ASGI behavior, and PgBouncer.

  • Build Smaller, Faster Django Docker Images

    Shrink and speed up Django Docker images: slim base images, multi-stage builds, layer ordering, BuildKit cache mounts, .dockerignore, and measurement.

  • Route Django Reads to PostgreSQL Read Replicas

    Add PostgreSQL read replicas to Django: multi-database settings, routers, replication lag and read-your-writes, transactions, migrations, and tests.

  • Tune Celery Worker Concurrency and Prefetch for Django

    Tune Celery workers for Django workloads: pool types, concurrency, prefetch multiplier, acks_late, time and memory limits, and dedicated queues.

  • Long-Lived Caching for Django Static Files

    Cache Django static files for a year without stale assets: manifest hashing, immutable Cache-Control headers in Nginx and WhiteNoise, CDNs, and deploy order.

  • Enable HTTP/2 and HTTP/3 for Django with Nginx

    Serve a Django site over HTTP/2 and HTTP/3 (QUIC) with Nginx: version-specific directives, Alt-Svc, UDP 443, keeping HTTP/1.1 to Gunicorn, verifying with curl, and what actually gets faster.

  • Set PostgreSQL Statement Timeouts for Django

    Stop runaway queries from exhausting Django workers: set statement_timeout per connection or role, give migrations and Celery different limits, handle QueryCanceled errors, and work with PgBouncer.

  • Tune PostgreSQL Autovacuum for Django

    Keep Django tables fast and small with autovacuum tuning: find dead tuples and bloat, set per-table thresholds for hot tables in migrations, raise cost limits, and remove blockers like long transactions.

  • Add Swap to a Small Django Server

    Add a swap file to a 1-2 GB Django server so pip installs, migrations, and traffic spikes do not trigger the OOM killer: sizing, fstab, swappiness, Docker and cloud caveats, and when swap is the wrong fix.

  • Speed Up Django With Template Fragment Caching

    Use Django template fragment caching in production: the cache tag with vary-on keys, per-user and per-language fragments, cached template loaders, invalidation with make_template_fragment_key, and avoiding stale or leaked content.

  • When Async Django Views Pay Off in Production

    Decide when async views improve a production Django app: concurrent external API calls, streaming, and long-polling benefit, while ORM-heavy views usually do not. Measure, avoid blocking calls, and run async code under ASGI correctly.

  • Speed Up Django REST Framework APIs

    Make Django REST Framework APIs faster in production: eliminate N+1 queries with select_related and prefetch_related, slim serializers, paginate everything, cache expensive endpoints, use values() for read-heavy lists, and measure with profiling.

  • Fast Pagination for Large Django Tables

    Paginate large Django tables without slow OFFSET and COUNT queries: keyset (cursor) pagination on an indexed column, DRF CursorPagination, avoiding exact counts, admin tips, and when page numbers are still fine.

  • Fast Bulk Inserts and Updates in Django

    Speed up large Django data loads and updates: bulk_create and bulk_update with batch_size, update() with F expressions, update_conflicts for upserts, transactions, disabling per-row signals, COPY for very large imports, and what bulk methods skip.

  • Tune PostgreSQL Settings for a Django Workload

    Tune PostgreSQL configuration for a Django app: shared_buffers, effective_cache_size, work_mem, max_connections with pooling, random_page_cost on SSDs, checkpoints and WAL, autovacuum, and statement timeouts, with safe starting values by server size.

  • Serve Optimised Images and Thumbnails From Django

    Serve fast, right-sized images from a Django app: generate thumbnails in background tasks, use WebP and AVIF, responsive srcset, CDN or image service resizing, cache headers for media, and avoid resizing images inside web requests.

  • Use Gunicorn preload_app With Django

    Decide whether to enable Gunicorn preload_app for Django: memory savings from copy-on-write, faster worker boot, fail-fast startup, the database and client connections you must close after fork, and why reloads need a full restart.

  • Tune Nginx Keepalive and Worker Connections for Django

    Tune Nginx for a Django backend: worker_processes and worker_connections, client keepalive_timeout, upstream keepalive connection pools to Gunicorn or Uvicorn, proxy buffering, and file descriptor limits, with when each setting matters.

  • Scale Django Channels WebSockets Across Servers

    Scale Django Channels beyond one process: the Redis channel layer and its capacity settings, running several Daphne or Uvicorn instances behind a load balancer, sticky sessions, connection limits, group fan-out costs, and graceful reconnects during deploys.

  • Autoscale Django on Kubernetes With the HPA

    Autoscale Django web pods on Kubernetes with the Horizontal Pod Autoscaler: accurate CPU requests, scaling on CPU or requests per second, min and max replicas, scale-down stabilisation, database connection limits, and testing under load.

  • Automatic ORM Query Caching in Django With Cachalot or Cacheops

    Cache Django ORM queries automatically with django-cachalot or django-cacheops: how invalidation works, when it speeds up read-heavy apps, which tables to exclude, multi-process and raw SQL pitfalls, and how to measure hit rates before trusting it in production.

  • Prevent Cache Stampedes in Django

    Stop cache stampedes (dogpile effects) in Django when a popular cache key expires and hundreds of requests recompute it at once: use locks, stale-while-revalidate, jittered timeouts, background refresh, and probabilistic early expiration.

  • A Practical Database Indexing Strategy for Django

    Decide which indexes a Django app needs: find slow queries, index foreign keys and filter and ordering columns, use composite, partial, covering, and expression indexes with Meta.indexes, remove unused indexes, and add them without locking production tables.

  • Use QuerySet.explain() and EXPLAIN ANALYZE to Speed Up Django Queries

    Read PostgreSQL query plans for Django ORM queries with QuerySet.explain(analyze=True, buffers=True): spot sequential scans, bad row estimates, sorts, and nested loops, run EXPLAIN safely on production, and turn plans into indexes and queryset fixes.

  • Index Django JSONField Queries With GIN on PostgreSQL

    Speed up Django JSONField lookups on PostgreSQL: choose GinIndex with jsonb_ops or jsonb_path_ops for containment queries, add expression B-tree indexes for specific keys, write queries that can use them, and build the indexes concurrently.

  • Delete Old Rows in Django in Batches Without Hurting Production

    Implement data retention in Django: delete old logs, task results, sessions, and events in small batches with primary key ranges, avoid long locks and replica lag, handle cascades, schedule cleanup with Celery or cron, and consider partitioning for big tables.

  • Auto Scale a Django Service on Amazon ECS

    Configure ECS service auto scaling for Django web and Celery services: target tracking on CPU or ALB requests per target, step scaling on SQS or custom queue metrics, sensible min and max tasks, cooldowns, database connection limits, and graceful shutdown.

  • Set Kubernetes CPU and Memory Requests and Limits for Django

    Size Kubernetes resources for Django web and Celery pods: measure real usage, set memory requests near the working set and limits above peaks, avoid CPU throttling with sensible CPU limits, match Gunicorn workers to CPU requests, and use VPA recommendations.

  • Manage PostgreSQL Connections for Django on Serverless Platforms

    Avoid connection exhaustion when Django runs on Lambda, Cloud Run, Vercel, or autoscaling containers: use pooled endpoints (RDS Proxy, Neon, Supabase pooler, PgBouncer), set CONN_MAX_AGE and server-side cursor options correctly, and cap concurrency per instance.

  • Speed Up Django JSON Responses With orjson

    Use orjson to make Django and Django REST Framework JSON serialization faster: an orjson renderer and parser for DRF, an ORJSONResponse for plain views, handling of datetimes, Decimals, and UUIDs, and when JSON encoding is actually your bottleneck.

Check it

Review lists to run before launch or after a change.

  • Django Performance Checklist Before Launch

    A pre-launch performance checklist for Django: query counts and indexes, caching, static and media delivery, Gunicorn and database sizing, background work, timeouts, and a load test against targets.

  • Django Traffic Spike Readiness Checklist

    Prepare a Django app for a launch, sale, TV mention, or viral post: load test the critical paths, cache anonymous pages at the edge, protect the database, scale workers and queues, add rate limits and graceful degradation, and staff the event.

  • Django Load Balancer Configuration Checklist

    A checklist for putting Django behind a load balancer: health checks that pass host validation, forwarded headers and SECURE_PROXY_SSL_HEADER, correct client IPs, idle and request timeouts, connection draining, sticky sessions avoided, and TLS policy.

Fix it

Errors and symptoms, each traced to a cause and a fix.

  • Fix a Slow First Request After a Django Deploy

    Fix slow first requests after a Django deploy or restart: measure cold versus warm, find heavy imports with -X importtime, precompile bytecode, preload, warm workers, and avoid cold starts.

  • Fix a Slow Django Admin on Large Tables

    Speed up Django admin changelists and forms on tables with millions of rows: avoid full counts, add list_select_related, use autocomplete and raw ID fields, index search and ordering, and limit filters.

  • Find and Fix a Django Memory Leak in Production

    Find and fix a Django memory leak in production: tell real leaks from normal Python growth, recycle Gunicorn workers, profile with tracemalloc, and fix DEBUG query logging, unbounded caches, and large querysets.

  • Fix High CPU Usage on a Django Server

    Fix high CPU usage on a Django server: find whether Gunicorn, PostgreSQL, Celery, or something else is burning CPU, profile hot endpoints with py-spy, fix N+1 queries and missing indexes, and block abusive traffic.

  • Gunicorn Worker Timeout in Django: Root Causes and Fixes

    A Gunicorn worker timeout in Django production usually means a worker process stopped responding before Gunicorn’s timeout limit expired.

  • Fix Django 504 Gateway Timeout Behind Nginx

    Diagnose Django 504 Gateway Timeout errors behind Nginx: find the slow layer, align proxy and Gunicorn timeouts, fix slow views, and offload long work.

  • Fix PostgreSQL "Too Many Connections" in Django

    Fix PostgreSQL sorry, too many clients already errors from Django: count connections by source, fix CONN_MAX_AGE and worker math, find idle leaks, and add pooling.

  • Fix Gunicorn Workers Killed by the OOM Killer

    Diagnose Gunicorn workers killed for running out of memory: OOM evidence, memory per worker, large querysets, leaks, max-requests, and right-sizing.

  • Fix Load Balancer Health Checks Failing for Django

    Fix load balancer health checks that mark healthy Django servers down: DisallowedHost 400s, HTTPS redirects, auth on health paths, and slow checks.

  • Fix Celery WorkerLostError in Django

    Fix Celery WorkerLostError 'Worker exited prematurely: signal 9 (SIGKILL)' in Django: find OOM kills and short stop timeouts, cap memory per child, chunk big tasks, and redeliver safely.

  • Fix Nginx 499 Client Closed Request with Django

    Understand and fix Nginx 499 status codes in front of Django: find the slow endpoints and impatient clients, align load balancer and proxy timeouts, and move long work to background tasks.

  • Fix Celery SoftTimeLimitExceeded and Hard Time Limits

    Fix Celery SoftTimeLimitExceeded and TimeLimitExceeded errors in Django: find why tasks run long, set soft and hard limits that fit the work, clean up on the soft limit, and split slow jobs into chunks.

  • Fix a Celery Worker Whose Memory Keeps Growing

    Fix Celery worker memory that grows until the OOM killer strikes: recycle children with max-tasks-per-child and max-memory-per-child, find the task that leaks, and avoid DEBUG query logging and large result sets.

  • Fix "Too many open files" on a Django Server

    Fix OSError Errno 24 Too many open files in Gunicorn, Celery, and Nginx: read the real per-process limit, find descriptor leaks with lsof, and raise LimitNOFILE, worker_rlimit_nofile, or Docker ulimits.

  • Fix Django Cache Not Working in Production

    Fix a Django cache that never hits in production: confirm the configured backend, avoid LocMemCache across processes, understand why Vary Cookie and CSRF break per-view caching, and check keys, versions, and timeouts.

  • Fix Cloudflare 524 Timeouts on Slow Django Requests

    Fix Cloudflare Error 524 a timeout occurred for slow Django views: understand the 100-second proxy limit, move exports and reports to Celery with polling, stream responses, and bypass the proxy for genuinely long admin tasks.

  • Fix Heroku H12 Request Timeout in Django

    Fix Heroku H12 Request timeout errors for Django: understand the 30-second router limit, find slow views, set Gunicorn timeouts below it, move long work to worker dynos with Celery or RQ, and avoid H13 cascades.

  • Fix "Canceling Statement Due to Conflict With Recovery" on Django Replicas

    Fix the PostgreSQL error canceling statement due to conflict with recovery when Django reads from a hot standby: understand vacuum conflicts, tune max_standby_streaming_delay and hot_standby_feedback, and route long reports to the right replica.

  • Fix Heroku R14 "Memory Quota Exceeded" for a Django App

    Fix Heroku R14 Memory quota exceeded and R15 errors for Django: measure memory per Gunicorn worker, set WEB_CONCURRENCY to fit the dyno, recycle workers with max-requests, find leaks in views and Celery, and choose a dyno size that matches.

  • Fix Nginx "worker_connections Are Not Enough" in Front of Django

    Fix the Nginx alert worker_connections are not enough while connecting to upstream for Django: count client and upstream connections, raise worker_connections and worker_rlimit_nofile, use upstream keepalive, and find slow responses that hold connections open.

Learning paths through this topic

Related topics