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.
Problem statement
Performance problems found after launch are expensive: they appear under real traffic, often at the worst moment (a campaign, a press mention), and fixing them means emergency changes to a live system. Most of them are predictable from a short review before launch: pages with hundreds of queries, missing indexes, static files served by Django, no caching for anonymous traffic, and timeouts that let one slow request take down the site.
This checklist covers what to verify before going live, in the order that usually gives the biggest wins. Each item has a concrete check, so it can be done by someone other than the original developer.
- Define targets: p95 latency for key pages (for example under 500 ms server time) and the peak requests per second you must handle.
- Check query counts and slow queries on the key pages with production-sized data.
- Cache what anonymous users see and what is expensive to compute.
- Serve static files with long cache lifetimes and compression; keep media off Django workers.
- Size Gunicorn workers, database connections, and Celery concurrency to the server.
- Set timeouts at every layer and move slow work to background tasks.
- Load test the critical paths and fix what fails before launch day.
Checklist
1. Targets and key pages
- Latency targets are written down for the five to ten most important pages and API endpoints (home, product list, product detail, search, login, checkout, main API calls).
- Expected peak traffic is estimated (from analytics, marketing plans, or a comparable site), with a safety factor.
- A dataset of realistic size exists for testing: production copy (sanitised) or generated data with similar row counts.
2. Database queries
- Each key page runs a bounded number of queries that does not grow with list length (check with Django Debug Toolbar or
assertNumQueries). select_relatedandprefetch_relatedare used for relations shown in lists.- Filters, ordering, and joins on large tables are backed by indexes;
EXPLAIN ANALYZEshows index scans for key queries. - List views and APIs are paginated; no view loads an unbounded queryset.
count()on large tables is avoided where not needed (admin changelists included).- Slow query logging (
log_min_duration_statement) andpg_stat_statementsare enabled for after-launch analysis.
3. Caching
- A shared cache (Redis or Memcached) is configured;
LocMemCacheis not used across multiple processes where consistency matters. - Pages for anonymous users are cached (per-view cache, template fragment cache, or a CDN or Nginx cache) where content allows.
- Expensive computed data (menus, counts, aggregates) is cached with explicit timeouts and invalidation.
- Sessions use a fast backend (
cached_dbor cache) if session reads are frequent. - The cache is not a single point of failure: pages still work (slower) if Redis is unavailable.
4. Static and media delivery
collectstaticruns in the build; static files use hashed filenames (manifest storage) andCache-Controlof a year.- Static files are served by WhiteNoise, Nginx, or a CDN, never by Django's development static view.
- Gzip or Brotli compression is enabled for text assets and HTML.
- Images are sized appropriately (thumbnails for lists, modern formats where possible); no multi-megabyte originals on list pages.
- Media files are served by Nginx, object storage, or a CDN; private files use
X-Accel-Redirector signed URLs.
5. Application server and capacity
- Gunicorn worker count fits the server's CPU and memory (measure per-worker memory; leave room for the database and cache if colocated).
--max-requestswith jitter is set to contain slow memory growth.- Database
max_connectionscovers all workers, Celery processes, and tools (or a pooler such as PgBouncer is in place). CONN_MAX_AGE(or Django's connection pool) is configured to avoid reconnecting per request.- Server resources have headroom at expected peak (CPU under about 70 percent, memory without swapping).
6. Timeouts and slow work
- Timeouts are aligned: client or load balancer > Nginx
proxy_read_timeout> Gunicorn--timeout> databasestatement_timeout. - Every outbound HTTP call (payment, email, APIs) has an explicit timeout.
- Emails, exports, imports, image processing, and third-party syncs run in background tasks, not in requests.
- Celery (or another worker) is sized and routed so urgent tasks are not stuck behind slow ones.
7. Frontend weight
- Key pages have been checked with Lighthouse or WebPageTest on a mobile profile.
- JavaScript and CSS are bundled and minified; unused libraries are removed.
- Third-party scripts (analytics, chat widgets) are loaded asynchronously and their impact measured.
8. Load test
- A load test covers the critical user journeys with realistic think times and data, against a production-like environment.
- The system meets latency targets at expected peak and degrades gracefully (slower, not failing) at 2× peak.
- Bottlenecks found (database CPU, worker saturation, a slow endpoint) are fixed and retested.
- Monitoring during the test shows where limits are reached, so the same dashboards can be used on launch day.
Explanation
Django performance problems in production cluster in a few places: too many or too slow queries, work done in the request that should be cached or deferred, assets served inefficiently, and capacity that was never measured. Checking these with production-sized data and concrete targets turns vague confidence into evidence. The load test ties everything together: it verifies that the parts work at the traffic level you expect, and shows which limit you will hit first as traffic grows.
Edge cases / notes
Launching with a CDN
A CDN in front of anonymous pages can absorb most read traffic, but only if cache headers allow it. Check Cache-Control on cacheable pages and make sure personalised pages are never cached publicly.
Admin and internal tools
Staff tools often run heavy queries on large tables. Include the admin changelists your team will use daily in the review.
When to automate this
Add query count assertions for key views to the test suite, a Lighthouse check in CI for key pages, and a scheduled load test before major releases. That keeps the launch-level performance from eroding release by release.
Next steps
- Run the final verification with How to Load Test a Django App Before Release.
- Fix query explosions with Find and Fix N+1 Queries in Django.
- Plan caching layers with Django Caching Strategy for Production.
- Cover non-performance items with the Django Deployment Checklist for Production.
FAQ
What should I check first for Django performance before launch?
Query counts and slow queries on key pages with production-sized data. They are the most common and most impactful problems.
How many Gunicorn workers should I use at launch?
As many as fit in memory and CPU with headroom, based on measured per-worker memory. Load test to confirm.
Do I need a CDN for a Django launch?
Not always. Long-lived cached static files and compression are the essentials. A CDN helps with global audiences and heavy anonymous traffic.
How realistic does the load test need to be?
It should cover critical user journeys with realistic data size and request mix. A test hammering one cached page proves little.