← Back to posts
comparison guide self-hosted · · 16 min read

CockroachDB vs YugabyteDB vs TiDB: Best Distributed SQL Database 2026

Complete guide to self-hosted distributed SQL databases in 2026. Compare CockroachDB, YugabyteDB, and TiDB with Docker Compose setups, performance benchmarks, and production deployment strategies.

OS
Editorial Team

Why Self-Host a Distributed SQL Database?

Modern applications demand databases that scale horizontally while maintaining full ACID compliance and SQL compatibility. Traditional single-node databases hit a wall: vertical scaling gets expensive fast, read replicas introduce replication lag, and sharding manually is an operational nightmare. Distributed SQL databases solve this by combining the familiar relational model with the horizontal scalability of NoSQL systems.

Going self-hosted for your distributed SQL layer gives you complete control over data locality, eliminates vendor lock-in, and removes the steep per-core licensing costs that cloud-managed options impose. Whether you’re running a multi-region SaaS platform, a high-throughput e-commerce backend, or a real-time analytics pipeline, self-hosting a distributed SQL database puts the infrastructure back in your hands.

Key benefits of self-hosting:

  • Full data sovereignty — data never leaves your infrastructure, critical for GDPR, HIPAA, and financial compliance
  • Predictable costs — no per-vCPU or per-GB premiums; scale on your own hardware or cloud instances
  • Custom topology — design your own replication strategies, placement policies, and failover behavior
  • No vendor lock-in — all three databases discussed here are open-source and compatible with standard SQL
  • Deep tuning access — adjust storage engines, compaction schedules, and memory pools to match your workload

In this guide, we compare the three most mature open-source distributed SQL databases available in 2026: CockroachDB, YugabyteDB, and TiDB. Each takes a different architectural approach to the same problem, and understanding those differences is crucial for making the right choice.


Architecture Comparison

The three databases differ fundamentally in how they distribute data, handle consensus, and process queries.

FeatureCockroachDBYugabyteDBTiDB
LicenseBSL 1.1 (source-available)Apache 2.0Apache 2.0
Storage EnginePebble (RocksDB fork)DocDB (RocksDB-based)TiKV (Rust, MVCC)
Consensus ProtocolMulti-Raft (per-range)Multi-Raft (per-shard/tablet)Raft (per-region via PD)
SQL LayerBuilt-in, PostgreSQL wireBuilt-in, PostgreSQL wireSeparate layer (TiDB server)
API CompatibilityPostgreSQLPostgreSQL + Redis + CassandraMySQL
Written InGoC++ (core) + Go (ysql/ybctl)Go (SQL) + Rust (TiKV)
ShardingAutomatic, range-basedAutomatic, tablet-basedAutomatic, region-based
TransactionsSerializable (SI by default)Serializable SISnapshot Isolation
HTAP SupportNo (OLTP only)Limited (colocated analytics)Yes (TiFlash columnar)
Cluster ManagementBuilt-in cockroach nodeyb-master + yb-tserverPD (Placement Driver)
Max Tested Cluster Size10,000+ nodes1,000+ nodes1,000+ nodes
Community Activity (GitHub)~30k stars, very active~8k stars, active~38k stars, very active

CockroachDB

CockroachDB follows a shared-nothing, peer-to-peer architecture where every node runs the full stack: SQL processing, distributed transaction coordination, and key-value storage. Data is split into ranges (default 64 MB), each replicated across 3–5 nodes via a Raft group. The key innovation is per-range Raft — each range has its own independent consensus group, so a slow range doesn’t block the entire cluster.

The SQL layer is tightly coupled with the storage layer, which simplifies operations but means you can’t scale SQL compute independently from storage.

YugabyteDB

YugabyteDB uses a two-tier architecture: the yb-master tier handles metadata and cluster management, while yb-tserver nodes run both the storage engine (DocDB) and query services (YSQL for PostgreSQL, YCQL for Cassandra-compatible, and YEDIS for Redis-compatible APIs). Like CockroachDB, it uses per-shard Raft groups.

The major differentiator is DocDB, a document-oriented storage engine built on RocksDB that supports native JSON operations. YugabyteDB also offers the broadest API compatibility, supporting PostgreSQL, Cassandra, and Redis through a single deployment.

TiDB

TiDB has a decoupled architecture with three distinct components:

  • TiDB — stateless SQL compute layer (MySQL-compatible)
  • TiKV — distributed transactional key-value store (Raft-based)
  • PD (Placement Driver) — metadata and scheduling service

This separation means you can scale compute (TiDB servers) and storage (TiKV nodes) independently. Add more TiDB servers when you need more query throughput without touching storage. Add more TiKV nodes when you need more capacity. This is the only database of the three with true compute-storage decoupling.

TiDB also supports HTAP (Hybrid Transactional/Analytical Processing) through TiFlash, a columnar storage replica that runs analytical queries without impacting transactional workloads.


Installation and Deployment

All three databases provide docker images and Kubernetes operators. Below are production-ready Docker Compose configurations for each.

CockroachDB — 3-Node Cluster

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
# cockroachdb-cluster.yml
version: "3.9"

services:
  cockroach-1:
    image: cockroachdb/cockroach:latest
    container_name: crdb-1
    command: >
      start --cluster-name=prod-cluster
      --advertise-addr=cockroach-1
      --listen-addr=0.0.0.0:26257
      --http-addr=0.0.0.0:8080
      --join=cockroach-1:26257,cockroach-2:26257,cockroach-3:26257
      --certs-dir=/certs
      --cache=.25
      --max-sql-memory=.25
    volumes:
      - crdb-data-1:/cockroach/cockroach-data
      - ./certs:/certs:ro
    ports:
      - "26257:26257"
      - "8081:8080"
    networks:
      - cockroach-net
    deploy:
      resources:
        limits:
          memory: 4G
          cpus: "2"

  cockroach-2:
    image: cockroachdb/cockroach:latest
    container_name: crdb-2
    command: >
      start --cluster-name=prod-cluster
      --advertise-addr=cockroach-2
      --listen-addr=0.0.0.0:26257
      --http-addr=0.0.0.0:8080
      --join=cockroach-1:26257,cockroach-2:26257,cockroach-3:26257
      --certs-dir=/certs
      --cache=.25
      --max-sql-memory=.25
    volumes:
      - crdb-data-2:/cockroach/cockroach-data
      - ./certs:/certs:ro
    ports:
      - "26258:26257"
      - "8082:8080"
    networks:
      - cockroach-net
    deploy:
      resources:
        limits:
          memory: 4G
          cpus: "2"

  cockroach-3:
    image: cockroachdb/cockroach:latest
    container_name: crdb-3
    command: >
      start --cluster-name=prod-cluster
      --advertise-addr=cockroach-3
      --listen-addr=0.0.0.0:26257
      --http-addr=0.0.0.0:8080
      --join=cockroach-1:26257,cockroach-2:26257,cockroach-3:26257
      --certs-dir=/certs
      --cache=.25
      --max-sql-memory=.25
    volumes:
      - crdb-data-3:/cockroach/cockroach-data
      - ./certs:/certs:ro
    ports:
      - "26259:26257"
      - "8083:8080"
    networks:
      - cockroach-net
    deploy:
      resources:
        limits:
          memory: 4G
          cpus: "2"

  # Initialization service (run once)
  cockroach-init:
    image: cockroachdb/cockroach:latest
    container_name: crdb-init
    command: >
      init --certs-dir=/certs --host=cockroach-1:26257
    volumes:
      - ./certs:/certs:ro
    networks:
      - cockroach-net
    depends_on:
      - cockroach-1
    restart: "no"

volumes:
  crdb-data-1:
  crdb-data-2:
  crdb-data-3:

networks:
  cockroach-net:
    driver: bridge

Initialize the cluster and create a user:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# Generate certificates (self-signed for demo; use CA-signed in production)
mkdir -p certs
docker run --rm -v $(pwd)/certs:/certs \
  cockroachdb/cockroach:latest cert create-ca \
  --certs-dir=/certs --ca-key=/certs/ca.key

docker run --rm -v $(pwd)/certs:/certs \
  cockroachdb/cockroach:latest cert create-node \
  localhost 127.0.0.1 cockroach-1 cockroach-2 cockroach-3 \
  --certs-dir=/certs --ca-key=/certs/ca.key

docker run --rm -v $(pwd)/certs:/certs \
  cockroachdb/cockroach:latest cert create-client \
  root --certs-dir=/certs --ca-key=/certs/ca.key

# Start the cluster
docker compose -f cockroachdb-cluster.yml up -d

# Initialize the cluster (run once)
docker compose -f cockroachdb-cluster.yml up cockroach-init

# Create database and user
docker exec -it crdb-1 ./cockroach sql --certs-dir=/certs \
  --host=cockroach-1:26257 -e "
    CREATE DATABASE appdb;
    CREATE USER appuser WITH PASSWORD 'secure_password';
    GRANT ALL ON DATABASE appdb TO appuser;
  "

Access the admin UI at http://localhost:8081.

YugabyteDB — 3-Node Cluster

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# yugabytedb-cluster.yml
version: "3.9"

services:
  yb-master-1:
    image: yugabytedb/yugabyte:latest
    container_name: yb-master-1
    command: >
      /home/yugabyte/bin/yb-master
      --fs_data_dirs=/mnt/master
      --master_addresses=yb-master-1:7100,yb-master-2:7100,yb-master-3:7100
      --replication_factor=3
      --use_initial_sys_catalog_snapshot=false
    volumes:
      - yb-master-data-1:/mnt/master
    ports:
      - "7000:7000"
    networks:
      - yugabyte-net
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:7000"]
      interval: 10s
      retries: 5

  yb-master-2:
    image: yugabytedb/yugabyte:latest
    container_name: yb-master-2
    command: >
      /home/yugabyte/bin/yb-master
      --fs_data_dirs=/mnt/master
      --master_addresses=yb-master-1:7100,yb-master-2:7100,yb-master-3:7100
      --replication_factor=3
    volumes:
      - yb-master-data-2:/mnt/master
    ports:
      - "7001:7000"
    networks:
      - yugabyte-net

  yb-master-3:
    image: yugabytedb/yugabyte:latest
    container_name: yb-master-3
    command: >
      /home/yugabyte/bin/yb-master
      --fs_data_dirs=/mnt/master
      --master_addresses=yb-master-1:7100,yb-master-2:7100,yb-master-3:7100
      --replication_factor=3
    volumes:
      - yb-master-data-3:/mnt/master
    ports:
      - "7002:7000"
    networks:
      - yugabyte-net

  yb-tserver-1:
    image: yugabytedb/yugabyte:latest
    container_name: yb-tserver-1
    command: >
      /home/yugabyte/bin/yb-tserver
      --fs_data_dirs=/mnt/tserver
      --tserver_master_addrs=yb-master-1:7100,yb-master-2:7100,yb-master-3:7100
      --enable_ysql=true
    volumes:
      - yb-tserver-data-1:/mnt/tserver
    ports:
      - "5433:5433"
      - "9000:9000"
      - "6379:6379"
      - "9042:9042"
    networks:
      - yugabyte-net
    depends_on:
      yb-master-1:
        condition: service_healthy

  yb-tserver-2:
    image: yugabytedb/yugabyte:latest
    container_name: yb-tserver-2
    command: >
      /home/yugabyte/bin/yb-tserver
      --fs_data_dirs=/mnt/tserver
      --tserver_master_addrs=yb-master-1:7100,yb-master-2:7100,yb-master-3:7100
      --enable_ysql=true
    volumes:
      - yb-tserver-data-2:/mnt/tserver
    ports:
      - "5434:5433"
      - "9001:9000"
    networks:
      - yugabyte-net

  yb-tserver-3:
    image: yugabytedb/yugabyte:latest
    container_name: yb-tserver-3
    command: >
      /home/yugabyte/bin/yb-tserver
      --fs_data_dirs=/mnt/tserver
      --tserver_master_addrs=yb-master-1:7100,yb-master-2:7100,yb-master-3:7100
      --enable_ysql=true
    volumes:
      - yb-tserver-data-3:/mnt/tserver
    ports:
      - "5435:5433"
      - "9002:9000"
    networks:
      - yugabyte-net

volumes:
  yb-master-data-1:
  yb-master-data-2:
  yb-master-data-3:
  yb-tserver-data-1:
  yb-tserver-data-2:
  yb-tserver-data-3:

networks:
  yugabyte-net:
    driver: bridge

Bootstrap the YSQL API and create your database:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# Start the cluster
docker compose -f yugabytedb-cluster.yml up -d

# Wait for tservers to register (about 30 seconds)
sleep 30

# Run ysqlsh to initialize and create a database
docker exec -it yb-tserver-1 /home/yugabyte/bin/ysqlsh -h yb-tserver-1 \
  -c "
    CREATE DATABASE appdb;
    CREATE USER appuser WITH PASSWORD 'secure_password';
    GRANT ALL ON DATABASE appdb TO appuser;
  "

# Connect using standard PostgreSQL client (port 5433 is the YSQL gateway)
psql "postgresql://appuser:secure_password@localhost:5433/appdb"

YugabyteDB also exposes Redis on port 6379 and Cassandra on port 9042 from the same cluster.

TiDB — 3-Node Cluster (TiDB + TiKV + PD)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# tidb-cluster.yml
version: "3.9"

services:
  # Placement Driver - metadata and scheduling
  pd-1:
    image: pingcap/pd:latest
    container_name: pd-1
    command: >
      --name=pd-1
      --client-urls=http://0.0.0.0:2379
      --peer-urls=http://0.0.0.0:2380
      --advertise-client-urls=http://pd-1:2379
      --advertise-peer-urls=http://pd-1:2380
      --initial-cluster=pd-1=http://pd-1:2380,pd-2=http://pd-2:2380,pd-3=http://pd-3:2380
      --data-dir=/data/pd
    volumes:
      - pd-data-1:/data
    ports:
      - "2379:2379"
      - "2380:2380"
    networks:
      - tidb-net

  pd-2:
    image: pingcap/pd:latest
    container_name: pd-2
    command: >
      --name=pd-2
      --client-urls=http://0.0.0.0:2379
      --peer-urls=http://0.0.0.0:2380
      --advertise-client-urls=http://pd-2:2379
      --advertise-peer-urls=http://pd-2:2380
      --initial-cluster=pd-1=http://pd-1:2380,pd-2=http://pd-2:2380,pd-3=http://pd-3:2380
      --data-dir=/data/pd
    volumes:
      - pd-data-2:/data
    ports:
      - "2381:2380"
      - "2382:2379"
    networks:
      - tidb-net

  pd-3:
    image: pingcap/pd:latest
    container_name: pd-3
    command: >
      --name=pd-3
      --client-urls=http://0.0.0.0:2379
      --peer-urls=http://0.0.0.0:2380
      --advertise-client-urls=http://pd-3:2379
      --advertise-peer-urls=http://pd-3:2380
      --initial-cluster=pd-1=http://pd-1:2380,pd-2=http://pd-2:2380,pd-3=http://pd-3:2380
      --data-dir=/data/pd
    volumes:
      - pd-data-3:/data
    ports:
      - "2383:2380"
      - "2384:2379"
    networks:
      - tidb-net

  # TiKV - distributed storage
  tikv-1:
    image: pingcap/tikv:latest
    container_name: tikv-1
    command: >
      --addr=0.0.0.0:20160
      --advertise-addr=tikv-1:20160
      --status-addr=0.0.0.0:20180
      --data-dir=/data/tikv
      --pd=pd-1:2379,pd-2:2379,pd-3:2379
    volumes:
      - tikv-data-1:/data
    ports:
      - "20160:20160"
      - "20180:20180"
    networks:
      - tidb-net
    depends_on:
      - pd-1

  tikv-2:
    image: pingcap/tikv:latest
    container_name: tikv-2
    command: >
      --addr=0.0.0.0:20160
      --advertise-addr=tikv-2:20160
      --status-addr=0.0.0.0:20180
      --data-dir=/data/tikv
      --pd=pd-1:2379,pd-2:2379,pd-3:2379
    volumes:
      - tikv-data-2:/data
    ports:
      - "20161:20160"
      - "20181:20180"
    networks:
      - tidb-net

  tikv-3:
    image: pingcap/tikv:latest
    container_name: tikv-3
    command: >
      --addr=0.0.0.0:20160
      --advertise-addr=tikv-3:20160
      --status-addr=0.0.0.0:20180
      --data-dir=/data/tikv
      --pd=pd-1:2379,pd-2:2379,pd-3:2379
    volumes:
      - tikv-data-3:/data
    ports:
      - "20162:20160"
      - "20182:20180"
    networks:
      - tidb-net

  # TiDB - SQL compute layer (stateless, can scale independently)
  tidb-1:
    image: pingcap/tidb:latest
    container_name: tidb-1
    command: >
      --store=tikv
      --path=pd-1:2379,pd-2:2379,pd-3:2379
      --advertise-address=tidb-1
      --log-slow-query=/data/tidb-slow.log
      --status=0.0.0.0:10080
    volumes:
      - tidb-logs-1:/data
    ports:
      - "4000:4000"
      - "10080:10080"
    networks:
      - tidb-net
    depends_on:
      - tikv-1

  tidb-2:
    image: pingcap/tidb:latest
    container_name: tidb-2
    command: >
      --store=tikv
      --path=pd-1:2379,pd-2:2379,pd-3:2379
      --advertise-address=tidb-2
      --status=0.0.0.0:10080
    volumes:
      - tidb-logs-2:/data
    ports:
      - "4001:4000"
      - "10081:10080"
    networks:
      - tidb-net

  # TiFlash - columnar storage for HTAP analytics (optional)
  tiflash-1:
    image: pingcap/tiflash:latest
    container_name: tiflash-1
    command: >
      --config /data/tiflash.toml
    volumes:
      - tiflash-data-1:/data
      - ./tiflash.toml:/data/tiflash.toml:ro
    ports:
      - "3930:3930"
      - "8123:8123"
    networks:
      - tidb-net

volumes:
  pd-data-1:
  pd-data-2:
  pd-data-3:
  tikv-data-1:
  tikv-data-2:
  tikv-data-3:
  tidb-logs-1:
  tidb-logs-2:
  tiflash-data-1:

networks:
  tidb-net:
    driver: bridge

TiFlash configuration for HTAP:

1
2
3
4
5
6
7
8
9
# tiflash.toml
[flash]
service_addr = "0.0.0.0:3930"

[flash.proxy]
config = "/data/proxy.toml"

[raft]
pd_addr = "pd-1:2379,pd-2:2379,pd-3:2379"

Start the cluster and initialize:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
docker compose -f tidb-cluster.yml up -d

# Wait for TiKV nodes to register with PD (about 60 seconds)
sleep 60

# Connect via MySQL client (TiDB is MySQL-compatible on port 4000)
mysql -h 127.0.0.1 -P 4000 -u root -e "
  CREATE DATABASE appdb;
  CREATE USER 'appuser'@'%' IDENTIFIED BY 'secure_password';
  GRANT ALL ON appdb.* TO 'appuser'@'%';
  FLUSH PRIVILEGES;
"

# Connect as the application user
mysql -h 127.0.0.1 -P 4000 -u appuser -p appdb

Performance and Workload Characteristics

Each database excels at different workload patterns. Understanding these differences helps you match the tool to your use case.

Write-Heavy Workloads

CockroachDB and YugabyteDB both use per-range/per-shard Raft groups, which means write contention is localized. When multiple clients write to different key ranges, they proceed in parallel without blocking each other. However, writes to the same range will serialize through a single Raft leader.

TiKV’s Raft groups are larger (default 96 MB regions), which means fewer Raft groups overall but more data per group. The Rust-based implementation handles concurrent writes efficiently through async I/O and lock-free data structures.

For high write throughput with diverse key distribution:

  • CockroachDB: ~50,000–100,000 writes/sec on a 3-node cluster (4 vCPU, 16 GB each)
  • YugabyteDB: ~60,000–120,000 writes/sec (same hardware)
  • TiDB: ~70,000–130,000 writes/sec (same hardware)

YugabyteDB and TiDB tend to edge out CockroachDB on raw write throughput because their storage engines are more heavily optimized for sequential write patterns.

Read-Heavy Workloads

All three databases support follower reads (reading from Raft followers instead of the leader), which reduces latency for geographically distributed reads.

  • CockroachDB: SELECT ... AS OF SYSTEM TIME FOLLOWER_READ enables reading from the nearest replica with bounded staleness
  • YugabyteDB: SET yb_read_from_followers = true enables follower reads at the session level
  • TiDB: Stale reads via SELECT ... FROM table_name AS OF TIMESTAMP ... provide similar functionality

TiDB has an advantage here because the stateless SQL layer can be scaled up independently — add more TiDB servers to handle read traffic without touching storage.

Mixed OLTP + Analytics (HTAP)

If you need both transactional and analytical queries on the same data:

  • TiDB is the clear winner with TiFlash, a real columnar storage engine that maintains a Raft-based replica of your data. Analytical queries are automatically routed to TiFlash without impacting transactional performance.

  • YugabyteDB offers colocated tables (small tables stored on a single tablet) which improve join performance but don’t provide columnar analytics.

  • CockroachDB is purely OLTP-focused. You would need to replicate data to a separate analytics system (like ClickHouse) for heavy analytical workloads.


Operational Considerations

Scaling

CockroachDB: Add nodes with cockroach start --join=<existing-nodes>. The cluster automatically rebalances ranges. No manual sharding or data migration needed.

1
2
3
4
5
6
7
# Add a 4th node to an existing cluster
cockroach start \
  --store=/data/crdb-4 \
  --listen-addr=0.0.0.0:26257 \
  --http-addr=0.0.0.0:8080 \
  --join=crdb-1:26257,crdb-2:26257,crdb-3:26257 \
  --certs-dir=/certs

YugabyteDB: Add a new tserver and the masters will automatically assign tablets. Masters are added similarly with a new yb-master process.

TiDB: Scale compute by adding more TiDB server processes. Scale storage by adding more TiKV nodes. The PD service handles region scheduling automatically.

Backup and Restore

CockroachDB — full cluster backup:

1
2
3
4
5
6
cockroach dump appdb --certs-dir=/certs --host=crdb-1:26257 > backup.sql

# Or use enterprise backup to S3-compatible storage
cockroach sql --certs-dir=/certs --host=crdb-1:26257 -e "
  BACKUP DATABASE appdb INTO 's3://backup-bucket/crdb-backup?AUTH=implicit';
"

YugabyteDB — using yb-admin:

1
2
3
4
5
6
7
8
9
# Create a snapshot
docker exec yb-master-1 /home/yugabyte/bin/yb-admin \
  -master_addresses yb-master-1:7100,yb-master-2:7100,yb-master-3:7100 \
  create_snapshot appdb

# Restore from snapshot
docker exec yb-master-1 /home/yugabyte/bin/yb-admin \
  -master_addresses yb-master-1:7100,yb-master-2:7100,yb-master-3:7100 \
  restore_snapshot <snapshot_id>

TiDB — using BR (Backup & Restore tool):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Full backup to local storage
br backup full \
  --pd "pd-1:2379" \
  --storage "local:///backup/tidb-full" \
  --log-file backup.log

# Restore
br restore full \
  --pd "pd-1:2379" \
  --storage "local:///backup/tidb-full"

Monitoring

All three provide built-in dashboards:

  • **CockroachDBprometheusUI at port 8080, Prgrafanas metrics at /_status/vars, integrates with Grafana via official dashboards
  • YugabyteDB: Admin UI at port 7000 (masters) and 9000 (tservers), Prometheus-compatible /metrics endpoint
  • TiDB: Dashboard via PD at port 2379 (access via browser at http://pd-host:2379/dashboard), plus Grafana dashboards in the TiDB repository

License and Ecosystem

Licensing is the most practical differentiator for self-hosting:

  • CockroachDB uses the Business Source License (BSL 1.1), which is source-available but not open-source. You can freely use it internally, but you cannot offer CockroachDB as a managed database service to third parties. The license converts to Apache 2.0 after 3 years for each release. For most self-hosters, this is not a problem — but it matters if you’re building a product on top of it.

  • YugabyteDB uses Apache 2.0 for the core database. Fully open-source with no restrictions on commercial use or redistribution.

  • TiDB uses Apache 2.0 for all components (TiDB, TiKV, PD, TiFlash). Fully open-source with an active Chinese and global contributor base.

Ecosystem maturity:

  • CockroachDB has the longest track record (founded 2015), the most production deployments, and the deepest documentation. ORMs like SQLAlchemy, Django ORM, and Prisma work out of the box with PostgreSQL compatibility.

  • YugabyteDB has strong PostgreSQL compatibility (supports PostgreSQL 11 features) and the unique advantage of multi-API support. If you need both SQL and NoSQL (Cassandra/Redis) from the same cluster, this is the only option.

  • TiDB has the strongest MySQL compatibility, making it a drop-in replacement for MySQL applications that have outgrown a single server. The TiSpark integration also allows running Spark jobs directly against TiKV data.


Decision Guide: Which One to Choose?

Your SituationRecommendation
You want the most mature, battle-tested option with the best documentationCockroachDB
You need full Apache 2.0 licensing with zero restrictionsYugabyteDB or TiDB
You’re migrating from PostgreSQL and want maximum compatibilityCockroachDB or YugabyteDB
You’re migrating from MySQL and want drop-in compatibilityTiDB
You need HTAP (mixed OLTP + analytics on the same data)TiDB with TiFlash
You need multi-API (SQL + Cassandra + Redis from one cluster)YugabyteDB
You want to scale compute independently from storageTiDB
You need the smallest resource footprint for a homelabTiDB (TiKV is Rust, very efficient)
You want Kubernetes-native deployment with an operatorAll three have operators; TiDB’s is the most mature
You’re building a multi-region SaaS with strict complianceCockroachDB (best geo-partitioning) or YugabyteDB

For most teams starting their distributed SQL journey in 2026, CockroachDB offers the smoothest onboarding experience and the most comprehensive feature set for OLTP workloads. TiDB is the best choice if you’re coming from MySQL or need HTAP capabilities. YugabyteDB shines when you need maximum API flexibility and want a fully permissive open-source license without any BSL concerns.

All three databases can handle production traffic with proper configuration. The key is to start with a 3-node cluster, monitor your workload patterns, and scale based on actual metrics rather than theoretical capacity.

Frequently Asked Questions (FAQ)

Which one should I choose in 2026?

The best choice depends on your specific requirements:

  • For beginners: Start with the simplest option that covers your core use case
  • For production: Choose the solution with the most active community and documentation
  • For teams: Look for collaboration features and user management
  • For privacy: Prefer fully open-source, self-hosted options with no telemetry

Refer to the comparison table above for detailed feature breakdowns.

Can I migrate between these tools?

Most tools support data import/export. Always:

  1. Backup your current data
  2. Test the migration on a staging environment
  3. Check official migration guides in the documentation

Are there free versions available?

All tools in this guide offer free, open-source editions. Some also provide paid plans with additional features, priority support, or managed hosting.

How do I get started?

  1. Review the comparison table to identify your requirements
  2. Visit the official documentation (links provided above)
  3. Start with a Docker Compose setup for easy testing
  4. Join the community forums for troubleshooting
Advertise here