CockroachDB monitoring checklist: the signals every production cluster needs

CockroachDB layers SQL execution on a replicated KV store backed by Pebble LSM trees, Raft consensus, and MVCC concurrency control. Each subsystem has distinct failure modes, and interactions between them create cascades that single-signal monitoring cannot catch. A cluster can show healthy CPU, adequate disk space, and sub-millisecond SQL latency while L0 sublevels climb toward write stalls or clock offset drifts toward the self-termination threshold.

This checklist defines four monitoring maturity levels (survival, operational, mature, expert) and maps every production signal to its level. Use it to audit current coverage, identify gaps before they cause incidents, and plan what to add as your cluster grows.

The four monitoring levels

The levels are cumulative. Each builds on the one below it.

flowchart BT
    L1["Level 1: Survival
5 signals: is it broken?"] L2["Level 2: Operational
16 more: is it healthy?"] L3["Level 3: Mature
20 more: leading indicators"] L4["Level 4: Expert
18 more: deep internals"] L1 --> L2 --> L3 --> L4
  • Survival: is the cluster fundamentally broken? If you have nothing else, have these.
  • Operational: is the cluster healthy, not just alive? A team running CockroachDB in production should have all of these.
  • Mature: leading indicators and storage internals that give 10 to 30 minutes of warning before degradation becomes an outage.
  • Expert: deep internals and per-range analysis. These are the signals operators add after repeated incidents, when a failure mode was invisible until too late.

Level 1: survival

SignalMetric / sourceWhat it tells youAlert posture
Node liveness/_status/nodes, /health?ready=1Whether the cluster considers each node alive. Loss triggers lease redistribution.PAGE: unexpected not-live transition sustained over 5 min, node not draining
Range unavailabilityranges_unavailableRanges with no leaseholder or lost Raft quorum. Any nonzero value means some keyspace is unreadable or unwritable.PAGE: any nonzero value sustained over 5 min
Disk spacecapacity_availableFree space per store. Below 20 percent, compaction may fail and trigger a storage death spiral.PAGE: under 10 percent available and decreasing. TICKET: under 20 percent
SQL connectivitySynthetic probe: cockroach sql -e "SELECT 1"Whether clients can connect and execute queries through the full pgwire path (TCP, TLS, SQL execution).PAGE: probe failure sustained over 2 min
Certificate expirationcockroach cert list, openssl x509 -enddateTime until TLS certificates expire. Expiry breaks inter-node communication or client access.TICKET: under 30 days. PAGE: expired or under 24 hours with no replacement staged

Use /health?ready=1 for load balancer health checks. It returns 503 when a node is draining or quorum is lost. Plain TCP checks will route traffic to functionally impaired nodes.

The liveness mechanism is evolving across versions, transitioning toward store-level liveness and lease-based failure detection. Check the release notes for your deployment version to understand the exact heartbeat and expiry behavior.

Level 2: operational

Everything in Level 1, plus:

SignalMetric / sourceWhat it tells you
SQL statement latency (P50, P99)sql_service_latencyClient-visible query performance. P99 is where SLO violations live.
Transaction throughputsql_select_count, sql_insert_count, sql_update_count, sql_delete_countWorkload volume and composition. Sudden drops need correlation with error rate to distinguish traffic decrease from system failure.
Transaction restart/retry ratetxn_restarts (by cause)Contention level. writetooold means schema contention, readwithinuncertainty means clock skew, txnpush means application conflicts. Each requires a different response.
SQL error ratesql_failure_countActive failures visible to applications. Watch for XX000 (internal errors) and 53200 (resource exhaustion).
CPU utilizationsys_cpu_user_ns, sys_cpu_sys_nsCapacity headroom. Per-range Raft ticking is a hidden CPU multiplier: 50,000 ranges costs significantly more CPU than 5,000 at identical query load.
Memory RSSsys_rssProximity to memory limits. CGo allocations (Pebble block cache) grow independently of the Go heap.
LSM L0 sublevel countstorage_l0_sublevelsThe single most predictive storage signal. 0 to 5 is healthy. 10 to 20 means active degradation. Over 20 means write stalls are imminent or active.
Active client connectionssql_connsConnection pressure. Each connection consumes a goroutine and memory. Over 1,000 to 2,000 per node suggests client pooling is misconfigured.
Under-replicated range countranges_underreplicatedReplication safety margin. Sustained under-replication means the cluster cannot heal fast enough.
Clock offsetclock_offset_meannanosTime sync health. Over 80 percent of --max-offset (default 500ms, so over 400ms) triggers self-termination. Even steady 200ms offset doubles the uncertainty interval and causes persistent read restarts.
Inter-node RPC latencyround_trip_latencyNetwork health as CockroachDB experiences it. Includes kernel scheduling delay, so CPU-saturated nodes show elevated RPC latency even with a healthy network.
Disk I/O utilization and latencyOS: iostat -xz 1, /proc/diskstatsWhether storage is the bottleneck. On cloud volumes, watch against provisioned IOPS caps, not just percentage utilization.
WAL fsync latencystorage_wal_fsync_latencyMost direct measure of write-path health. WAL fsync is on the critical path of every write. Over 50ms on SSDs signals I/O saturation.
Job statuscrdb_internal.jobs, per-type Prometheus metricsWhether backups, schema changes, and other long-running jobs are progressing or stuck. Track backup duration trend, not just success.
Admission control queue depthadmission.wait_durations.*, admission.io.overloadWhether internal flow control is throttling. The store-write queue begins shaping traffic at approximately 5 L0 sublevels.
Node uptimesys_uptimeCold-start gating signal. Suppress most performance alerts during the first 10 minutes after process start. Block cache warmup takes 10 to 30 minutes.

Level 3: mature

Everything in Level 2, plus leading indicators:

SignalMetric / sourceWhat it tells you
LSM read amplificationrocksdb_read_amplificationSSTable files consulted per read. Name retained from RocksDB era. 10 to 15 is normal. Over 25 indicates compaction debt.
Compaction throughput and backlogrocksdb_compactions, storage_marked_for_compaction_files, storage_l0_num_filesWhether background storage maintenance keeps pace with writes. Upward backlog trend over hours means approaching write capacity.
Write stall countstorage_write_stallsPebble refusing writes because LSM state is unsafe. Rate over 1 per second sustained for over 1 minute means foreground writes are materially impaired.
Pebble block cache hit raterocksdb_block_cache_hits, rocksdb_block_cache_missesCache efficiency. Drop below 90 percent impacts read-heavy workloads. Sudden drop after restart is expected (cold cache).
SQL memory budget utilizationsql_mem_root_currentMemory available for query execution. Over 90 percent risks error 53200. Per-node, not per-query: one large query can starve all others.
Go GC pause duration and frequencysys_gc_pause_ns, sys_gc_countPauses approaching the liveness heartbeat interval risk node death. GC CPU over 15 percent indicates memory pressure.
Goroutine countsys_goroutinesConcurrency level. Monotonic growth without workload increase means leak. Over 100,000 is critical.
Range count per noderanges, leases_countPer-node overhead from Raft ticking. Over 50,000 ranges per node creates significant CPU cost independent of query load.
Raft snapshot raterange_snapshots_generated, range_snapshots_applied_initialReplication health. High rate beyond rebalancing means nodes cannot keep up with log application. Each snapshot transfers up to 512 MiB.
Lease transfer rateleases_transfers_successSystem churn. Each transfer creates a brief unavailability window. Elevated rate without operational cause indicates instability.
Intent count and bytesintentcount, intentbytesUnresolved write intents from uncommitted transactions. Growing count means abandoned transactions or intent resolution falling behind.
MVCC garbage bytesMVCC metrics via /_status/varsDead data awaiting GC. Grows silently and eventually consumes disk. Protected timestamps from CDC or backups can block GC entirely.
KV read/write latencyexec_latencyStorage layer latency isolated from SQL planning overhead. Rising without SQL changes means storage degradation.
Raft log commit latencyraft.process.logcommit.latencyWAL fsync path specifically. Over 50ms on SSDs is a strong I/O saturation signal.
Changefeed lag (if using CDC)changefeed_max_behind_nanosCDC consumer freshness. A stalled changefeed creates protected timestamps that silently prevent MVCC GC, causing unbounded disk growth.
Protected timestamp count and agespanconfig_kvsubscriber_protected_record_count, jobs_changefeed_protected_age_secRecords preventing MVCC GC. Age over 24 hours or count growing without active backup or CDC means a stalled operation is blocking cleanup.
File descriptor usagesys_fd_open, /proc/<pid>/fdFD exhaustion prevents new connections and SSTable opens. Production should have ulimit of at least 35,000 to 65,000.
Composite pattern alertingMulti-signal correlationsDetects cascades that individual thresholds miss. Example: L0 sublevels climbing plus write stalls plus lease transfers equals a compaction death spiral.
Per-table and per-index statisticscrdb_internal queries (diagnostic use only)Size, row count, and query patterns for capacity planning. Do not use crdb_internal tables for automated monitoring pipelines; they are unsupported and may change between versions.
Network throughput between nodesOS: /proc/net/devInter-node bandwidth saturation. CockroachDB multiplexes all traffic on a single port, so Raft heartbeats cannot be prioritized over data transfer.

Level 4: expert

Everything in Level 3, plus signals operators add after repeated incidents:

SignalWhat it tells you
Raft proposal drop rateDropped proposals mean silently lost writes (retried, but immediate latency impact). Rarely watched.
Per-range request rate distributionDetects hot ranges before they become obvious from cluster-level metrics. A range at 10x average QPS bottlenecks its leaseholder while other nodes idle.
Intent resolution throughputSeparate from intent count. Tells you whether cleanup is keeping pace during an intent accumulation cascade.
Lease preference violation countWhether the allocator can satisfy zone config lease preferences. Critical for multi-region latency SLOs.
Raft entry cache hit rateCache misses cause disk reads for log entries, a hidden latency source.
Compaction debt by LSM levelShows where in the LSM tree pressure is building, not just the aggregate.
Goroutine profiling (debug/pprof)Stack traces for all goroutines. Detects where goroutines are stuck (I/O, lock contention) before count becomes critical.
KV write batch size distributionChanges in write patterns that precede L0 pressure or compaction issues.
SQL plan cache hit rateCache misses mean re-planning, which is CPU-intensive.
Cross-range transaction percentageHigher percentage means more distributed coordination overhead and latency.
Time-series data retention pressureCockroachDB’s internal time-series data can itself become a storage burden on large clusters.
Node decommission progress rateEnsures decommissions complete before patience runs out. Stalled decommissions block maintenance.
Admission control token exhaustion rateHow frequently work is being delayed, broken down by queue type. Foreground queue exhaustion is more concerning than elastic queue.
Closed timestamp lagAffects follower read freshness in multi-region setups.
Queue processor error countsSplit, merge, replicate, and GC queue failures indicate internal scheduling problems.
Disk stall detection metricsstorage_disk_stalled (nonzero means the node may self-terminate) and storage_disk_slow (incrementing means slow disk operations).
Non-gateway SQL activityConnections arriving at nodes not designated as application gateways. May indicate unauthorized direct access or misconfigured routing.
Bulk data export activityUnexpected EXPORT or BACKUP operations. May indicate data exfiltration.

How to alert on these signals

Not every signal should trigger a page. The playbook distinguishes PAGE (wake someone up) from TICKET (investigate during business hours).

Gate on sustained duration. Most conditions require sustained presence (typically over 5 minutes) before alerting. Transient spikes from range splits, lease transfers, or brief I/O contention are normal operating noise.

Gate on node uptime. Suppress most performance alerts during the first 10 minutes after process start (sys_uptime). Cache warmup takes 10 to 30 minutes. Statistics collection after restart may produce suboptimal query plans temporarily.

Binary signals page immediately. ranges_unavailable is zero under normal conditions. Any nonzero value sustained over 5 minutes is live user impact. storage_disk_stalled is similarly binary: nonzero means the node may self-terminate.

Workload-shaped signals require context. CPU at 80 percent alone is not page-worthy. CPU at 80 percent plus liveness flapping plus elevated lease transfers is a cascade. Latency thresholds are workload-dependent: establish baselines per statement fingerprint and alert on deviation (over 2x rolling P99 is a reasonable starting point), not on absolute values.

Distinguish restart causes. Alarm on txn_restarts breakdown, not the aggregate. readwithinuncertainty appearing for the first time is a clock problem. writetooold climbing is a schema or contention problem. txnpush spiking is an application transaction design problem. Each routes to a different team.

Treat recovery activity with suspicion. Snapshot and rebalance storms during cluster healing compete with foreground traffic. Recovery I/O can degrade the cluster further. Monitor recovery rate and foreground latency together.

What most teams miss

These gaps appear most often in incident reviews:

  • L0 sublevel count uninstrumented until write stalls. The most common and damaging gap. L0 gives 10 to 30 minutes of warning before stalls, and teams consistently waste that window because the metric was never collected.
  • Clock offset not monitored proactively. NTP is treated as set-and-forget. The first sign of trouble is a node self-terminating at 3 a.m. Meanwhile, readwithinuncertainty restarts have been degrading tail latency for days.
  • Aggregate latency instead of per-statement-fingerprint latency. A new slow query among fast queries barely moves P99 but kills specific endpoints. Per-fingerprint tracking catches plan regressions and missing indexes.
  • Transaction restart causes not distinguished. Total retry rate is alarmed without breakdown, wasting diagnosis time because the three main causes need completely different responses.
  • MVCC garbage and protected timestamps unmonitored. Data gets deleted but nobody checks whether GC reclaims space. Protected timestamps from stalled CDC or hung backups silently prevent GC. Disk fills with dead data.
  • Range count not tracked as a scaling dimension. Operators monitor data volume and query throughput but miss that 200,000 ranges per node behaves fundamentally differently from 20,000 because of per-range Raft processing overhead.
  • TCP health checks instead of /health?ready=1. Load balancers route traffic to draining, write-stalled, or GC-thrashing nodes because the TCP check succeeds.
  • Backup duration trend not tracked. Backup succeeds so the check is green. But duration grew from 20 minutes to 3 hours. Eventually it exceeds the backup interval, creating overlapping backups or gaps.
  • Admission control ignored as a capacity signal. Regular AC queuing means zero burst headroom. The system is maintaining throughput by adding latency, not by having spare capacity.
  • Cluster averages trusted over per-node data. One hot leaseholder or overloaded region hides under healthy global metrics. Always monitor per-node.

How Netdata helps

  • Per-second collection from CockroachDB’s /_status/vars endpoint means brief write stalls and lease transfers are visible, not hidden by 15 to 30 second scrape gaps.
  • ML anomaly detection on storage_l0_sublevels, read amplification, and write stalls catches slow drift toward compaction death spirals before static thresholds fire.
  • Cross-signal correlation links clock offset spikes to readwithinuncertainty restart rates, or L0 growth to admission control queue depth, so you see the cascade instead of isolated symptoms.
  • Per-node dashboards prevent hot ranges and single-node degradation from hiding under cluster averages.
  • Cold-start awareness uses sys_uptime to suppress false positives during the warmup window after node restarts.

Netdata’s CockroachDB monitoring with Netdata brings these signals together with per-second metrics and ML anomaly detection.