Prompt
How do I scale PostgreSQL when my app starts getting more traffic?
Latest observation
When PostgreSQL starts getting more traffic, scaling usually means combining better use of the current database with read scaling, write optimization, and eventually architecture changes. A good approach is to scale in stages so you don’t add complexity too early.
1. First, measure what’s actually slow
Before changing architecture, find the bottleneck:
- Slow queries
- Missing indexes
- Too many connections
- High lock contention
- CPU saturation
- Disk I/O bottlenecks
- Autovacuum lag / table bloat
Useful tools:
EXPLAIN (ANALYZE, BUFFERS)pg_stat_statementspg_stat_activitypg_locks- Monitoring for CPU, memory, IOPS, cache hit rate
Often, the biggest “scaling” win is fixing a few bad queries.
2. Optimize the database you already have
These improvements usually come first:
Indexing
- Add indexes for frequent
WHERE,JOIN,ORDER BY, andGROUP BYcolumns - Use composite indexes when queries filter on multiple columns
- Avoid over-indexing because writes get slower
Query tuning
- Select only needed columns
- Avoid N+1 queries from the application
- Prefer set-based operations over many small queries
- Paginate correctly; avoid large
OFFSETs for deep pages
Schema design
- Normalize where it helps consistency
- Denormalize selectively for heavy read paths
- Use appropriate data types
- Consider partitioning very large tables
Maintenance
- Make sure autovacuum is healthy
- Reindex or vacuum if bloat becomes severe
- Keep statistics updated with
ANALYZE
3. Scale vertically first
If the database is simply outgrowing current hardware, vertical scaling is the simplest next step:
- More CPU
- More RAM
- Faster storage (NVMe / SSD)
- Better network if DB is remote
More RAM often helps a lot because PostgreSQL benefits from a larger cache.
This is usually the easiest scaling method until you hit cost or hardware limits.
4. Use connection pooling
PostgreSQL doesn’t love huge numbers of client connections.
If your app opens many connections:
- Add a pooler like PgBouncer
- Keep a sensible max connection count
- Prefer short-lived transactions
This can dramatically improve throughput and reduce overhead.
5. Scale reads with replicas
If your workload is mostly reads, add read replicas:
- Primary handles writes
- Replicas handle SELECT-heavy traffic
- Route read-only queries to replicas
This is one of the most common scaling strategies.
Things to watch:
- Replication lag
- Eventual consistency
- Read-after-write behavior in the app
Use replicas for:
- Reporting
- Analytics
- Search-heavy read endpoints
- Public content reads
6. Reduce write pressure
If writes are the bottleneck:
- Batch inserts/updates instead of doing one row at a time
- Use async processing for non-critical writes
- Reduce transaction length
- Avoid unnecessary updates to unchanged rows
- Minimize indexes on high-write tables
Also consider:
- Moving logging/event data to append-only tables
- Using partitioning for high-ingest tables
7. Partition large tables
Partitioning helps when tables become very large or write-heavy.
Good use cases:
- Time-series/event data
- Logs
- Multi-tenant data
- Tables where queries usually hit a subset of rows
Benefits:
- Faster pruning of irrelevant data
- Easier archival/deletion
- Smaller indexes per partition
But it adds complexity, so don’t use it unless it solves a real problem.
8. Separate workloads
Sometimes one PostgreSQL instance is serving very different workloads:
- OLTP app traffic
- Reporting queries
- Background jobs
- Analytics
These can interfere with each other.
You can improve things by:
- Sending analytics/reporting to replicas
- Moving search to a dedicated search engine
- Offloading jobs to queues
- Splitting hot tables from cold data
9. Shard when one database is no longer enough
If you outgrow a single primary database, sharding may be needed.
Sharding means splitting data across multiple PostgreSQL instances, usually by:
- Tenant
- User ID range
- Geographic region
- Hash partitioning
Pros:
- Scales writes and storage horizontally
- Limits blast radius
Cons:
- Harder queries across shards
- More complex transactions
- More application logic
- Operational complexity
Sharding is usually a later-stage move, not the first solution.
10. Consider managed Postgres
If you’re running your own PostgreSQL and scaling is becoming operationally painful, managed services can help:
- AWS RDS / Aurora PostgreSQL
- Google Cloud SQL
- Azure Database for PostgreSQL
- Supabase, Neon, Crunchy, etc.
These can simplify:
- Backups
- Replication
- Failover
- Storage scaling
- Monitoring
Practical scaling path
A common progression looks like this:
- Fix queries and add indexes
- Use connection pooling
- Increase hardware
- Add read replicas
- Partition hot/large tables
- Split workloads
- Shard if necessary
Signs you need to scale PostgreSQL
You’ll usually notice:
- Query latency rising under load
- CPU pegged on the DB server
- Disk I/O wait times increasing
- Connection exhaustion
- Frequent lock waits
- Replica lag growing
- Vacuum not keeping up
- Large tables becoming slow to query
Rule of thumb
If your app is still in the early growth stage, optimize first, then add replicas and pooling, then scale hardware. Only consider sharding when a single primary plus replicas can’t meet your write or data size requirements.
If you want, I can also give you:
- a step-by-step scaling checklist
- advice for read-heavy vs write-heavy apps
- or a PostgreSQL scaling architecture diagram.