+1 (740) 926-6856

Backups You Have Actually Restored: A Cassandra Recovery Runbook

Two questions we ask during a cluster review get the same nervous answer. When did you last restore from a backup? and how long would a full-cluster restore take? Most teams can point at a snapshot job. Far fewer can point at a restore anyone has run since the cluster was built.

Cassandra makes this easy to defer. Replication factor 3 across two datacenters feels like durability, and for hardware failure it is. It is not backup. Replication faithfully copies a TRUNCATE you did not mean, a bad migration script, and an application bug that overwrites a million rows with nulls. Backups exist for the failures that replicate.

This is the scheme we install, and the restores we make teams rehearse before we sign off on it.

What Cassandra gives you

Three mechanisms, and they solve different problems.

Snapshots (nodetool snapshot -t <tag> <keyspace>) flush memtables and create hard links to the current SSTables in <data_dir>/<keyspace>/<table>/snapshots/<tag>/. They are instant and, at creation, nearly free in disk terms — hard links, not copies. They stop being free as compaction rewrites the live SSTables and the snapshot keeps the old files pinned. A snapshot you forget to clear (nodetool clearsnapshot) is one of the more common causes of a node quietly filling its data disk.

Incremental backups (incremental_backups: true) hard-link every newly flushed SSTable into a backups/ directory. They give you the deltas between snapshots, and Cassandra never cleans them up. You own that lifecycle.

Commit-log archiving (commitlog_archiving.properties) copies each commit log segment as it is retired, which is what makes point-in-time recovery between snapshots possible at all. It costs a per-segment copy and a lot of storage discipline. Most teams do not need it; teams with a strict RPO do.

All three produce files on the node. None of them is a backup until those files are somewhere the node is not. A snapshot sitting on the same EBS volume as the data it protects survives operator error and nothing else.

The part everyone forgets: schema and topology

SSTables are useless without the table definitions that shaped them. Capture, alongside every snapshot:

  • cqlsh -e "DESCRIBE SCHEMA" for the full CQL schema, including UDTs, which restores fail on with unhelpful errors when missing.
  • The token assignments per node (nodetool ring, or the initial_token list). A restore that puts data on nodes with different tokens is a restore that silently loses rows unless you repair the whole cluster afterwards.
  • cassandra.yaml and the JVM options actually in effect, not the ones in the config repo.
  • The Cassandra version. SSTable formats move forward, not backward. Restoring 4.x-era files onto 5.0 is supported; the reverse is not.

We write these into the same object-storage prefix as the SSTables, under the same snapshot tag. If the runbook requires someone to find four artifacts in three systems at 3 a.m., the runbook does not work.

A scheme that fits most clusters

For a typical production keyspace on a cluster of 6 to 30 nodes:

  1. Nightly snapshot, staggered across nodes so the flush and the upload do not hit every node in a replica set at once.
  2. Upload to object storage immediately, with server-side encryption and a lifecycle policy: 7 daily, 4 weekly, 3 to 12 monthly, chosen against your actual compliance requirement rather than a default.
  3. Clear the local snapshot once the upload is verified. Verified means the object listing matches the local file list and byte counts, not that the upload command exited zero.
  4. Incremental backups on if your RPO is tighter than 24 hours, with the same upload-and-prune loop running more frequently.
  5. Commit-log archiving only if you need sub-hour RPO, and only after you have tested a point-in-time restore end to end.

Tools worth using rather than rebuilding: Medusa (from the Reaper family) handles snapshot, upload, and cluster-wide restore orchestration for self-managed clusters; cass-operator and K8ssandra wire the same primitives into Kubernetes. DSE has OpsCenter Backup Service. Astra DB takes this off your plate entirely — which is a legitimate reason to consider it, and one we weigh honestly when a team's real problem is that nobody owns backups.

Whatever you use, understand what it does under the hood. Every one of these is nodetool snapshot plus file movement plus bookkeeping.

Four restores worth rehearsing

Backups are not a system until you have run these. Each one has a different procedure and a very different clock.

1. Single-node loss

Usually you do not restore. You replace: bring up a new node with -Dcassandra.replace_address_first_boot, let streaming rebuild it from the surviving replicas, then repair. This is faster and safer than a file-level restore, and it is the scenario your RF is designed for. Know your streaming throughput — a 2 TB node at 100 MB/s is roughly six hours, and that number belongs in your capacity plan, not in a postmortem.

2. Accidental truncate or drop of one table

The common case, and the one most people get wrong under pressure. TRUNCATE in recent versions takes an automatic snapshot before it runs (auto_snapshot: true); check for it before you reach for object storage, because it is sitting on the nodes right now and will save you an hour.

The procedure: recreate the table with the exact original schema, copy the snapshot SSTables into the table directory on each node, then nodetool refresh <keyspace> <table>. Do it node by node, and repair after. sstableloader is the alternative when node and token topology changed; it is slower but it re-distributes rows correctly, which matters more than speed if you are not restoring onto identical tokens.

3. Logical corruption discovered late

A bad deploy wrote garbage for six hours and you found it on Tuesday. This is where point-in-time recovery earns its cost, and where most teams discover they cannot do it. Without commit-log archiving your options are the last clean snapshot — losing everything since — or a targeted repair of affected partitions from an application-side source of truth.

Decide in advance which you are buying. "We would figure it out" is not an RPO.

4. Full cluster loss

Region gone, or a control-plane mistake that took the whole ring. Restore the schema first, on a cluster of the same size with the same tokens, then place SSTables per node and refresh. The number that matters is not backup size, it is download bandwidth: 20 TB pulled from object storage at 500 MB/s per node in parallel is hours, and the same 20 TB through one bottlenecked path is days.

Measure it once, on real data, and write the measured number in the runbook. An RTO you have never timed is a guess.

The tests that keep it honest

A quarterly restore rehearsal into a scratch cluster, on production-shaped data, with the runbook followed literally by someone who did not write it. Time it. Note every step that was wrong or missing — there are always three — and fix them the same week.

Between rehearsals, alert on the boring things: backup job age per node, object count and byte delta versus the previous run (a sudden 90% drop means an empty upload succeeded), local snapshot directory size, and disk headroom on every node. A backup system that fails loudly is worth more than one that runs perfectly and silently until the day it does not.

The short version

Replication protects against hardware. Backups protect against people and software. Snapshots plus off-node storage plus captured schema and tokens is the minimum; commit-log archiving is the upgrade you buy only when your RPO demands it. And none of it counts until someone has restored from it on a clock.

If you want a second pair of eyes on a backup scheme — or you need a restore rehearsed properly before an auditor asks — that is hourly work we take. Tell us the cluster size, the data footprint, and the RPO and RTO you have promised.