The problem: connections eat RAM before any work happens

  • A Postgres connection costs roughly 1.3 MB of memory (2:23).
  • Instagram's Django servers created new connections constantly — every Django process kept its own pool to the database (2:23).
  • The arithmetic that makes it concrete (2:23):
QuantityValue
Application servers50
Connections each30
Total connections1,500
Memory per connection1.3 MB
RAM consumed≈ 2 GB
  • That 2 GB is spent before the database does a single useful thing — it just sits there holding connections and eating RAM (2:52).

The fix: PgBouncer

  • PgBouncer is a lightweight proxy that sits between the application and Postgres (2:52).
  • The app thinks it's talking to a database; it's actually talking to PgBouncer (2:52).
  • PgBouncer multiplexes many incoming app connections onto a much smaller pool of real Postgres connections (2:52).
flowchart LR
    subgraph before["Without pooling"]
        A1[50 app servers<br/>× 30 conns] -->|1,500 conns ≈ 2 GB RAM| P1[(Postgres)]
    end
    subgraph after["With PgBouncer"]
        A2[50 app servers<br/>× 30 conns] --> B[PgBouncer proxy]
        B -->|~30 real conns| P2[(Postgres)]
    end

The result

  • 1,500 connections eating 2 GB become ~30 real connections handling all the traffic (3:16).
  • The freed memory goes back to what the database should be doing: caching pages, sorting query results, planning queries (3:16).

Why this matters generally

  • This is the first invisible bottleneck almost every Postgres deployment hits, and most teams don't add a pooler until they're already in pain (3:16).
  • Actionable rule stated twice in the video: if you run Postgres at meaningful scale without PgBouncer or an equivalent, that is the single highest-leverage thing you can fix this week (3:42, repeated at 21:23).