Skip to content

Lab: Sharded Postgres (Citus)

Pairs with: Sharding

A real Citus cluster — 1 coordinator + 3 workers. Distribute a table by shard key, query pg_dist_shard_placement to see where data actually landed, compare a cross-shard query plan against a single-shard one, and reproduce a real hot shard with docker stats showing one container doing all the work.

docker-compose.yml

name: labs-sharding

services:
  coordinator:
    image: citusdata/citus:12.1
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: app_pass
      POSTGRES_DB: appdb
    ports:
      - "5432:5432"
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "appuser"]
      interval: 5s
      timeout: 5s
      retries: 20

  worker1:
    image: citusdata/citus:12.1
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: app_pass
      POSTGRES_DB: appdb
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "appuser"]
      interval: 5s
      timeout: 5s
      retries: 20

  worker2:
    image: citusdata/citus:12.1
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: app_pass
      POSTGRES_DB: appdb
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "appuser"]
      interval: 5s
      timeout: 5s
      retries: 20

  worker3:
    image: citusdata/citus:12.1
    environment:
      POSTGRES_USER: appuser
      POSTGRES_PASSWORD: app_pass
      POSTGRES_DB: appdb
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "appuser"]
      interval: 5s
      timeout: 5s
      retries: 20

  # Runs once: waits for coordinator + all workers, then wires the cluster
  # together with SELECT citus_add_node(...). This is the "shard count"
  # control from the sim brought to life — add/remove services below and
  # re-run this to change the cluster shape.
  cluster-init:
    image: citusdata/citus:12.1
    depends_on:
      coordinator:
        condition: service_healthy
      worker1:
        condition: service_healthy
      worker2:
        condition: service_healthy
      worker3:
        condition: service_healthy
    entrypoint: ["bash", "-c"]
    command:
      - |
        set -e
        for w in worker1 worker2 worker3; do
          psql -h coordinator -U appuser -d appdb -c "SELECT citus_add_node('$$w', 5432);"
        done
        psql -h coordinator -U appuser -d appdb -c "SELECT * FROM citus_get_active_worker_nodes();"
    environment:
      PGPASSWORD: app_pass

Exercises

The full walkthrough (distribute a table, verify real shard balance, feel the cross-shard query cost, simulate a hot key) lives in the lab's README:

labs/sharding-citus/README.md on GitHub

git clone https://github.com/sanketn26/interview-prep
cd interview-prep/labs/sharding-citus
docker compose up -d --wait coordinator worker1 worker2 worker3
docker compose up cluster-init

Apple Silicon / arm64

Citus only publishes linux/amd64 images — Docker runs it under emulation on an arm64 host. Works fine, just slower to start.

← All Labs