Sharding PostgreSQL for Django Applications with Citus
postgresql

Sharding PostgreSQL for Django Applications with Citus

Learn how to scale Django horizontally by using the Citus extension for distributed database sharding.

Termtrix
Termtrix
Jan 29, 2026
4min read

Sharding PostgreSQL for Django Applications with Citus

Scaling a Django application often leads developers to a crossroads: continue scaling vertically by adding more RAM and CPU, or move toward horizontal scaling. While database sharding is a powerful solution for horizontal growth, it is often avoided due to its perceived complexity. However, you can achieve database sharding without leaving the PostgreSQL ecosystem or abandoning the Django ORM by using Citus.

What is Citus?

Citus is an open-source extension for PostgreSQL that transforms it into a distributed database. Rather than keeping all data on a single instance, Citus distributes your data across multiple machines, known as nodes.

Key features of Citus include:

  • Sharding: Data is split into smaller chunks called shards.
  • Distributed Storage: Shards are spread across multiple worker nodes.
  • Parallel Query Execution: Queries are executed in parallel across nodes for faster performance.
  • Horizontal Scalability: You can add more nodes to handle increased load.

To your Django application, Citus appears as a standard PostgreSQL database, allowing you to scale without switching to a different database engine.

Why Django and Citus Work Well Together

The primary advantage of using Citus with Django is that it requires no special drivers, SDKs, or heavy ORM modifications. Since Citus is a PostgreSQL extension, Django continues to communicate using standard SQL.

From the application's perspective, it is simply connecting to PostgreSQL. The Citus coordinator node receives the query and determines exactly which shard should handle the request. This separation of concerns allows you to maintain a clean codebase while Citus manages the underlying distributed logic.

The Key Concept: The Distribution Key

Citus does not automatically determine how to shard your data. You must define a distribution key (also known as a shard key), which is a column that dictates where each row is stored.

In a typical multi-tenant SaaS application, the distribution key is usually:

  • tenant_id
  • org_id
  • account_id

Example Django Model

Consider a standard Django model where we want to shard data by a tenant:

from django.db import models

class User(models.Model):
    tenant_id = models.UUIDField()
    email = models.EmailField()
    created_at = models.DateTimeField(auto_now_add=True)

After running your standard Django migrations, you distribute the table using a single SQL command in the Citus coordinator:

SELECT create_distributed_table('users', 'tenant_id');

Once this is configured, Citus handles the complexity of data placement.

How Sharding Works Internally

When your Django application executes a command, the workflow follows a specific path to ensure efficiency:

  1. Django ORM: Executes a standard command, such as User.objects.create().
  2. Citus Coordinator: Receives the SQL query.
  3. Hashing: The coordinator hashes the tenant_id value.
  4. Routing: The query is routed to the specific worker node containing the corresponding shard.

This process ensures that no custom routing logic is required within your Django views or models, and no custom ORM hacks are necessary.

UUIDs and Sharding Strategy

A common concern involves using UUIDs in distributed systems. If you shard by a unique user_id (where every row has a different ID), you lose the benefits of data locality.

The rule of thumb is to shard by a group, not an individual row.

  • Recommended: Use a UUID for the tenant_id. All users belonging to that tenant will live on the same shard, making joins and lookups efficient.
  • Avoid: Sharding by a unique row ID (like a primary key id), as this forces queries to scan multiple shards unnecessarily.

Practical Use Cases for Citus

Citus is specifically designed for high-growth scenarios where a single PostgreSQL instance becomes a bottleneck. It is a perfect fit for:

  • Multi-tenant SaaS Architecture: Keeping all data for one customer on a single shard.
  • Large Datasets: Tables containing millions or billions of rows.
  • Write-Heavy Workloads: Distributing write operations across multiple nodes.
  • Real-time Analytics: Running complex queries over event logs, audit logs, or telemetry data.

Common Mistakes and Limitations

While Citus is powerful, it is not a "magic button" for every application. You should avoid Citus if:

  • The App is a Simple CRUD App: If your dataset fits comfortably on a single node, the overhead of a distributed system is unnecessary.
  • Early Stage MVPs: Vertical scaling is usually sufficient and easier to manage in the early stages.
  • Heavy Random Joins: Performing joins across tables that are not sharded by the same key can be slow, as it requires moving data between nodes.
  • Vertical Scaling is Still Viable: If you can solve your performance issues by adding more CPU or RAM, that remains the simplest path.

Conclusion

Citus allows Django developers to scale PostgreSQL horizontally while remaining within the familiar Django ecosystem. By carefully choosing a distribution key and designing your schema for multi-tenancy, you can handle massive datasets and high-traffic workloads without the risk of a custom sharding implementation.

Key Takeaways:

  • Citus extends PostgreSQL to support distributed tables and parallel queries.
  • Django interacts with Citus as if it were a standard PostgreSQL database.
  • The choice of a distribution key (like tenant_id) is the most critical design decision.
  • Use Citus for multi-tenant SaaS or high-volume write workloads, but stick to plain PostgreSQL for smaller, simpler applications.
#postgresql#django#citus#sharding#distributed-databases#database-scaling

Read Next

Building RAG with Elasticsearch as a Vector Store
System Design

Building RAG with Elasticsearch as a Vector Store

Build a production-ready RAG system using Elasticsearch as a unified vector store. Learn how to integrate LangChain and Ollama for efficient document retrieval.

Using an MCP Server with LangGraph: A Practical Guide to MCP Adapters
AI Agent

Using an MCP Server with LangGraph: A Practical Guide to MCP Adapters

Learn how to integrate an MCP server with LangGraph using MCP adapters to build deterministic, schema-validated AI agents. This practical guide explains why prompt-only tool calling fails and how MCP enables reliable, production-grade agent workflows.

🚀 Turbopack in Next.js: Does turbopackFileSystemCacheForDev Make Your App Lightning Fast?
Next js

🚀 Turbopack in Next.js: Does turbopackFileSystemCacheForDev Make Your App Lightning Fast?

How to Create a Perfect AWS Security Group (Production-Ready & Secure)
Cloud Security

How to Create a Perfect AWS Security Group (Production-Ready & Secure)

Learn how to design a production-ready AWS Security Group using least-privilege principles for EC2, RDS, and Redis—without breaking your app. AWS Security Group best practices Secure EC2 Security Group RDS Security Group configuration Redis Security Group AWS AWS least privilege networking Cloud security for backend apps

Load Testing FastAPI: Can Your API Handle 1 Million Requests?
Backend Engineering

Load Testing FastAPI: Can Your API Handle 1 Million Requests?

Learn how to load test a FastAPI application using Apache JMeter to simulate one million requests, analyze throughput and latency, and uncover real production bottlenecks before traffic hits.

How to Use PostgreSQL for LangGraph Memory and Checkpointing with FastAPI
AI Engineering

How to Use PostgreSQL for LangGraph Memory and Checkpointing with FastAPI

A deep dive into real-world issues when integrating LangGraph with FastAPI and Postgres. Learn why async context managers break checkpointing, how to fix _AsyncGeneratorContextManager errors, create missing tables, and deploy LangGraph agents correctly in production.

Building a Simple AI Agent Using FastAPI, LangGraph, and MCP
AI Agents

Building a Simple AI Agent Using FastAPI, LangGraph, and MCP

Build a production-ready AI agent using FastAPI, LangGraph, and MCP. Learn how to design tool-calling agents with memory, Redis persistence, and clean workflow orchestration.

Using Celery With FastAPI: Solving Async Event Loop Errors Cleanly--
Backend Engineering

Using Celery With FastAPI: Solving Async Event Loop Errors Cleanly--

Learn why async/await fails inside Celery tasks when using FastAPI, and discover a clean, production-safe pattern to avoid event loop errors using internal FastAPI endpoints.Python FastAPI Celery AsyncProgramming BackendEngineering DistributedSystems Microservices

Sharding PostgreSQL for Django Applications with Citus
Databases

Sharding PostgreSQL for Django Applications with Citus

Scale your Django application horizontally by sharding PostgreSQL with the Citus extension. Improve performance with distributed storage and parallel queries.