Run Django on CockroachDB
Run Django on CockroachDB with the django-cockroachdb backend: match backend and Django versions, configure TLS connections, handle transaction retries, adjust migrations and primary keys, and understand what works differently from PostgreSQL.
Problem statement
You need a database that survives the loss of a node, zone, or region without manual failover, or that serves users in several regions with local reads. CockroachDB is a distributed SQL database that speaks the PostgreSQL wire protocol. Django can use it through the django-cockroachdb backend, but it is not a drop-in PostgreSQL: transactions are serializable and may need retries, some PostgreSQL features are missing or different, and schema changes run online in the background.
- Install
django-cockroachdbmatching your Django version, and setENGINE = "django_cockroachdb". - Connect over TLS to port 26257 (CockroachDB Cloud provides a connection string and CA certificate).
- Expect serialization errors (SQLSTATE
40001) under contention and retry whole transactions. - Prefer UUID primary keys or accept CockroachDB's
unique_rowid()behavior for integer keys. - Test your migrations and queries against CockroachDB in CI; some PostgreSQL-specific features are unsupported.
Step-by-step solution
1. Install and configure the backend
pip install django-cockroachdb psycopg[binary]
DATABASES = {
"default": {
"ENGINE": "django_cockroachdb",
"NAME": "shop",
"USER": "shopapp",
"PASSWORD": os.environ["DB_PASSWORD"],
"HOST": "shop-cluster.aws-eu-central-1.cockroachlabs.cloud",
"PORT": "26257",
"OPTIONS": {"sslmode": "verify-full", "sslrootcert": "/etc/ssl/crdb/root.crt"},
"CONN_MAX_AGE": 60,
}
}
The backend's major version follows Django's (for example, django-cockroachdb 5.2.x for Django 5.2). Using mismatched versions causes errors at startup.
2. Handle transaction retries
CockroachDB runs transactions at SERIALIZABLE isolation. When concurrent transactions conflict, one fails with:
psycopg.errors.SerializationFailure: restart transaction: TransactionRetryWithProtoRefreshError
Retry the whole unit of work:
import time
from django.db import OperationalError, transaction
def run_in_txn(fn, attempts=5):
for i in range(attempts):
try:
with transaction.atomic():
return fn()
except OperationalError as exc:
if getattr(exc.__cause__, "sqlstate", None) != "40001" or i == attempts - 1:
raise
time.sleep(0.05 * 2 ** i)
Keep transactions short and avoid hot rows (counters updated by every request), which cause the most retries.
3. Primary keys and ordering
Integer AutoField columns use unique_rowid() (or identity columns, depending on settings), producing large, non-sequential values. Code that assumes IDs reflect insertion order breaks; order by a timestamp instead. Sequential keys also concentrate writes on one range; UUID primary keys spread them:
import uuid
class Order(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
4. Migrations and schema changes
Schema changes in CockroachDB run as background jobs while the table stays available. Django migrations work, but DDL inside a transaction with other statements has limitations; the backend handles common cases. Some operations behave differently, such as altering column types. Run the full migration history against CockroachDB in CI before switching.
5. Know the unsupported features
Check the backend's documentation for its current list. Typical gaps include some PostgreSQL extensions, full-text search features (django.contrib.postgres.search lookups behave differently or are unsupported), and certain index types. Test the parts of django.contrib.postgres you rely on.
Explanation
CockroachDB splits data into ranges replicated with the Raft consensus protocol across nodes. Every write needs agreement from a majority of replicas, which is what lets the cluster survive failures without losing committed data. To keep that correct under concurrency, it uses serializable isolation and resolves conflicts by asking clients to retry. Django's PostgreSQL-based backend works with these semantics once retries are handled and PostgreSQL-only features are avoided.
Edge cases / notes
- Latency. Writes involve a consensus round; multi-region writes can take tens of milliseconds more than local PostgreSQL.
- Connection pooling. Connections are cheaper than in PostgreSQL, but pooling still helps with many workers.
- Licensing. CockroachDB's licensing has changed over time; check the current terms for self-hosted clusters.
Next steps
- Compare with standard setups in Connect Django to PostgreSQL in Production.
- Plan multi-region architecture with Django Multi-Region Deployment.
- Understand lock conflicts in PostgreSQL with Fix PostgreSQL Deadlock Detected in Django.
FAQ
Does Django support CockroachDB?
Through the third-party django-cockroachdb backend, maintained to track Django releases.
Can I use the standard PostgreSQL backend?
Not reliably. Use the dedicated backend, which handles CockroachDB's differences.
Why do I get serialization errors?
Concurrent transactions conflicted under serializable isolation. Retry the whole transaction.
Are auto-increment IDs sequential on CockroachDB?
No. They are unique but not sequential. Order records by timestamps.