Skip to content
Back to insights
PostgresArchitectureScalingFebruary 14, 20262 min read

Scaling Postgres to millions of rows without rewriting your stack

Five concrete moves that will buy your team another order of magnitude on Postgres before you reach for sharding.

By APLINDO Engineering

Frequently asked questions

When is Postgres actually out of capacity?
After indexes, pooling, partitioning, replicas, and async writes are in place—not when a single slow query appears in APM.
Should all reads go to replicas?
No—keep transactional reads on the primary; send lag-tolerant analytics and exports to replicas.
Is sharding ever the right first step?
Rarely. Sharding without a clear hot-table profile usually multiplies operational cost without fixing root causes.

Most teams that think they need to rewrite their database actually need a few well-placed Postgres changes. Here are the five we recommend, in priority order.

1. Do your indexes match real queries?

EXPLAIN ANALYZE on every query that hits the top of your APM. If a query touches a table over 100k rows and you can't see the index in the plan, fix that first. Composite indexes that match the WHERE + ORDER BY shape will pay back in days.

2. Is connection pooling configured correctly?

A Postgres instance with 200 connection slots will fall over long before it runs out of CPU. Put PgBouncer (or your cloud's equivalent) in front of every app server, and budget connections per service.

3. Can you move cold rows out of the hot path?

If you have a rows table with 80% of rows that are read once a year, partition by date or move them to a cold_* table. The hot table fits in memory; the cold table doesn't have to.

4. Are read replicas used for the right workloads?

Read replicas are easy to add and easy to misuse. Send your "expensive but lag-tolerant" queries (analytics dashboards, exports) to a replica. Keep transactional reads on the primary.

5. Should heavy side effects stay in the request transaction?

If you're sending an email, indexing a search document, or updating a denormalized counter inside the same transaction as a user-visible write, move it to a background job. Latency, throughput, and reliability all improve.

Key takeaways

  • Index and pool before you shard—most "we need a new database" moments are fixable in Postgres.
  • Partition or archive cold data so the working set stays in memory.
  • Shard only when access patterns are documented and the hot tables are obvious.

When should you actually shard?

After all five of the above. If your single Postgres still can't keep up, you have a much clearer picture of which tables and which access patterns to shard — and you've bought yourself another year to do it well.

Ready to ship something real?

Book a 30-minute call. We'll review your roadmap, recommend the smallest useful next step, and tell you honestly whether we're the right partner.