Architecture and operations
David Sterling  

How OpenAI Scales PostgreSQL: 5 Ops Lessons

OpenAI’s engineering team recently published “Scaling PostgreSQL to power 800 million ChatGPT users” — a detailed account of how ChatGPT’s primary data store survives traffic that would flatten most databases. The headline: it’s one PostgreSQL primary handling every write, roughly 50 read replicas, and a stack of operational habits that keep the write path alive. For anyone running Postgres in production — including the energy, logistics, and healthcare systems we work with around Houston — the post is a practical playbook for capacity planning, replication, and high availability. Here are the five lessons that matter most.

The Architecture: One Primary, ~50 Replicas

OpenAI runs ChatGPT’s core data on a single-primary PostgreSQL architecture: one Azure PostgreSQL Flexible Server instance handles all writes, while roughly 50 replicas serve reads. That sounds almost too simple for a product serving 800 million users with millions of queries per second — and that is exactly the point. The engineering is in the discipline around that one primary, not in exotic distributed machinery. ChatGPT’s demand for PostgreSQL capacity grew more than tenfold in three years, and OpenAI scaled by protecting the primary instead of replacing it.

Lesson 1 — Protect the Write Path

The failure mode OpenAI describes will be familiar to anyone who has run a busy OLTP system: a write spike (a cache miss, an expensive query, a popular new feature) overloads the primary. Requests slow down, users retry, retries pile on more writes, and the primary tips into an outage. Their countermeasures, in order of impact:

  • Route every read to replicas. The primary only ever sees writes, which dramatically cuts its CPU load.
  • Defer non-urgent writes. OpenAI deliberately processes some writes lazily, smoothing spikes instead of absorbing them all at once.
  • Shard what can be sharded — elsewhere. Workloads that tolerated sharding moved to a shardable store (Cosmos DB), because sharding the PostgreSQL primary itself would have meant touching hundreds of applications and months of migration.

Takeaway: before you reach for sharding, exhaust load reduction. Most write-path crises are solved with replicas, timeouts, and query hygiene, not a new architecture.

Lesson 2 — Treat Queries Like Capacity Planning

OpenAI found 12-table joins running against an OLTP database, ORM-generated SQL with N+1 problems, and sessions stuck idle in transaction. Three fixes carried most of the win:

  • Separate OLTP from OLAP. Analytical queries belong on a reporting replica or warehouse, never on the transactional primary.
  • Audit the SQL your ORM emits. Don’t trust the abstraction — read the generated queries and fix the ones that scan.
  • Kill stuck sessions automatically with idle_in_transaction_session_timeout:
ALTER SYSTEM SET idle_in_transaction_session_timeout = '60s';
SELECT pg_reload_conf();

That one setting — documented in the PostgreSQL runtime configuration docs — prevents a single abandoned transaction from holding locks and bloating the primary indefinitely.

Lesson 3 — A Hot Standby Is Not Optional

A single primary is a single point of failure, so OpenAI runs a continuously-synced hot standby that takes over if the primary dies. This is standard Patroni territory — for a two-node cluster the config is short:

bootstrap:
  dcs:
    ttl: 30
    loop_wait: 10
    retry_timeout: 10
    postgresql:
      use_pg_rewind: true
      parameters:
        max_connections: 500
        hot_standby: "on"
        max_standby_streaming_delay: 30s

If you’re not on Patroni yet, a streaming standby built with pg_basebackup still beats nothing:

pg_basebackup -h primary.internal -D /var/lib/postgresql/16/main \
  -U replicator -X stream -P -R

The -R flag writes the standby settings for you. Whatever tool you choose, test the failover — a standby that has never actually taken over will fail the one time you need it.

Lesson 4 — Know When Sharding Is (and Isn’t) the Answer

OpenAI explicitly considered sharding the primary and rejected it — not because sharding is wrong, but because the migration cost was too high relative to the load-reduction wins available. For most companies the decision is the same: sharding is a last resort, not a first move. Declarative table partitioning (or pg_partman for time-series data) delivers most of the operational benefit — smaller indexes, faster archive, easier vacuum — at a fraction of the cost of splitting one logical database into many.

Lesson 5 — Measure Before You Scale

Every decision above starts with visibility. If you can’t see replica lag, idle-in-transaction sessions, or cache-miss storms, you’re guessing. Two queries worth running daily:

SELECT pid, state,
       now() - xact_start AS txn_age,
       left(query, 80) AS query
FROM pg_stat_activity
WHERE state = 'idle in transaction'
ORDER BY txn_age DESC;

SELECT client_addr, state,
       pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(),
                                      replay_lsn)) AS lag
FROM pg_stat_replication;

The first finds the stuck sessions that poison the primary; the second shows how far each replica is falling behind. Set alerts on both before you need them.

What This Means for Your Stack

None of this requires OpenAI’s scale. A working checklist for any Postgres deployment:

  • Reads on replicas, writes on primary — enforced at the connection layer, not by convention
  • idle_in_transaction_session_timeout set and verified
  • OLAP queries moved off the transactional primary
  • Hot standby configured, and failover actually tested
  • Replica lag and stuck sessions monitored with alerts
  • A capacity plan that assumes 10x growth, not 10%

The full OpenAI post is worth reading — VentureBeat’s summary covers the highlights if you’re short on time. The boring stuff — timeouts, standbys, monitoring — is what lets a single PostgreSQL primary carry 800 million users.

Leave A Comment