citus

Distributed PostgreSQL as an extension

Overview

PackageVersionCategoryLicenseLanguage
citus14.0.0OLAPAGPL-3.0C
IDExtensionBinLibLoadCreateTrustRelocSchema
2400citusNoYesYesYesNoNopg_catalog
2401citus_columnarNoYesNoYesNoNopg_catalog
Relatedpg_partman plproxy columnar pg_fkpart timescaledb pg_duckdb tablefunc hll
Depended Bydocumentdb_distributed

conflict with hydra

Version

TypeRepoVersionPG VerPackageDeps
EXTPIGSTY14.0.01817161514citus-
RPMPIGSTY14.0.01817161514citus_$v-
DEBPIGSTY14.0.01817161514postgresql-$v-citus-
OS / PGPG18PG17PG16PG15PG14
el8.x86_64
PIGSTY 13.0.0
el8.x86_64.pg14 : citus_14
citus_14-13.0.0-1PIGSTY.el8.x86_64.rpm PIGSTY · 13.0.0 · 807.0KiB citus_14-13.0.0-1PGDG.rhel8.x86_64.rpm PGDG · 13.0.0 · 814.2KiB citus_14-12.1.6-1PGDG.rhel8.x86_64.rpm PGDG · 12.1.6 · 813.6KiB citus_14-12.1.5-1PGDG.rhel8.x86_64.rpm PGDG · 12.1.5 · 812.5KiB citus_14-12.1.4-1PGDG.rhel8.x86_64.rpm PGDG · 12.1.4 · 812.5KiB citus_14-12.1.3-1PGDG.rhel8.x86_64.rpm PGDG · 12.1.3 · 812.3KiB citus_14-12.1.2-1PGDG.rhel8.x86_64.rpm PGDG · 12.1.2 · 811.1KiB citus_14-12.1.1-1PGDG.rhel8.x86_64.rpm PGDG · 12.1.1 · 811.0KiB citus_14-12.1.0-2PGDG.rhel8.x86_64.rpm PGDG · 12.1.0 · 810.5KiB citus_14-12.0.0-1PGDG.rhel8.x86_64.rpm PGDG · 12.0.0 · 813.4KiB citus_14-11.3.0-2.rhel8.x86_64.rpm PGDG · 11.3.0 · 796.7KiB citus_14-11.2.1-1.rhel8.x86_64.rpm PGDG · 11.2.1 · 776.9KiB citus_14-11.2.0-1.rhel8.x86_64.rpm PGDG · 11.2.0 · 776.0KiB citus_14-11.1.5-1.rhel8.x86_64.rpm PGDG · 11.1.5 · 765.6KiB citus_14-11.1.4-1.rhel8.x86_64.rpm PGDG · 11.1.4 · 765.3KiB citus_14-11.1.3-1.rhel8.x86_64.rpm PGDG · 11.1.3 · 765.1KiB citus_14-11.1.2-1.rhel8.x86_64.rpm PGDG · 11.1.2 · 764.4KiB citus_14-11.1.1-1.rhel8.x86_64.rpm PGDG · 11.1.1 · 762.6KiB citus_14-11.0.6-1.rhel8.x86_64.rpm PGDG · 11.0.6 · 701.4KiB citus_14-11.0.5-1.rhel8.x86_64.rpm PGDG · 11.0.5 · 700.3KiB citus_14-11.0.4-1.rhel8.x86_64.rpm PGDG · 11.0.4 · 699.6KiB citus_14-11.0.3-1.rhel8.x86_64.rpm PGDG · 11.0.3 · 699.5KiB citus_14-11.0.2-1.rhel8.x86_64.rpm PGDG · 11.0.2 · 698.6KiB citus_14-10.2.5-1.rhel8.x86_64.rpm PGDG · 10.2.5 · 618.5KiB citus_14-10.2.4-1.rhel8.x86_64.rpm PGDG · 10.2.4 · 618.6KiB citus_14-10.2.3-1.rhel8.x86_64.rpm PGDG · 10.2.3 · 618.5KiB citus_14-10.2.2-1.rhel8.x86_64.rpm PGDG · 10.2.2 · 615.3KiB citus_14-10.2.1-1.rhel8.x86_64.rpm PGDG · 10.2.1 · 614.8KiB citus_14-10.2.0-1.rhel8.x86_64.rpm PGDG · 10.2.0 · 614.0KiB
el8.aarch64
el9.x86_64
PIGSTY 13.0.0
el9.x86_64.pg14 : citus_14
citus_14-13.0.0-1PIGSTY.el9.x86_64.rpm PIGSTY · 13.0.0 · 801.9KiB citus_14-13.0.0-1PGDG.rhel9.x86_64.rpm PGDG · 13.0.0 · 803.4KiB citus_14-12.1.6-1PGDG.rhel9.x86_64.rpm PGDG · 12.1.6 · 802.9KiB citus_14-12.1.5-1PGDG.rhel9.x86_64.rpm PGDG · 12.1.5 · 800.4KiB citus_14-12.1.4-1PGDG.rhel9.x86_64.rpm PGDG · 12.1.4 · 800.6KiB citus_14-12.1.3-1PGDG.rhel9.x86_64.rpm PGDG · 12.1.3 · 800.1KiB citus_14-12.1.2-1PGDG.rhel9.x86_64.rpm PGDG · 12.1.2 · 798.9KiB citus_14-12.1.1-1PGDG.rhel9.x86_64.rpm PGDG · 12.1.1 · 798.9KiB citus_14-12.1.0-2PGDG.rhel9.x86_64.rpm PGDG · 12.1.0 · 798.9KiB citus_14-12.0.0-1PGDG.rhel9.x86_64.rpm PGDG · 12.0.0 · 802.0KiB citus_14-11.3.0-2.rhel9.x86_64.rpm PGDG · 11.3.0 · 787.6KiB citus_14-11.2.1-1.rhel9.x86_64.rpm PGDG · 11.2.1 · 767.2KiB citus_14-11.2.0-1.rhel9.x86_64.rpm PGDG · 11.2.0 · 766.5KiB citus_14-11.1.5-1.rhel9.x86_64.rpm PGDG · 11.1.5 · 756.8KiB citus_14-11.1.4-1.rhel9.x86_64.rpm PGDG · 11.1.4 · 755.9KiB citus_14-11.1.3-1.rhel9.x86_64.rpm PGDG · 11.1.3 · 755.8KiB citus_14-11.1.2-1.rhel9.x86_64.rpm PGDG · 11.1.2 · 755.4KiB citus_14-11.1.1-1.rhel9.x86_64.rpm PGDG · 11.1.1 · 754.1KiB citus_14-11.0.6-1.rhel9.x86_64.rpm PGDG · 11.0.6 · 691.3KiB citus_14-11.0.5-1.rhel9.x86_64.rpm PGDG · 11.0.5 · 690.5KiB citus_14-11.0.4-1.rhel9.x86_64.rpm PGDG · 11.0.4 · 690.0KiB citus_14-11.0.3-1.rhel9.x86_64.rpm PGDG · 11.0.3 · 689.8KiB citus_14-11.0.2-1.rhel9.x86_64.rpm PGDG · 11.0.2 · 689.0KiB citus_14-10.2.5-1.rhel9.x86_64.rpm PGDG · 10.2.5 · 612.1KiB citus_14-10.2.4-1.rhel9.x86_64.rpm PGDG · 10.2.4 · 613.7KiB citus_14-10.2.3-1.rhel9.x86_64.rpm PGDG · 10.2.3 · 613.7KiB
el9.aarch64
el10.x86_64PIGSTY MISS
el10.aarch64PIGSTY MISS
d12.x86_64
d12.aarch64
d13.x86_64PIGSTY MISS
d13.aarch64
PIGSTY 14.0.0
PIGSTY 14.0.0
PIGSTY 14.0.0
PIGSTY 13.2.0
PIGSTY MISS
u22.x86_64
u22.aarch64
PIGSTY 14.0.0
PIGSTY 14.0.0
PIGSTY 14.0.0
PIGSTY 13.2.0
PIGSTY 13.0.0
u24.x86_64
u24.aarch64
PIGSTY 14.0.0
PIGSTY 14.0.0
PIGSTY 14.0.0
PIGSTY 13.2.0
PIGSTY 13.0.0

Build

You can build the RPM / DEB packages for citus using pig build:

pig build pkg citus         # build RPM / DEB packages

Install

You can install citus directly. First, make sure the PGDG and PIGSTY repositories are added and enabled:

pig repo add pgsql -u          # Add repo and update cache

Install the extension using pig or apt/yum/dnf:

pig install citus;          # Install for current active PG version
pig ext install -y citus -v 18  # PG 18
pig ext install -y citus -v 17  # PG 17
pig ext install -y citus -v 16  # PG 16
dnf install -y citus_18       # PG 18
dnf install -y citus_17       # PG 17
dnf install -y citus_16       # PG 16
apt install -y postgresql-18-citus   # PG 18
apt install -y postgresql-17-citus   # PG 17
apt install -y postgresql-16-citus   # PG 16

Preload:

shared_preload_libraries = 'citus';

Create Extension:

CREATE EXTENSION citus;

Usage

citus: Distributed PostgreSQL for multi-tenant and real-time analytics

Citus transforms PostgreSQL into a distributed database, enabling horizontal scaling by sharding tables across multiple nodes. It supports multi-tenant SaaS workloads, real-time analytics, and high-throughput transactional use cases while preserving the full PostgreSQL feature set.

Key Documentation:

Getting Started

Enable the extension and add worker nodes:

CREATE EXTENSION citus;

-- Add worker nodes to the cluster
SELECT citus_set_coordinator_host('coord-host', 5432);
SELECT * FROM citus_add_node('worker-1', 5432);
SELECT * FROM citus_add_node('worker-2', 5432);

-- Verify the cluster
SELECT * FROM citus_get_active_worker_nodes();

Creating Distributed Tables

Distribute a table by a chosen distribution column (shard key). Rows with the same key value are colocated on the same shard.

CREATE TABLE events (
    tenant_id   INT,
    event_id    BIGSERIAL,
    event_time  TIMESTAMPTZ DEFAULT now(),
    event_type  TEXT,
    payload     JSONB,
    PRIMARY KEY (tenant_id, event_id)
);

-- Hash-distribute by tenant_id (default: 32 shards)
SELECT create_distributed_table('events', 'tenant_id');

You can control the shard count:

SELECT create_distributed_table('events', 'tenant_id', shard_count := 64);

Reference Tables

Small lookup tables that need to be joined with distributed tables should be created as reference tables. They are replicated in full to every node.

CREATE TABLE countries (
    code CHAR(2) PRIMARY KEY,
    name TEXT NOT NULL
);

SELECT create_reference_table('countries');

Reference tables can be joined with any distributed table without restrictions.

Colocation

Tables distributed on the same column type and shard count are automatically colocated, meaning rows with matching distribution keys are stored on the same node. This enables efficient local joins.

CREATE TABLE tenants (
    id   INT PRIMARY KEY,
    name TEXT
);
SELECT create_distributed_table('tenants', 'id');

CREATE TABLE orders (
    tenant_id  INT REFERENCES tenants(id),
    order_id   BIGSERIAL,
    amount     NUMERIC,
    PRIMARY KEY (tenant_id, order_id)
);
SELECT create_distributed_table('orders', 'tenant_id');

-- This join is pushed down to each node (no cross-shard traffic)
SELECT t.name, sum(o.amount)
FROM tenants t JOIN orders o ON t.id = o.tenant_id
GROUP BY t.name;

You can also explicitly specify colocation groups:

SELECT create_distributed_table('orders', 'tenant_id',
    colocate_with := 'tenants');

Distributed Queries

Citus pushes queries down to individual shards when possible. Queries that filter on the distribution column are routed to a single shard:

-- Single-shard query (fast, touches one node)
SELECT * FROM events WHERE tenant_id = 42;

Cross-shard queries are parallelized across all workers:

-- Parallel aggregation across all shards
SELECT event_type, count(*), avg(payload->>'duration')::numeric
FROM events
WHERE event_time > now() - INTERVAL '1 hour'
GROUP BY event_type
ORDER BY count DESC
LIMIT 10;

Distributed Joins

Joins between colocated tables on the distribution column are executed locally on each shard:

-- Colocated join: efficient, no data movement
SELECT e.*, o.amount
FROM events e JOIN orders o
    ON e.tenant_id = o.tenant_id
WHERE e.tenant_id = 42;

Joins with reference tables work from any distributed table:

SELECT e.*, c.name AS country_name
FROM events e JOIN countries c ON e.payload->>'country' = c.code;

Node Management

-- Add a new node
SELECT * FROM citus_add_node('worker-3', 5432);

-- Remove a node (moves shards to other nodes first)
SELECT * FROM citus_drain_node('worker-1', 5432);
SELECT * FROM citus_remove_node('worker-1', 5432);

-- Disable a node temporarily
SELECT * FROM citus_disable_node('worker-2', 5432);
SELECT * FROM citus_activate_node('worker-2', 5432);

-- View current cluster state
SELECT * FROM citus_get_active_worker_nodes();

Shard Rebalancing

After adding or removing nodes, rebalance shards to distribute data evenly:

-- Rebalance all distributed tables
SELECT citus_rebalance_start();

-- Monitor rebalance progress
SELECT * FROM citus_rebalance_status();

-- Rebalance a specific table
SELECT rebalance_table_shards('events');

Shard Management

-- View shard placements
SELECT * FROM citus_shards;

-- View shard sizes
SELECT table_name, shard_count, citus_total_relation_size(table_name::text)
FROM citus_tables;

-- Move a specific shard to another node
SELECT citus_move_shard_placement(shard_id, 'source-host', 5432, 'dest-host', 5432);

Configuration Parameters

Key GUC parameters for tuning Citus:

-- Number of parallel connections per node for multi-shard queries
SET citus.max_adaptive_executor_pool_size = 4;

-- Shard replication factor (default 1; set to 2 for HA without streaming replication)
SET citus.shard_replication_factor = 1;

-- Control executor behavior
SET citus.multi_shard_modify_mode = 'parallel';   -- or 'sequential'
SET citus.enable_repartition_joins = on;           -- enable repartitioned joins

-- Task assignment policy
SET citus.task_assignment_policy = 'round-robin';  -- or 'greedy', 'first-replica'

-- Log distributed queries
SET citus.log_multi_join_order = on;
SET citus.explain_all_tasks = on;

Example: Multi-Tenant SaaS Schema

A typical multi-tenant schema distributes all tenant-scoped tables by tenant_id:

CREATE TABLE tenants (
    tenant_id   INT PRIMARY KEY,
    name        TEXT,
    plan        TEXT DEFAULT 'free',
    created_at  TIMESTAMPTZ DEFAULT now()
);
SELECT create_distributed_table('tenants', 'tenant_id');

CREATE TABLE users (
    tenant_id   INT,
    user_id     BIGSERIAL,
    email       TEXT,
    PRIMARY KEY (tenant_id, user_id)
);
SELECT create_distributed_table('users', 'tenant_id');

CREATE TABLE projects (
    tenant_id   INT,
    project_id  BIGSERIAL,
    name        TEXT,
    owner_id    BIGINT,
    PRIMARY KEY (tenant_id, project_id)
);
SELECT create_distributed_table('projects', 'tenant_id');

-- Shared lookup: replicated to every node
CREATE TABLE plans (
    name TEXT PRIMARY KEY,
    max_users INT,
    max_projects INT
);
SELECT create_reference_table('plans');

-- All joins scoped to a tenant are colocated and fast
SELECT u.email, p.name AS project
FROM users u
JOIN projects p ON u.tenant_id = p.tenant_id AND u.user_id = p.owner_id
WHERE u.tenant_id = 7;

Example: Real-Time Analytics

Distributed aggregation for dashboards and analytics:

CREATE TABLE page_views (
    site_id      INT,
    url          TEXT,
    view_time    TIMESTAMPTZ DEFAULT now(),
    user_agent   TEXT,
    country      CHAR(2)
);
SELECT create_distributed_table('page_views', 'site_id');

-- Real-time rollup: parallelized across shards
SELECT
    date_trunc('minute', view_time) AS minute,
    count(*)                        AS views,
    count(DISTINCT country)         AS countries
FROM page_views
WHERE site_id = 1
  AND view_time > now() - INTERVAL '1 hour'
GROUP BY minute
ORDER BY minute DESC;

-- Top pages across all sites (cross-shard parallel query)
SELECT url, count(*) AS views
FROM page_views
WHERE view_time > now() - INTERVAL '24 hours'
GROUP BY url
ORDER BY views DESC
LIMIT 20;

Last Modified 2026-03-12: add pg extension catalog (95749bf)