Your Django app is getting slower every week. Queries that once took milliseconds now crawl. You check the indexes, tune the ORM queries, add select_related — nothing helps. The culprit is table bloat: PostgreSQL's MVCC architecture means every UPDATE and DELETE leaves behind dead row versions, and autovacuum's default settings are wildly insufficient for write-heavy Django workloads. In production systems, we've seen tables reach 97% dead tuples — PostgreSQL scanning through gigabytes of invisible garbage on every query while the logs showed nothing wrong.
The mystery (5 min). We open with a real production incident: a Django SaaS platform where response times degraded over weeks despite no code changes. The detective work that led us from slow queries to pg_stat_user_tables revealing 97%+ dead tuple ratios across core tables.
How Django creates bloat (8 min). Most Django developers never learn how PostgreSQL's MVCC works under the hood. Every UPDATE creates a new row version and marks the old one as dead. Every DELETE just marks rows invisible. Common Django ORM patterns — bulk status updates, soft deletes, frequent save() calls on hot models — accelerate bloat dramatically. We'll trace exactly what happens at the storage level when your Django code runs Model.objects.filter(status='pending').update(status='processing').
Diagnosis (5 min). The exact queries using pg_stat_user_tables and pgstattuple that reveal whether your database is healthy or heading toward disaster. How to read n_dead_tup, last_autovacuum, and n_mod_since_analyze to catch problems early. Why autovacuum was running but not keeping up.
Emergency and preventive fixes (8 min). Tuning autovacuum_vacuum_scale_factor and autovacuum_vacuum_threshold specifically for Django workload patterns. The difference between VACUUM and VACUUM FULL — and why VACUUM FULL locks your table. Using pg_repack for zero-downtime table reorganization. The loose index scan technique for dramatically speeding up queries on high-cardinality columns. How EXPLAIN (ANALYZE, BUFFERS) reveals what EXPLAIN alone hides.
Monitoring checklist (4 min). The five PostgreSQL metrics every Django developer should track, alert thresholds to catch bloat early, and an autovacuum configuration template tuned for typical Django applications.