Cassandra Maintenance Guide
All Cassandra maintenance happens online. Need to restart a node? Drain it first, and traffic routes to other replicas. Need to change configuration? Roll through nodes one at a time. Need to upgrade versions? Same approach—one node restarts while the others handle requests.
The pattern is always the same: take one node out of rotation, do the work, bring it back, verify it is healthy, then move to the next. With replication factor 3 and LOCAL_QUORUM consistency, losing one node temporarily does not affect availability.
This guide covers routine maintenance tasks: repair scheduling, compaction monitoring, disk management, and rolling restarts.
Maintenance Philosophy
Section titled “Maintenance Philosophy”Cassandra is designed for continuous operation. All maintenance procedures can be performed without cluster downtime using rolling operations—apply changes to one node at a time while the rest of the cluster serves traffic.
Rolling Operation Pattern:─────────────────────────────────────────────────────────────────────────────For each node in cluster: 1. Drain node (stop accepting writes, flush data) 2. Perform maintenance (restart, upgrade, etc.) 3. Wait for node to rejoin and stabilize 4. Verify cluster health 5. Proceed to next node
Avoid: Performing maintenance on multiple nodes simultaneously (exception: nodes in different failure domains with RF > 2)Maintenance Task Reference
Section titled “Maintenance Task Reference”| Task | Frequency | Impact | When to Perform |
|---|---|---|---|
| Repair | Weekly to daily | Medium | Off-peak hours |
| Cleanup | After topology changes | Medium | After adding/removing nodes |
| Rolling restart | As needed | Low | Configuration changes |
| Compaction tuning | Ongoing | None | When metrics indicate issues |
| Schema changes | As needed | Low | Any time (for most changes) |
| Upgrades | Quarterly | High | Planned maintenance window |
| Log rotation | Daily | None | Automated |
| Backup verification | Weekly to monthly | None | Scheduled |
Rolling Restart
Section titled “Rolling Restart”When to Perform
Section titled “When to Perform”- After changing
cassandra.yamlconfiguration - After changing JVM options
- To apply OS patches
- To clear memory/restart after issues
- As part of upgrade process
Procedure
Section titled “Procedure”#!/bin/bash# rolling_restart.sh - Run on each node sequentially
set -e
HOSTNAME=$(hostname -f)echo "=== Starting rolling restart on ${HOSTNAME} ==="
# 1. Check cluster health before proceedingecho "Checking cluster health..."STATUS=$(nodetool status)if echo "${STATUS}" | grep -E "^D[NLM]"; then echo "ERROR: Down nodes detected. Fix before proceeding." exit 1fi
# Check for pending operationsPENDING=$(nodetool compactionstats | grep "pending tasks" | awk '{print $3}' || echo "0")if [ "${PENDING:-0}" -gt 20 ]; then echo "WARNING: ${PENDING} pending compactions. Consider waiting."fi
# 2. Drain the nodeecho "Draining node..."nodetool drain
# drain does:# - Stops accepting new connections# - Flushes all memtables to disk# - Stops listening on native protocol port
# 3. Stop Cassandraecho "Stopping Cassandra..."sudo systemctl stop cassandra
# 4. Apply changes (configuration, JVM options, etc.)# --- Your changes here ---
# 5. Start Cassandraecho "Starting Cassandra..."sudo systemctl start cassandra
# 6. Wait for node to come upecho "Waiting for node to join cluster..."MAX_WAIT=300 # 5 minutesWAITED=0
while true; do if nodetool status 2>/dev/null | grep -E "^UN.*${HOSTNAME}" > /dev/null; then echo "Node is up and normal (UN)" break fi
if [ ${WAITED} -ge ${MAX_WAIT} ]; then echo "ERROR: Node did not come up within ${MAX_WAIT} seconds" exit 1 fi
sleep 10 WAITED=$((WAITED + 10)) echo "Waiting... (${WAITED}s)"done
# 7. Wait for streaming to complete (if any)echo "Checking for streaming..."while nodetool netstats | grep -q "Receiving\|Sending"; do echo "Streaming in progress, waiting..." sleep 30done
# 8. Final health checkecho "Final health check..."nodetool statusnodetool tpstats | head -15
echo "=== Rolling restart complete on ${HOSTNAME} ==="echo "Wait at least 2 minutes before proceeding to next node"Important Notes
Section titled “Important Notes”┌─────────────────────────────────────────────────────────────────────────────┐│ DO NOT skip the wait between nodes! ││ ││ Why: ││ - Node needs time to stabilize and warm caches ││ - Gossip needs to propagate new state ││ - Pending hints need to be delivered ││ - Compaction from flush needs to complete ││ ││ Minimum wait: 2 minutes for small clusters, 5+ minutes for large clusters │├─────────────────────────────────────────────────────────────────────────────┤│ Watch for restarting too quickly: ││ - Hints piling up ││ - Increased read latency ││ - Temporary consistency issues │└─────────────────────────────────────────────────────────────────────────────┘Cleanup
Section titled “Cleanup”What Cleanup Does
Section titled “What Cleanup Does”After topology changes (adding/removing nodes), data is redistributed. Nodes that no longer own certain token ranges keep the old data. Cleanup removes this orphaned data.
Before cleanup:─────────────────────────────────────────────────────────────────────────────Original 3-node cluster: Each node owns ~33% of data
After adding 4th node:- Each node now owns ~25% of data- But original 3 nodes still have ~33% of data on disk- Extra 8% per node is orphaned
Cleanup removes the orphaned 8%, freeing disk spaceWhen to Run
Section titled “When to Run”Run cleanup AFTER:✓ Adding nodes (and bootstrap completes)✓ Changing keyspace replication factor✓ Restoring from backup to different topology
Do NOT run cleanup:✗ Before repair completes (may lose not-yet-repaired data)✗ During another maintenance operation✗ When cluster is under heavy loadProcedure
Section titled “Procedure”#!/bin/bash# cleanup.sh - Run on each EXISTING node after adding new nodes
KEYSPACE=$1
if [ -z "$KEYSPACE" ]; then echo "Usage: cleanup.sh <keyspace>" exit 1fi
echo "Starting cleanup for keyspace: ${KEYSPACE}"echo "This may take a while for large keyspaces..."
# Check disk space before cleanupDISK_BEFORE=$(df -h /var/lib/cassandra | tail -1)echo "Disk before: ${DISK_BEFORE}"
# Run cleanup# Cleanup is CPU and I/O intensivenodetool cleanup ${KEYSPACE}
# Check disk space afterDISK_AFTER=$(df -h /var/lib/cassandra | tail -1)echo "Disk after: ${DISK_AFTER}"
echo "Cleanup complete"Cleanup for All Keyspaces
Section titled “Cleanup for All Keyspaces”# List all keyspaces and run cleanupcqlsh -e "DESC KEYSPACES" | tr ' ' '\n' | grep -v "^$\|system" | while read ks; do echo "Cleaning up keyspace: ${ks}" nodetool cleanup ${ks}doneThrottling Cleanup
Section titled “Throttling Cleanup”# Reduce cleanup impact on production traffic# Set compaction throughput (cleanup uses compaction resources)nodetool setcompactionthroughput 32 # MB/s (default is 64)
# Run cleanupnodetool cleanup my_keyspace
# Reset to defaultnodetool setcompactionthroughput 64Compaction Management
Section titled “Compaction Management”Understanding Compaction
Section titled “Understanding Compaction”Compaction merges SSTables to:
- Reclaim space from overwrites and deletes
- Reduce number of SSTables to read
- Remove expired data (TTL)
- Purge tombstones (after gc_grace_seconds)
Monitoring Compaction
Section titled “Monitoring Compaction”# Current compaction activitynodetool compactionstats
# Example output:# pending tasks: 15# - keyspace.table: 3# - keyspace.table2: 12## id compaction type keyspace table completed total unit progress# c91a-... COMPACTION my_ks my_table 1234567 9876543 bytes 12.5%
# Historical compaction statisticsnodetool compactionhistory
# Table-level compaction metricsnodetool tablestats my_keyspace.my_table | grep -i compactCompaction Thresholds
Section titled “Compaction Thresholds”| Metric | Normal | Warning | Action |
|---|---|---|---|
| Pending tasks | 0-5 | 20-50 | Investigate if sustained |
| Pending tasks | 50+ | 100+ | Increase throughput or add resources |
| SSTable count per table | < 20 | 50+ | May need compaction strategy change |
Tuning Compaction
Section titled “Tuning Compaction”# Increase compaction throughput (default 64 MB/s)nodetool setcompactionthroughput 128
# Decrease (to reduce impact on reads)nodetool setcompactionthroughput 32
# View current settingnodetool getcompactionthroughput
# Set concurrent compactors (default based on CPUs)# Edit cassandra.yaml: concurrent_compactors: 4Forcing Compaction
Section titled “Forcing Compaction”Use sparingly—forced compaction creates large SSTables that must be recompacted later:
# Force compaction on specific tablenodetool compact my_keyspace my_table
# Force major compaction (creates single SSTable - usually BAD!)nodetool compact my_keyspace my_table --user-defined
# Better: Upgrade SSTables after Cassandra upgradenodetool upgradesstables my_keyspace my_tableStopping Compaction
Section titled “Stopping Compaction”In emergencies only:
# Stop all compactionsnodetool stop COMPACTION
# Check that compactions stoppednodetool compactionstats
# Compactions will resume automatically# To re-enable immediately:nodetool enableautocompaction my_keyspace my_tableSchema Management
Section titled “Schema Management”Schema Best Practices
Section titled “Schema Best Practices”DO:✓ Make schema changes from a single node✓ Wait for schema agreement before next change✓ Test schema changes in staging first✓ Back up schema before major changes
DON'T:✗ Make schema changes from multiple nodes simultaneously✗ Drop columns without verifying they're not in use✗ Change primary keys (requires table recreation)✗ Make schema changes during high loadChecking Schema Agreement
Section titled “Checking Schema Agreement”# All nodes should show same schema versionnodetool describecluster | grep -A10 "Schema versions"
# Output should show single version with all IPs:# Schema versions:# abc123-def456 [10.0.0.1, 10.0.0.2, 10.0.0.3]
# Multiple versions = disagreement:# abc123-def456 [10.0.0.1, 10.0.0.2]# xyz789-... [10.0.0.3] ← Problem!Fixing Schema Disagreement
Section titled “Fixing Schema Disagreement”# Option 1: Wait (usually resolves within minutes)sleep 60 && nodetool describecluster | grep -A10 "Schema versions"
# Option 2: Restart affected node# (node with different schema version)ssh affected_node "nodetool drain && sudo systemctl restart cassandra"
# Option 3: Reset local schema (last resort, lose local schema modifications)nodetool resetlocalschemaSafe Schema Changes
Section titled “Safe Schema Changes”-- Adding columns (instant, no data movement)ALTER TABLE my_keyspace.users ADD phone TEXT;ALTER TABLE my_keyspace.users ADD (address TEXT, city TEXT);
-- Dropping columns (instant, data remains until compaction)ALTER TABLE my_keyspace.users DROP phone;
-- Changing table optionsALTER TABLE my_keyspace.users WITH compaction = {'class': 'LeveledCompactionStrategy'} AND gc_grace_seconds = 172800;
-- Adding indexes (may take time to build)CREATE INDEX ON my_keyspace.users (email);Dangerous Schema Changes
Section titled “Dangerous Schema Changes”-- Cannot change primary key (must recreate table)-- DO NOT: ALTER TABLE users ALTER PRIMARY KEY...
-- Cannot change column types (must recreate)-- DO NOT: ALTER TABLE users ALTER email TYPE blob;
-- Dropping tables (immediate, data lost)DROP TABLE my_keyspace.users; -- DATA LOSS!
-- Use IF EXISTS to prevent errorsDROP TABLE IF EXISTS my_keyspace.old_table;Disk Maintenance
Section titled “Disk Maintenance”Monitoring Disk Usage
Section titled “Monitoring Disk Usage”# Per-node disk usagedf -h /var/lib/cassandra
# Per-keyspace disk usagenodetool tablestats my_keyspace | grep "Space used"
# Find large directoriesdu -sh /var/lib/cassandra/data/*
# Find large tablesdu -sh /var/lib/cassandra/data/*/* | sort -h | tail -20Reclaiming Disk Space
Section titled “Reclaiming Disk Space”# Clear snapshots (major space consumer)nodetool listsnapshotsnodetool clearsnapshot -t old_snapshot_namenodetool clearsnapshot # Clear ALL snapshots
# Clear incremental backups (if enabled)find /var/lib/cassandra/data -path "*/backups/*" -delete
# Force compaction to reclaim tombstone space# Only if gc_grace_seconds has passed and repair is currentnodetool compact my_keyspace my_tableDisk Space Alerts
Section titled “Disk Space Alerts”Set up alerts for:
| Threshold | Action |
|---|---|
| 60% used | Plan capacity expansion |
| 75% used | Urgent: add capacity soon |
| 85% used | Critical: add capacity immediately |
| 90% used | Emergency: stop writes if necessary |
Log Maintenance
Section titled “Log Maintenance”Log Files
Section titled “Log Files”Default log locations:─────────────────────────────────────────────────────────────────────────────/var/log/cassandra/system.log → Main application log/var/log/cassandra/debug.log → Debug output (high volume)/var/log/cassandra/gc.log → Garbage collection logs/var/log/cassandra/audit/ → Audit logs (if enabled)Log Rotation
Section titled “Log Rotation”# Logrotate configuration (/etc/logrotate.d/cassandra)/var/log/cassandra/*.log { daily rotate 7 compress delaycompress missingok notifempty create 644 cassandra cassandra}
# Manual rotationlogrotate -f /etc/logrotate.d/cassandra
# Clear old logs manuallyfind /var/log/cassandra -name "*.log.*" -mtime +7 -deleteImportant Log Patterns
Section titled “Important Log Patterns”# Find errorsgrep -i "error\|exception" /var/log/cassandra/system.log | tail -50
# Find warningsgrep -i "warn" /var/log/cassandra/system.log | tail -50
# GC pausesgrep "GC pause" /var/log/cassandra/gc.log | tail -20
# Compaction issuesgrep -i "compact" /var/log/cassandra/system.log | grep -i "error\|fail"
# Dropped messagesgrep "DROPPED" /var/log/cassandra/system.log | tail -20Version Upgrades
Section titled “Version Upgrades”Upgrade Planning
Section titled “Upgrade Planning”Upgrade Path Rules:─────────────────────────────────────────────────────────────────────────────1. Always upgrade one major version at a time 3.11 → 4.0 → 4.1 → 5.0 (correct) 3.11 → 5.0 (WRONG - skip versions)
2. Read release notes for breaking changes3. Test in staging with production data4. Schedule during low-traffic period5. Have rollback plan readyPre-Upgrade Checklist
Section titled “Pre-Upgrade Checklist”#!/bin/bashecho "=== Pre-Upgrade Checklist ==="
# 1. Cluster healthecho "1. Checking cluster health..."nodetool status | grep -E "^[UD][NLMJ]"
# 2. No pending operationsecho "2. Checking pending operations..."nodetool compactionstats | head -5nodetool netstats | head -10
# 3. Backupecho "3. Verify recent backup exists..."nodetool listsnapshots | head -10
# 4. Repair statusecho "4. Check repair status..."nodetool tablestats system.local | grep -i repair
# 5. Schema agreementecho "5. Checking schema agreement..."nodetool describecluster | grep -A10 "Schema versions"
# 6. Disk space for SSTable upgradeecho "6. Checking disk space..."df -h /var/lib/cassandra
echo ""echo "Review above output before proceeding with upgrade"Upgrade Procedure
Section titled “Upgrade Procedure”#!/bin/bash# upgrade_node.sh - Run on each node sequentially
set -e
NEW_VERSION="4.1.3"
echo "=== Upgrading to Cassandra ${NEW_VERSION} ==="
# 1. Drain and stopecho "Draining node..."nodetool drainecho "Stopping Cassandra..."sudo systemctl stop cassandra
# 2. Backup configurationecho "Backing up configuration..."cp /etc/cassandra/cassandra.yaml /etc/cassandra/cassandra.yaml.bak.$(date +%Y%m%d)cp /etc/cassandra/jvm*.options /etc/cassandra/jvm.options.bak.$(date +%Y%m%d)
# 3. Upgrade packagesecho "Upgrading packages..."# For package manager install:sudo apt-get update && sudo apt-get install cassandra=${NEW_VERSION}# Or for tarball:# Stop, extract new version, update symlinks
# 4. Merge configuration changesecho "Review configuration changes..."# diff /etc/cassandra/cassandra.yaml.bak /etc/cassandra/cassandra.yaml.new# Apply necessary changes
# 5. Start Cassandraecho "Starting Cassandra..."sudo systemctl start cassandra
# 6. Wait for node to joinecho "Waiting for node to join..."sleep 60nodetool status
# 7. Upgrade SSTables (run after all nodes upgraded)# echo "Upgrading SSTables..."# nodetool upgradesstables
echo "=== Upgrade complete on this node ==="Post-Upgrade Tasks
Section titled “Post-Upgrade Tasks”# After ALL nodes upgraded:
# 1. Upgrade SSTables to new formatnodetool upgradesstables -a # -a for all keyspaces
# 2. Run repairnodetool repair -pr
# 3. Verify metrics and logstail -100 /var/log/cassandra/system.log | grep -i error
# 4. Test application functionalityAutomated Maintenance Script
Section titled “Automated Maintenance Script”#!/bin/bashLOG_FILE="/var/log/cassandra/maintenance_$(date +%Y%m%d).log"KEYSPACE="my_keyspace"
exec > >(tee -a ${LOG_FILE}) 2>&1
echo "=== Weekly Maintenance: $(date) ==="
# 1. Health checkecho -e "\n--- Health Check ---"nodetool statusnodetool describecluster | grep -A5 "Schema"
# 2. Take snapshot before maintenanceecho -e "\n--- Taking Snapshot ---"SNAPSHOT="weekly_$(date +%Y%m%d)"nodetool flush ${KEYSPACE}nodetool snapshot -t ${SNAPSHOT} ${KEYSPACE}
# 3. Run repairecho -e "\n--- Running Repair ---"nodetool repair -pr ${KEYSPACE}
# 4. Check and clear old snapshotsecho -e "\n--- Managing Snapshots ---"nodetool listsnapshots# Clear snapshots older than 2 weeksfor snap in $(nodetool listsnapshots | grep "weekly_" | awk '{print $1}' | sort | head -n -2); do echo "Clearing old snapshot: ${snap}" nodetool clearsnapshot -t ${snap}done
# 5. Final statusecho -e "\n--- Final Status ---"nodetool compactionstats | head -5df -h /var/lib/cassandra
echo -e "\n=== Maintenance Complete: $(date) ==="Troubleshooting Common Issues
Section titled “Troubleshooting Common Issues”High Pending Compactions
Section titled “High Pending Compactions”# Diagnosenodetool compactionstats # What is pendingnodetool tablestats | grep -i sstable # SSTable counts
# Solutionsnodetool setcompactionthroughput 128 # Increase throughput# Or check if disk I/O is saturatediostat -x 1 5Slow Queries
Section titled “Slow Queries”# Enable slow query logging (cassandra.yaml)# slow_query_log_timeout_in_ms: 500
# Check logsgrep "slow" /var/log/cassandra/debug.log
# Common causes:# - Large partitions (nodetool tablehistograms)# - Tombstone accumulation (enable tracing)# - Missing indexes (check query plans)Disk Space Issues
Section titled “Disk Space Issues”# Find what is using spacedu -sh /var/lib/cassandra/data/*du -sh /var/lib/cassandra/data/*/*du -sh /var/lib/cassandra/commitlog
# Clear snapshotsnodetool clearsnapshot
# Check for large tombstone-heavy tablesnodetool tablestats | grep -i tombstoneAxonOps Maintenance Automation
Section titled “AxonOps Maintenance Automation”Manual maintenance requires careful coordination, scripting expertise, and vigilant monitoring. AxonOps Operations automates routine maintenance tasks, reducing operational burden and risk.
Automated Rolling Operations
Section titled “Automated Rolling Operations”AxonOps provides:
- Coordinated rolling restarts: Automated drain, restart, and health verification across nodes
- Pre-flight validation: Automatic checks before each operation (cluster health, disk space, pending operations)
- Progress tracking: Real-time visibility into rolling operation progress
- Automatic pacing: Intelligent delays between nodes based on cluster state
Scheduled Maintenance
Section titled “Scheduled Maintenance”- Repair scheduling: Automated repair with configurable frequency and scope
- Cleanup orchestration: Automatic cleanup after topology changes
- Snapshot management: Scheduled backups with retention policies
- Log rotation: Centralized log management and retention
Upgrade Management
Section titled “Upgrade Management”- Version tracking: Monitor Cassandra versions across clusters
- Upgrade planning: Impact analysis before upgrades
- Rolling upgrade automation: Coordinated upgrade with health gates
- Rollback support: Quick rollback if issues detected
Compliance and Audit
Section titled “Compliance and Audit”- Change tracking: Complete audit log of all maintenance activities
- Compliance reporting: Documentation for regulatory requirements
- SLA monitoring: Track maintenance windows and availability
See the AxonOps documentation for maintenance automation features.
Next Steps
Section titled “Next Steps”- Repair Guide - Detailed repair procedures
- Backup Guide - Backup strategies
- Monitoring - Set up observability
- Troubleshooting - Problem resolution