Citus extends PostgreSQL with distributed database capabilities, allowing data and workloads to be spread across multiple nodes while preserving the PostgreSQL ecosystem developers already know.

PostgreSQL is remarkably capable, but eventually some workloads outgrow the limits of a single database server. When larger datasets, higher write volumes, and increasing query concurrency become the bottleneck, simply adding more CPU or memory stops being enough. Citus extends PostgreSQL with distributed database capabilities, allowing data and workloads to be spread across multiple nodes while preserving the PostgreSQL ecosystem developers already know. The real challenge is not adding nodes—it is choosing the right distribution strategy, keeping related data together, minimizing cross-node queries, and designing an architecture that remains predictable as the system grows.
Traditional PostgreSQL scaling usually starts vertically.
PostgreSQL
│
├── More CPU
├── More RAM
├── Faster Storage
└── Better NetworkingFor many applications, this works extremely well.
But eventually a workload can encounter limits around:
CPU
Memory
Storage throughput
Connection capacity
Write volume
Dataset size
Query concurrency
At that point, simply buying a larger database server may become expensive or insufficient.
Horizontal scaling changes the model:
Application
│
▼
Distributed SQL
│
┌────────────┼────────────┐
▼ ▼ ▼
Worker 1 Worker 2 Worker 3
│ │ │
└────────────┼────────────┘
▼
Distributed DataInstead of making one PostgreSQL server increasingly powerful, the workload is distributed across multiple machines.
That is where Citus becomes interesting.
Citus extends PostgreSQL with distributed data and query execution capabilities.
A simplified architecture looks like:
Application
↓
Coordinator
↓
┌──┼──────────┐
▼ ▼ ▼
W1 W2 W3The coordinator understands the distributed schema and determines where work needs to happen.
Workers store and process distributed data.
From the application's perspective, the system can still expose a PostgreSQL-compatible interface.
That means teams can continue using familiar technologies such as:
SQL
PostgreSQL drivers
ORMs
Indexes
Transactions where supported by the architecture
Existing PostgreSQL tooling
This is one of Citus's biggest advantages.
You are not abandoning PostgreSQL to adopt a completely different database model.
You are extending it.
The core architecture has two major roles.
The coordinator receives queries and manages distributed metadata.
Workers store distributed shards and execute portions of queries.
Conceptually:
Client
│
▼
Coordinator
│
Query Distribution
/ | \
▼ ▼ ▼
Worker 1 Worker 2 Worker 3A table can be distributed into multiple shards:
Orders
│
├── Shard 1 → Worker 1
├── Shard 2 → Worker 2
├── Shard 3 → Worker 3
└── Shard 4 → Worker 4The goal is to allow different parts of the workload to execute in parallel.
But there is an important architectural truth:
Citus scales workloads that can be distributed effectively. It cannot magically parallelize every PostgreSQL query.
Your data model still matters.
This is arguably the most important decision in a Citus architecture.
Suppose you have a SaaS platform:
Customers
Orders
Invoices
Events
UsersIf each customer represents an independent tenant, `tenant_id` may be a natural distribution key.
For example:
tenant_id = 101
↓
Shard A
tenant_id = 202
↓
Shard B
tenant_id = 303
↓
Shard CNow queries such as:
SELECT *
FROM orders
WHERE tenant_id = 101;can be routed efficiently to the relevant shard.
The ideal distribution column generally has:
High cardinality
Good data distribution
Frequent appearance in queries
Stable ownership semantics
Most importantly:
Choose a distribution key based on how the application accesses data—not simply based on which column looks unique.
Suppose your application has:
Tenants
│
├── Users
├── Orders
├── Payments
└── EventsIf these tables share the same distribution key, related rows can be placed together.
Conceptually:
Tenant 101
│
├── Users
├── Orders
├── Payments
└── Events
↓
Same Shard GroupThis is called co-location.
It can make joins much more efficient because the database can execute them locally rather than moving large amounts of data between workers.
Compare:
Worker 1
├── Orders
└── Users
Local Joinwith:
Worker 1 → Orders
↓
Network
↓
Worker 3 → UsersThe second architecture introduces additional coordination and data movement.
This is why distribution strategy should be designed together with the application's query patterns.
Not every table should necessarily be distributed.
Some data is relatively small and shared across the application.
Examples might include:
Countries
Currencies
Product categories
Configuration
Small lookup tables
These can often be treated as reference data.
Conceptually:
Reference Data
│
┌────────────┼────────────┐
▼ ▼ ▼
Worker 1 Worker 2 Worker 3This avoids forcing every small table into a complex distribution strategy.
The principle is simple:
Distribute large, scalable workloads. Replicate small shared data where that makes architectural sense.
Citus can distribute query execution, but query shape still matters.
Consider:
SELECT *
FROM orders
WHERE tenant_id = 1001;If `tenant_id` is the distribution key, this can be efficiently routed.
Now consider:
SELECT *
FROM orders
ORDER BY created_at DESC
LIMIT 100;Across a large distributed dataset, the database may need to coordinate work across multiple workers.
That can require:
Worker 1 ──┐
Worker 2 ──┼──→ Coordinator → Final Result
Worker 3 ──┘This is not necessarily bad.
But it has different performance characteristics from a query executed entirely on one shard.
When designing distributed SQL, ask:
Can the query be answered locally by one shard?
If yes, you can often achieve excellent performance.
One of the major benefits of distributing a large table is spreading storage and write workload.
Instead of:
One PostgreSQL Server
████████████████████you can have:
Worker 1 █████
Worker 2 █████
Worker 3 █████
Worker 4 █████As the dataset grows, additional workers can provide more capacity.
This can be particularly useful for:
Event platforms
SaaS applications
Analytics-heavy transactional systems
High-volume applications
Large multi-tenant datasets
But scaling capacity requires attention to shard distribution.
A poorly selected distribution key can create a hot shard:
Worker 1 ███████████████
Worker 2 ██
Worker 3 ██
Worker 4 █The cluster has multiple workers.
But the workload behaves like a single overloaded machine.
Distribution quality matters more than node count.
Multi-tenancy is one of the most natural use cases for distributed PostgreSQL.
Consider a SaaS platform:
SaaS Platform
│
┌───────────┼───────────┐
▼ ▼ ▼
Tenant A Tenant B Tenant C
│ │ │
▼ ▼ ▼
Shards Shards ShardsThe tenant ID can become the central distribution key.
Then application queries naturally become:
SELECT *
FROM orders
WHERE tenant_id = $1;This provides a strong architectural alignment between:
Business ownership
Data ownership
Query routing
Scaling
It can also make operational isolation easier to reason about.
For example, a particularly large tenant can be identified as a source of disproportionate workload.
A useful concept when designing Citus systems is data locality.
If a request primarily operates on one tenant:
Request
↓
tenant_id = 500
↓
Single Shardthe architecture can remain highly efficient.
If a request constantly needs:
Tenant A
+
Tenant B
+
Tenant C
+
Tenant Dthen distributed coordination becomes unavoidable.
Neither pattern is inherently wrong.
The important thing is to know which one your product actually needs.
Transactions become more interesting once data is distributed.
A local transaction on one shard can be much simpler:
Transaction
↓
Shard
↓
CommitA transaction involving multiple shards may require distributed coordination.
For example:
Transaction
├── Worker 1
└── Worker 3That is more expensive and more operationally complex.
This leads to an important design principle:
Design business operations so that the most common transactions stay within a single distribution boundary whenever practical.
For multi-tenant applications, that often means keeping tenant-owned data co-located.
Moving from a single PostgreSQL instance to a distributed architecture is not just a database configuration change.
You need to consider:
Table size
Shard count
Distribution key
Existing indexes
Foreign keys
Application query patterns
Downtime requirements
A migration might look like:
Existing PostgreSQL
↓
Workload Analysis
↓
Choose Distribution Key
↓
Create Distributed Tables
↓
Move Data
↓
Validate
↓
Route Application TrafficDo not select the distribution key after the migration has already started.
Changing a foundational data-placement decision later can be expensive.
A distributed database creates more things to monitor.
Track:
Coordinator health
Worker health
Shard distribution
Query latency
CPU
Memory
Storage
Network traffic
Cross-node queries
Hot shards
Connection counts
A useful architecture dashboard should answer:
Is the cluster healthy?
↓
Is the workload balanced?
↓
Are queries staying local?
↓
Which shards are hot?
↓
Which workers are approaching capacity?This is more useful than simply monitoring total cluster CPU.
For example:
Cluster CPU = 45%
may look healthy.
But:
Worker 1 = 95%
Worker 2 = 30%
Worker 3 = 25%
Worker 4 = 20%reveals a distribution problem.
Database scaling and application scaling should be considered together.
A modern architecture might look like:
Users
│
▼
Load Balancer
│
┌─────────┼─────────┐
▼ ▼ ▼
App 1 App 2 App 3
│ │ │
└─────────┼─────────┘
▼
Citus
/ \
▼ ▼
Worker 1 Worker 2
│ │
└───┬───┘
▼
Worker 3The application tier can scale horizontally while the database layer distributes storage and query execution.
This creates a platform that can grow in multiple dimensions.
A unique ID is not necessarily a good distribution key.
Query patterns matter more.
If related data constantly lives on different workers, network coordination can become expensive.
A poorly distributed workload can remain bottlenecked by one worker.
Distributed joins and aggregations are sometimes necessary, but excessive cross-shard operations can reduce predictability.
The PostgreSQL interface remains familiar, but the execution model is distributed.
Architect accordingly.
One enterprise customer may generate more traffic than hundreds of smaller tenants.
Plan for uneven workloads.
Small shared tables may be better handled as reference data.
The existing application may contain queries that are poorly suited to distributed execution.
Profile before migrating.
A scalable Citus deployment might look like:
Applications
│
▼
Connection Layer
│
▼
Citus Coordinator
│
┌───────────────┼───────────────┐
▼ ▼ ▼
Worker 1 Worker 2 Worker 3
┌──────┐ ┌──────┐ ┌──────┐
│Shard │ │Shard │ │Shard │
│Shard │ │Shard │ │Shard │
└──────┘ └──────┘ └──────┘
│ │ │
└───────────────┼───────────────┘
▼
Distributed DatasetSupporting layers should include:
Monitoring
Backups
Security
Connection management
Capacity planning
Migration tooling
The database should be treated as a distributed platform, not merely a larger PostgreSQL server.
Citus can be especially attractive for workloads with:
Large datasets
High write volumes
Multi-tenant data
Natural distribution keys
High query concurrency
PostgreSQL compatibility requirements
Horizontally scalable application workloads
It can be particularly compelling for SaaS architectures where tenant boundaries map naturally to data distribution.
For example:
Tenant
↓
Distribution Key
↓
Shard
↓
WorkerThis alignment can make the architecture much easier to reason about.
A distributed database is not automatically better.
Citus may be a poor fit when:
The dataset is comfortably handled by one PostgreSQL server
Queries frequently require global cross-tenant joins
The workload has no natural distribution key
Transactions routinely span many shards
The operational complexity outweighs the scalability requirement
In these cases, improving a single PostgreSQL deployment may be the better engineering decision.
Possible alternatives include:
Vertical scaling
Read replicas
Partitioning
Caching
Query optimization
Connection pooling
Workload separation
Do not distribute a workload simply because distribution is available.
Engineering leaders considering Citus should ask:
What is actually limiting our current PostgreSQL deployment?
Is the problem compute, storage, connections, write throughput, or query concurrency?
Do our tables have a natural distribution key?
Can related data be co-located?
Which queries must remain global?
How frequently do transactions cross data boundaries?
Are some tenants dramatically larger than others?
Can the application consistently include the distribution key in important queries?Most importantly:
Will distributing the data simplify our scaling problem—or simply move the complexity from one database server into the network?
That is the decision that matters.
Scaling PostgreSQL horizontally with Citus is fundamentally a data-modeling and workload-design problem.
The architecture is:
Understand Workload
↓
Choose Distribution Key
↓
Co-Locate Related Data
↓
Design Local Queries
↓
Distribute Storage + Compute
↓
Monitor Shards
↓
Scale WorkersStart with the application's access patterns.
Choose distribution keys around how data is naturally owned and queried.
Keep related tables co-located.
Use reference tables for small shared datasets where appropriate.
Design common transactions to stay within a distribution boundary.
Watch for hot shards and uneven tenant sizes.
Measure cross-node queries rather than assuming the cluster is balanced.
And remember that adding workers is only useful when the workload can actually use them.
The power of Citus is not simply that PostgreSQL can run on more machines. Its real value is that a familiar relational database can be extended into a distributed system while preserving much of the PostgreSQL development experience.
The winning architecture is therefore not:
"Put PostgreSQL on ten servers."
It is:
"Design the data so ten servers can work on the problem efficiently."
When distribution keys, query patterns, tenant boundaries, and operational requirements align, Citus can turn a PostgreSQL system that has reached the limits of vertical scaling into a horizontally scalable distributed SQL platform—without forcing the engineering team to abandon the PostgreSQL ecosystem they already understand.
We build custom software, mobile apps, and web platforms for startups and enterprises.



Their team became an extension of ours — within months they'd rebuilt our entire product experience from the ground up.
