Loading learning content...
While online backups dominate production environments where uptime is paramount, offline backup (also called cold backup) remains a fundamental component of comprehensive data protection strategies. An offline backup is performed when the database is completely shut down—no active connections, no running transactions, no I/O activity. The database files are in a quiescent, consistent state, ready for direct, reliable copying.
Far from being an obsolete relic, offline backup offers unique advantages that make it indispensable for certain scenarios: guaranteed consistency without complex mechanisms, simplified implementation, complete isolation from production activity, and reliable baselines for disaster recovery. Understanding when and how to employ offline backup is essential knowledge for any database professional.
By the end of this page, you will understand when offline backup is the right choice, master the techniques for implementing cold backups across major database platforms, recognize the trade-offs compared to online backup, and be able to design hybrid backup strategies that leverage the strengths of both approaches.
Defining Offline Backup
An offline (cold) backup occurs when the database management system is completely stopped before any backup activity begins. During a cold backup:
With the database in this quiescent state, all files can be copied directly using standard file system tools—cp, rsync, tar, or storage-level snapshot—with absolute certainty of consistency.
The Simplicity Advantage
Offline backup derives its reliability from simplicity. There's no need for:
The copied files are the backup—complete, consistent, immediately usable. This simplicity translates to fewer failure modes, easier verification, and simpler recovery procedures.
| Characteristic | Online (Hot) Backup | Offline (Cold) Backup |
|---|---|---|
| Database State | Running, accepting connections | Completely stopped |
| Application Impact | Minimal (performance only) | Total downtime during backup |
| Consistency Mechanism | WAL + checkpoint coordination | Natural quiescent state |
| Implementation Complexity | Higher (database-specific tools) | Lower (file system tools) |
| Recovery Complexity | Requires log replay | Direct file copy restore |
| Backup Speed | May be throttled for performance | Maximum disk throughput |
| Risk of Failure | More failure points | Fewer failure points |
| Validation | Requires database-level checks | Simple checksum verification |
Some engineers dismiss cold backup as obsolete in an age of online backup capabilities. This is a mistake. Cold backup provides the most reliable baseline for disaster recovery, the simplest recovery path when that reliability matters most, and often the fastest backup/restore speeds since no application coordination is needed.
Offline backup is the right choice in numerous scenarios. Understanding these use cases ensures you select the appropriate strategy for each situation.
1. Scheduled Maintenance Windows
Many systems have defined maintenance windows—periods where downtime is planned and accepted:
During these windows, cold backup provides maximum reliability without the complexity of online backup. Since downtime is already planned, there's no additional impact.
2. Major Upgrades and Migrations
Before significant system changes, a cold backup provides an unambiguous fallback:
The ability to restore to the exact pre-change state, without any questions about consistency, is invaluable when rollback becomes necessary.
3. Maximum Reliability Requirements
In scenarios where backup reliability is more critical than availability, cold backup wins:
4. Small Databases with Flexible SLAs
Not every database serves 24/7 global traffic. Many databases can tolerate brief downtime:
For these systems, a 15-minute cold backup at 2 AM is often the most practical approach.
Most enterprise environments benefit from a hybrid approach: frequent online backups (daily or more often) for normal operations, combined with periodic cold backups (weekly or monthly) to establish verified-consistent baselines. This provides both continuous protection and reliable restoration guarantees.
Implementing cold backup requires a methodical approach to ensure all prerequisites are met and the backup is complete. The following procedures apply across database platforms.
Phase 1: Preparation
Before initiating shutdown:
Phase 2: Graceful Shutdown
Proper shutdown ensures all data is flushed and consistent:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
#!/bin/bash# Graceful Database Shutdown for Cold Backup # =====================================# PostgreSQL Shutdown# =====================================# Standard graceful shutdown - wait for connections to closesudo -u postgres pg_ctl stop -D /var/lib/postgresql/14/main -m smart # Fast shutdown - disconnect clients, but complete running transactionssudo -u postgres pg_ctl stop -D /var/lib/postgresql/14/main -m fast # Immediate shutdown - abort transactions (not recommended for backup)# sudo -u postgres pg_ctl stop -D /var/lib/postgresql/14/main -m immediate # Using systemd (modern systems)sudo systemctl stop postgresql # Verify shutdown completepg_isready -h localhost# Should return "no response" when properly stopped # =====================================# MySQL/MariaDB Shutdown# =====================================# Graceful shutdownsudo systemctl stop mysql# ormysqladmin -u root -p shutdown # Verify shutdownmysqladmin ping 2>/dev/null || echo "MySQL is stopped" # =====================================# SQL Server (Linux) Shutdown# =====================================sudo systemctl stop mssql-server# Verifysystemctl status mssql-server # =====================================# Oracle Shutdown# =====================================# Connect as SYSDBA and shutdownsqlplus / as sysdba <<EOFSHUTDOWN IMMEDIATE;EXIT;EOF # SHUTDOWN options:# NORMAL - Wait for all users to disconnect# IMMEDIATE - Rollback active transactions, disconnect users# TRANSACTIONAL - Complete ongoing transactions, prevent new ones# ABORT - Immediate halt (requires recovery on startup)Phase 3: File Backup
With the database stopped, copy all relevant files. The specific files depend on the database platform:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
#!/bin/bash# Cold Backup File Copy Procedures BACKUP_ROOT="/backup/cold"TIMESTAMP=$(date +%Y%m%d_%H%M%S) # =====================================# PostgreSQL Cold Backup# =====================================PG_DATA="/var/lib/postgresql/14/main"PG_BACKUP="$BACKUP_ROOT/postgresql/$TIMESTAMP" mkdir -p "$PG_BACKUP" # Method 1: tar with compressiontar -czf "$PG_BACKUP/pgdata.tar.gz" -C "$PG_DATA" . # Method 2: rsync (supports incremental, preserves attributes)rsync -av --delete "$PG_DATA/" "$PG_BACKUP/data/" # Method 3: cp with archive modecp -a "$PG_DATA" "$PG_BACKUP/data" # Include tablespaces if externalfor ts_dir in $(ls -d /var/lib/postgresql/tablespaces/* 2>/dev/null); do tar -czf "$PG_BACKUP/$(basename $ts_dir).tar.gz" -C "$ts_dir" .done # =====================================# MySQL Cold Backup# =====================================MYSQL_DATA="/var/lib/mysql"MYSQL_BACKUP="$BACKUP_ROOT/mysql/$TIMESTAMP" mkdir -p "$MYSQL_BACKUP" # Backup entire data directorytar -czf "$MYSQL_BACKUP/mysql_data.tar.gz" \ -C "$MYSQL_DATA" . \ --exclude='*.sock' \ --exclude='*.pid' # Backup configurationcp /etc/mysql/my.cnf "$MYSQL_BACKUP/"cp -r /etc/mysql/conf.d "$MYSQL_BACKUP/" 2>/dev/null # =====================================# Oracle Cold Backup# =====================================ORACLE_BASE="/u01/app/oracle"ORACLE_BACKUP="$BACKUP_ROOT/oracle/$TIMESTAMP" mkdir -p "$ORACLE_BACKUP" # Backup data files (example paths - verify for your environment)tar -czf "$ORACLE_BACKUP/datafiles.tar.gz" \ /u01/oradata/*/datafile/*.dbf # Backup control filestar -czf "$ORACLE_BACKUP/controlfiles.tar.gz" \ /u01/oradata/*/controlfile/*.ctl # Backup redo logstar -czf "$ORACLE_BACKUP/redologs.tar.gz" \ /u01/oradata/*/onlinelog/*.log # Backup parameter filescp $ORACLE_HOME/dbs/init*.ora "$ORACLE_BACKUP/"cp $ORACLE_HOME/dbs/spfile*.ora "$ORACLE_BACKUP/" # =====================================# Verification# =====================================echo "Backup completed at: $TIMESTAMP"echo "Backup size:"du -sh "$BACKUP_ROOT"/*/"$TIMESTAMP" # Generate checksums for verificationfind "$BACKUP_ROOT"/*/"$TIMESTAMP" -type f -exec sha256sum {} \; > \ "$BACKUP_ROOT/checksums_$TIMESTAMP.txt"Phase 4: Verification
Before restarting the database, verify the backup:
Phase 5: Database Restart
After successful verification, restart the database:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
#!/bin/bash# Database Restart After Cold Backup # =====================================# PostgreSQL# =====================================sudo systemctl start postgresql # Wait for startupfor i in {1..30}; do pg_isready -h localhost && break sleep 1done # Verify database is operationalsudo -u postgres psql -c "SELECT datname, pg_database_size(datname) FROM pg_database;" # Run quick consistency checksudo -u postgres vacuumdb --analyze --all # =====================================# MySQL# =====================================sudo systemctl start mysql # Wait for startupfor i in {1..30}; do mysqladmin ping 2>/dev/null && break sleep 1done # Run table checksmysqlcheck -u root -p --all-databases --check # =====================================# Oracle# =====================================sqlplus / as sysdba <<EOFSTARTUP;SELECT name, open_mode FROM v\$database;SELECT tablespace_name, status FROM dba_tablespaces;EXIT;EOF # =====================================# Calculate Downtime# =====================================# Using environment variables set at shutdown/startupecho "Database downtime: $(( $(date +%s) - $SHUTDOWN_TIMESTAMP )) seconds"Modern storage systems offer capabilities that significantly enhance cold backup efficiency. Leveraging these features can reduce backup windows from hours to seconds.
LVM Snapshots
Logical Volume Manager (LVM) on Linux enables instant snapshots of database volumes:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546
#!/bin/bash# LVM-based Cold Backup # ConfigurationVG_NAME="dbvg" # Volume group nameLV_NAME="pg_data" # Logical volume nameSNAPSHOT_NAME="pg_data_snap" # Snapshot nameSNAPSHOT_SIZE="50G" # Space for changed blocks during copyMOUNT_POINT="/mnt/dbsnapshot" # Where to mount snapshotBACKUP_DEST="/backup/lvm" # Backup destination # Step 1: Stop database for consistencyecho "Stopping PostgreSQL..."sudo systemctl stop postgresqlsleep 5 # Step 2: Create LVM snapshot (instant operation)echo "Creating LVM snapshot..."sudo lvcreate -L $SNAPSHOT_SIZE -s -n $SNAPSHOT_NAME /dev/$VG_NAME/$LV_NAME# Snapshot created in milliseconds! # Step 3: Restart database immediately - snapshot is independentecho "Restarting PostgreSQL..."sudo systemctl start postgresqlpg_isready -h localhost && echo "PostgreSQL is back online" # Database downtime ends here - typically under 30 seconds! # Step 4: Mount snapshot for backup (non-blocking)echo "Mounting snapshot for backup..."sudo mkdir -p $MOUNT_POINTsudo mount -o ro /dev/$VG_NAME/$SNAPSHOT_NAME $MOUNT_POINT # Step 5: Backup from snapshot at leisureecho "Backing up from snapshot..."TIMESTAMP=$(date +%Y%m%d_%H%M%S)tar -czf "$BACKUP_DEST/pg_cold_backup_$TIMESTAMP.tar.gz" \ -C $MOUNT_POINT . # Step 6: Cleanup snapshotecho "Cleaning up snapshot..."sudo umount $MOUNT_POINTsudo lvremove -f /dev/$VG_NAME/$SNAPSHOT_NAME echo "Backup complete. Database downtime was minimal."echo "Backup file: $BACKUP_DEST/pg_cold_backup_$TIMESTAMP.tar.gz"ZFS Snapshots
ZFS provides even more powerful snapshot capabilities with atomic, space-efficient snapshots and built-in send/receive for backup:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
#!/bin/bash# ZFS-based Cold Backup # ConfigurationZFS_DATASET="zpool/database" # ZFS dataset containing databaseBACKUP_POOL="backup" # Destination pool for backupTIMESTAMP=$(date +%Y%m%d_%H%M%S) # Step 1: Stop databaseecho "Stopping database for consistent snapshot..."sudo systemctl stop postgresqlsleep 3 # Step 2: Create ZFS snapshot (atomic, instant)echo "Creating ZFS snapshot..."sudo zfs snapshot $ZFS_DATASET@cold-$TIMESTAMP# Snapshot is atomic - guaranteed consistent # Step 3: Restart database immediatelyecho "Restarting database..."sudo systemctl start postgresqlpg_isready && echo "Database online" # Total downtime: typically 5-15 seconds # Step 4: Send snapshot to backup storageecho "Sending snapshot to backup storage..."# Local backupsudo zfs send $ZFS_DATASET@cold-$TIMESTAMP | \ sudo zfs receive $BACKUP_POOL/db-backup-$TIMESTAMP # Or remote backup via SSH# sudo zfs send $ZFS_DATASET@cold-$TIMESTAMP | \# ssh backupserver "sudo zfs receive backup_pool/db-backup-$TIMESTAMP" # Or save to file# sudo zfs send $ZFS_DATASET@cold-$TIMESTAMP | \# gzip > /backup/zfs/db-$TIMESTAMP.zfs.gz # Step 5: Verify backupecho "Verifying backup..."sudo zfs list -t snapshot | grep "cold-$TIMESTAMP" # Step 6: Cleanup old snapshots (keep last N)echo "Rotating old snapshots..."KEEP_SNAPSHOTS=7sudo zfs list -t snapshot -H -o name | \ grep "$ZFS_DATASET@cold-" | \ head -n -$KEEP_SNAPSHOTS | \ xargs -I {} sudo zfs destroy {} echo "ZFS cold backup complete"Cloud Volume Snapshots
Cloud providers offer snapshot capabilities for their block storage:
These integrate seamlessly with cold backup strategies:
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
#!/bin/bash# AWS EBS Snapshot-based Cold Backup # ConfigurationINSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)REGION="us-east-1"VOLUME_ID="vol-0abc123def456789" # Database EBS volumeRETENTION_DAYS=30 # Step 1: Stop database serviceecho "Stopping database for snapshot consistency..."sudo systemctl stop postgresqlsleep 5 # Step 2: Sync filesystem (ensure all writes to disk)sync # Step 3: Create EBS snapshotecho "Creating EBS snapshot..."SNAPSHOT_ID=$(aws ec2 create-snapshot \ --volume-id $VOLUME_ID \ --description "Cold backup $(date +%Y-%m-%d)" \ --tag-specifications "ResourceType=snapshot,Tags=[{Key=Name,Value=db-cold-backup},{Key=Date,Value=$(date +%Y-%m-%d)},{Key=Retention,Value=$RETENTION_DAYS}]" \ --query 'SnapshotId' \ --output text \ --region $REGION) echo "Snapshot initiated: $SNAPSHOT_ID" # Step 4: Restart database immediatelyecho "Restarting database..."sudo systemctl start postgresqlpg_isready && echo "Database online" # Step 5: Wait for snapshot completion (optional - for verification)echo "Waiting for snapshot completion..."aws ec2 wait snapshot-completed \ --snapshot-ids $SNAPSHOT_ID \ --region $REGION echo "Snapshot completed: $SNAPSHOT_ID" # Step 6: Copy to another region (disaster recovery)DR_REGION="us-west-2"echo "Copying snapshot to DR region..."DR_SNAPSHOT_ID=$(aws ec2 copy-snapshot \ --source-region $REGION \ --source-snapshot-id $SNAPSHOT_ID \ --destination-region $DR_REGION \ --description "DR copy of $SNAPSHOT_ID" \ --query 'SnapshotId' \ --output text \ --region $DR_REGION) echo "DR snapshot: $DR_SNAPSHOT_ID in $DR_REGION" # Step 7: Cleanup old snapshotsecho "Cleaning up snapshots older than $RETENTION_DAYS days..."CUTOFF_DATE=$(date -d "-$RETENTION_DAYS days" +%Y-%m-%d)aws ec2 describe-snapshots \ --owner-ids self \ --filters "Name=tag:Name,Values=db-cold-backup" \ --query "Snapshots[?StartTime<='$CUTOFF_DATE'].SnapshotId" \ --output text \ --region $REGION | \ xargs -r -n1 aws ec2 delete-snapshot --snapshot-id \ --region $REGION echo "Cold backup complete"Storage-level snapshots transform cold backup from a lengthy process to a brief pause. The database is only stopped for the instant required to create the snapshot—often just seconds. The time-consuming backup copy happens afterward, with the database fully operational. This hybrid approach gives you the consistency guarantees of cold backup with near-zero downtime impact.
Recovery from cold backup is straightforward compared to online backup—no log replay or complex recovery phases. The process is essentially the inverse of the backup process.
Recovery Workflow
Complete Database Recovery
For full disaster recovery, restoring to a new server or after complete data loss:
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
#!/bin/bash# Cold Backup Recovery Procedure # =====================================# PostgreSQL Recovery# =====================================BACKUP_FILE="/backup/cold/postgresql/pgdata.tar.gz"PG_DATA="/var/lib/postgresql/14/main" # Step 1: Stop PostgreSQL if runningsudo systemctl stop postgresql 2>/dev/null || true # Step 2: Clear existing data (CAUTION!)echo "WARNING: This will destroy existing data. Press Ctrl+C to abort."sleep 5sudo rm -rf $PG_DATA/* # Step 3: Restore from backupecho "Restoring from backup..."sudo tar -xzf $BACKUP_FILE -C $PG_DATA # Step 4: Set correct ownershipsudo chown -R postgres:postgres $PG_DATA # Step 5: Verify permissions on critical filessudo chmod 700 $PG_DATAsudo chmod 600 $PG_DATA/pg_hba.confsudo chmod 600 $PG_DATA/postgresql.conf # Step 6: Start PostgreSQLecho "Starting PostgreSQL..."sudo systemctl start postgresql # Step 7: Verify recoverypg_isready -h localhost -p 5432sudo -u postgres psql -c "SELECT current_timestamp, pg_database_size('postgres');" # =====================================# MySQL Recovery# =====================================MYSQL_BACKUP="/backup/cold/mysql/mysql_data.tar.gz"MYSQL_DATA="/var/lib/mysql" # Stop MySQLsudo systemctl stop mysql # Clear existing datasudo rm -rf $MYSQL_DATA/* # Restoresudo tar -xzf $MYSQL_BACKUP -C $MYSQL_DATA # Set ownershipsudo chown -R mysql:mysql $MYSQL_DATA # Start MySQLsudo systemctl start mysql # Verifymysql -u root -p -e "SHOW DATABASES;" # =====================================# ZFS Recovery (if using ZFS snapshots)# =====================================ZFS_SOURCE="backup/db-backup-20240115"ZFS_TARGET="zpool/database" # Stop databasesudo systemctl stop postgresql # Rollback to snapshot (if snapshot exists on target)sudo zfs rollback $ZFS_TARGET@recovery-point # Or receive from backup pool/filesudo zfs destroy $ZFS_TARGET # CAUTION: destroys current datasudo zfs send $ZFS_SOURCE@snapshot | sudo zfs receive $ZFS_TARGET # Or from filegunzip -c /backup/zfs/db-backup.zfs.gz | sudo zfs receive $ZFS_TARGET # Start databasesudo systemctl start postgresqlPoint-in-Time Considerations
Cold backup captures a single point in time—the moment the database was stopped. Unlike online backup with WAL archiving, you cannot recover to any arbitrary point between backups.
What this means:
Hybrid Recovery Strategy
For environments requiring both reliability and minimal data loss:
Recovery then becomes:
The simplicity of cold backup recovery can create false confidence. Regular restore tests remain essential. Verify that: (1) backup files are readable and complete, (2) restore procedures are documented and work, (3) recovery time meets business requirements, and (4) recovered data passes integrity checks.
Understanding the complete picture of cold backup strengths and limitations enables informed strategy decisions.
| Requirement | Favors Cold Backup | Favors Hot Backup |
|---|---|---|
| 24/7 Availability Required | No | Yes (strongly) |
| Maximum Recovery Reliability | Yes (strongly) | Yes |
| Point-in-Time Recovery | No | Yes (strongly) |
| Limited DBA Expertise | Yes | No |
| Compliance/Audit Requirements | Often yes | Depends on requirement |
| Development/Test Environment | Yes (usually) | Overkill |
| Before Major Changes | Yes (strongly) | Also recommended |
| Minimal RPO Tolerance | No | Yes (strongly) |
| Simple Recovery Needed | Yes | No (more complex) |
Offline backup remains a valuable tool in the database professional's arsenal. Its simplicity, reliability, and guaranteed consistency make it indispensable for certain scenarios.
You now understand offline (cold) backup—when to use it, how to implement it, and its role in comprehensive data protection. Cold backup's simplicity and reliability make it essential for baselines, pre-change snapshots, and scenarios where guaranteed consistency outweighs availability requirements. Next, we'll explore the critical concept of consistent backup and how to achieve it across different database architectures.