The 'Small Data' Fallacy: Why Citus is Often a Premature Optimization
Scaling out via Citus or sharding often introduces more latency and complexity than it solves for mid-market SaaS providers. Before you distribute your data, ensure you have actually exhausted the limits of a single beefy instance and clean schema isolation.
The Allure of the Infinite Scale
In the modern SaaS engineering landscape, there is a recurring obsession with 'infinite scale.' Developers look at the architecture of GitHub or Instagram and decide that their multi-tenant B2B app, which currently occupies 400GB of disk space, needs a distributed database strategy. Enter Citus: a powerful, well-engineered extension for PostgreSQL that transforms it into a distributed system.
Citus is impressive technology, but for 95% of mid-market SaaS companies, implementing it is a premature optimization that results in a 'distributed join tax' and operational overhead that far outweighs the benefits. The reality of production DBA work is that vertical scaling is still the most cost-effective, reliable, and performance-predictable path for the vast majority of workloads.
The Distributed Join Tax
The moment you move from a single-node PostgreSQL instance to a distributed Citus cluster, the fundamental physics of your queries change. On a single node, a join between two tables is a local memory and CPU operation. In a distributed environment, if your data isn't perfectly co-located by a distribution key (usually a tenant_id), the database must perform 'reshuffling.'
This means data is pulled from worker nodes, sent across the network, and joined in flight or on the coordinator node. Even with 10Gbps networking, the latency floor for a distributed join is orders of magnitude higher than a local index lookup. If your application code relies on complex reporting queries or deep relational joins across many tables, Citus will punish you unless your schema is perfectly aligned. Most legacy SaaS schemas are not.
Hardware is Cheaper than Engineering Hours
I often see teams spend six months re-architecting their persistence layer to support Citus because they hit 'performance issues' on an AWS r5.large. This is madness.
A single PostgreSQL instance on modern hardware can handle an incredible amount of throughput. We now live in an era where you can provision a cloud instance with 4TB of RAM and 128 vCPUs. With NVMe storage and proper tuning of shared_buffers, work_mem, and parallel_workers, a single node can comfortably manage multi-terabyte datasets and tens of thousands of transactions per second.
The cost of a high-end RDS or Bare Metal instance is negligible compared to the salary of two senior engineers spending a year debugging distributed deadlocks, managing shard rebalancing, and rewriting application code to avoid cross-shard constraints. Before you shard, buy more RAM. Then buy more.
Schema Isolation: The Middle Path
If you are worried about tenant 'noisy neighbor' issues or data isolation, you don't need a distributed database. You need better schema design. PostgreSQL is exceptionally good at handling multiple schemas within a single database.
By isolating tenants into their own schemas or using Row-Level Security (RLS) with a solid indexing strategy, you get the logical separation required for compliance and performance predictability without the complexity of a distributed coordinator. If one tenant grows so large that they impact others, you can migrate that single schema to a dedicated instance far more easily than you can un-pick a Citus distribution key.
When Citus Actually Makes Sense
To be clear, Citus is not 'bad'—it is specialized. It shines in two specific scenarios:
1. Real-time Analytics on Massive Streams: If you are ingesting millions of events per second and need to run sub-second aggregations over billions of rows.
2. True Multi-Tenant Hyper-Scale: When you have hundreds of thousands of tenants and your data grows by terabytes per month, making vertical scaling physically impossible.
If you aren't at that scale, you are paying for a solution to a problem you don't have.
The Operational Burden
Running Citus isn't just about the database; it's about the ecosystem. Your backups become more complex. Your high-availability (HA) requirements double or triple because you now have a coordinator and multiple workers to failover. Your monitoring stack must now account for distributed execution plans.
In a single-node setup, EXPLAIN ANALYZE tells you exactly what happened. In a distributed setup, you are looking at distributed execution traces, trying to figure out which worker node bottlenecked the coordinator. It requires a level of DBA expertise that is rare and expensive.
Conclusion: Stay Monolithic as Long as Possible
As a DBA, my goal is stability and predictability. Sharding is a one-way door that introduces non-deterministic latency and massive architectural rigidity.
The takeaway: Optimize your queries, normalize your indexes, and scale your hardware vertically until it is no longer viable. Only when the largest available instance on the market cannot handle your load should you consider the complexity of a distributed PostgreSQL layer. Until then, keep it simple. Your on-call rotation will thank you.
Related services
Dealing with this in production? Here's how we help.
Database Performance Tuning
Slow queries, execution plans, index strategy, and lock contention fixed at the root cause.
24/7 Remote DBA Support
Around-the-clock monitoring, proactive detection, and emergency incident response.
← All posts