diff --git a/mailcow-backup-recovery.md b/mailcow-backup-recovery.md deleted file mode 100644 index 0b908cc..0000000 --- a/mailcow-backup-recovery.md +++ /dev/null @@ -1,860 +0,0 @@ -# Mailcow Backup and Recovery Guide - -## Overview - -This document provides comprehensive backup and recovery procedures for Mailcow email server. Since Mailcow is **not running on ZFS or BTRFS**, snapshots are not available and we rely on Mailcow's native backup script combined with Kopia for offsite storage in vaults. - -## Quick Reference - -### Common Backup Commands - -```bash -# Run a manual backup (all components) -cd /opt/mailcow-dockerized -MAILCOW_BACKUP_LOCATION=/opt/mailcow-backups \ - ./helper-scripts/backup_and_restore.sh backup all --delete-days 7 - -# Backup with multithreading (faster) -THREADS=4 MAILCOW_BACKUP_LOCATION=/opt/mailcow-backups \ - ./helper-scripts/backup_and_restore.sh backup all --delete-days 7 - -# List Kopia snapshots -kopia snapshot list --tags mailcow - -# View backup logs -tail -f /var/log/mailcow-backup.log -``` - -### Common Restore Commands - -```bash -# Restore using mailcow native script (interactive) -cd /opt/mailcow-dockerized -./helper-scripts/backup_and_restore.sh restore - -# Restore from Kopia to new server -kopia snapshot list --tags tier1-backup -kopia restore /opt/mailcow-backups/ - -# Check container status after restore -docker compose ps -docker compose logs -f -``` - -## Critical Components to Backup - -### 1. Docker Compose File -- **Location**: `/opt/mailcow-dockerized/docker-compose.yml` (or your installation path) -- **Purpose**: Defines all containers, networks, and volumes -- **Importance**: Critical for recreating the exact container configuration - -### 2. Configuration Files -- **Primary Config**: `/opt/mailcow-dockerized/mailcow.conf` -- **Additional Configs**: - - `/opt/mailcow-dockerized/data/conf/` (all subdirectories) - - Custom SSL certificates if not using Let's Encrypt - - Any override files (e.g., `docker-compose.override.yml`) - -### 3. Database -- **MySQL/MariaDB Data**: Contains all mailbox configurations, users, domains, aliases, settings -- **Docker Volume**: `mailcowdockerized_mysql-vol` -- **Container Path**: `/var/lib/mysql` - -### 4. Email Data -- **Maildir Storage**: All actual email messages -- **Docker Volume**: `mailcowdockerized_vmail-vol` -- **Container Path**: `/var/vmail` -- **Size**: Typically the largest component - -### 5. Additional Important Data -- **Redis Data**: `mailcowdockerized_redis-vol` (cache and sessions) -- **Rspamd Data**: `mailcowdockerized_rspamd-vol` (spam learning) -- **Crypt Data**: `mailcowdockerized_crypt-vol` (if using mailbox encryption) -- **Postfix Queue**: `mailcowdockerized_postfix-vol` (queued/deferred mail) - -## Backup Strategy - -### Two-Tier Backup Approach - -We use a **two-tier approach** combining Mailcow's native backup script with Kopia for offsite storage: - -1. **Tier 1 (Local)**: Mailcow's `backup_and_restore.sh` script creates consistent, component-level backups -2. **Tier 2 (Offsite)**: Kopia snapshots the local backups and syncs to vaults - -#### Why This Approach? - -- **Best of both worlds**: Native script ensures mailcow-specific consistency, Kopia provides deduplication and offsite protection -- **Component-level restore**: Can restore individual components (just vmail, just mysql, etc.) using mailcow script -- **Disaster recovery**: Full system restore from Kopia backups on new server -- **Efficient storage**: Kopia's deduplication reduces storage needs for offsite copies - -#### Backup Frequency -- **Daily**: Mailcow native backup runs at 2 AM -- **Daily**: Kopia snapshot of backups runs at 3 AM -- **Retention (Local)**: 7 days of mailcow backups (managed by script) -- **Retention (Kopia/Offsite)**: 30 daily, 12 weekly, 12 monthly - -### Mailcow Native Backup Script - -Mailcow includes `/opt/mailcow-dockerized/helper-scripts/backup_and_restore.sh` which handles: -- **vmail**: Email data (mailboxes) -- **mysql**: Database (using mariabackup for consistency) -- **redis**: Redis database -- **rspamd**: Spam filter learning data -- **crypt**: Encryption data -- **postfix**: Mail queue - -**Key Features:** -- Uses `mariabackup` (hot backup without stopping MySQL) -- Supports multithreading for faster backups -- Architecture-aware (handles x86/ARM differences) -- Built-in cleanup with `--delete-days` parameter -- Creates compressed archives (.tar.zst or .tar.gz) - -### Setting Up Mailcow Backups - -#### Step 1: Configure Backup Location - -Set the backup destination via environment variable or in mailcow.conf: - -```bash -# Option 1: Set environment variable (preferred for automation) -export MAILCOW_BACKUP_LOCATION="/opt/mailcow-backups" - -# Option 2: Add to cron job directly (shown in automated script below) -``` - -Create the backup directory: -```bash -mkdir -p /opt/mailcow-backups -chown -R root:root /opt/mailcow-backups -chmod 700 /opt/mailcow-backups -``` - -#### Step 2: Manual Backup Commands - -```bash -cd /opt/mailcow-dockerized - -# Backup all components, delete backups older than 7 days -MAILCOW_BACKUP_LOCATION=/opt/mailcow-backups \ - ./helper-scripts/backup_and_restore.sh backup all --delete-days 7 - -# Backup with multithreading (faster for large mailboxes) -THREADS=4 MAILCOW_BACKUP_LOCATION=/opt/mailcow-backups \ - ./helper-scripts/backup_and_restore.sh backup all --delete-days 7 - -# Backup specific components only -MAILCOW_BACKUP_LOCATION=/opt/mailcow-backups \ - ./helper-scripts/backup_and_restore.sh backup vmail mysql --delete-days 7 -``` - -**What gets created:** -- Backup directory: `/opt/mailcow-backups/mailcow-YYYY-MM-DD-HH-MM-SS/` -- Contains: `.tar.zst` compressed archives for each component -- Plus: `mailcow.conf` copy for restore reference - -#### Step 3: Automated Backup Script - -Create `/opt/scripts/backup-mailcow.sh`: - -```bash -#!/bin/bash - -# Mailcow Automated Backup Script -# This creates mailcow native backups, then snapshots them with Kopia for offsite storage - -set -e - -BACKUP_DATE=$(date +%Y%m%d_%H%M%S) -LOG_FILE="/var/log/mailcow-backup.log" -MAILCOW_DIR="/opt/mailcow-dockerized" -BACKUP_DIR="/opt/mailcow-backups" -THREADS=4 # Adjust based on your CPU cores -KEEP_DAYS=7 # Keep local mailcow backups for 7 days - -echo "[${BACKUP_DATE}] ========================================" | tee -a "$LOG_FILE" -echo "[${BACKUP_DATE}] Starting Mailcow backup process" | tee -a "$LOG_FILE" - -# Step 1: Run mailcow's native backup script -echo "[${BACKUP_DATE}] Running mailcow native backup..." | tee -a "$LOG_FILE" - -cd "$MAILCOW_DIR" - -# Run the backup with multithreading -THREADS=${THREADS} MAILCOW_BACKUP_LOCATION=${BACKUP_DIR} \ - ./helper-scripts/backup_and_restore.sh backup all --delete-days ${KEEP_DAYS} \ - 2>&1 | tee -a "$LOG_FILE" - -BACKUP_EXIT=${PIPESTATUS[0]} - -if [ $BACKUP_EXIT -ne 0 ]; then - echo "[${BACKUP_DATE}] ERROR: Mailcow backup failed with exit code ${BACKUP_EXIT}" | tee -a "$LOG_FILE" - exit 1 -fi - -echo "[${BACKUP_DATE}] Mailcow native backup completed successfully" | tee -a "$LOG_FILE" - -# Step 2: Create Kopia snapshot of backup directory -echo "[${BACKUP_DATE}] Creating Kopia snapshot..." | tee -a "$LOG_FILE" - -kopia snapshot create "${BACKUP_DIR}" \ - --tags mailcow,tier1-backup \ - --description "Mailcow backup ${BACKUP_DATE}" \ - 2>&1 | tee -a "$LOG_FILE" - -KOPIA_EXIT=${PIPESTATUS[0]} - -if [ $KOPIA_EXIT -ne 0 ]; then - echo "[${BACKUP_DATE}] WARNING: Kopia snapshot failed with exit code ${KOPIA_EXIT}" | tee -a "$LOG_FILE" - echo "[${BACKUP_DATE}] Local mailcow backup exists but offsite copy may be incomplete" | tee -a "$LOG_FILE" - exit 2 -fi - -echo "[${BACKUP_DATE}] Kopia snapshot completed successfully" | tee -a "$LOG_FILE" - -# Step 3: Also backup the mailcow installation directory (configs, compose files) -echo "[${BACKUP_DATE}] Backing up mailcow installation directory..." | tee -a "$LOG_FILE" - -kopia snapshot create "${MAILCOW_DIR}" \ - --tags mailcow,config,docker-compose \ - --description "Mailcow config ${BACKUP_DATE}" \ - 2>&1 | tee -a "$LOG_FILE" - -echo "[${BACKUP_DATE}] Backup process completed successfully" | tee -a "$LOG_FILE" -echo "[${BACKUP_DATE}] ========================================" | tee -a "$LOG_FILE" - -# Optional: Send notification on completion -# Add your notification method here (email, webhook, etc.) -``` - -Make it executable: -```bash -chmod +x /opt/scripts/backup-mailcow.sh -``` - -Add to crontab (daily at 2 AM): -```bash -# Edit root's crontab -crontab -e - -# Add this line: -0 2 * * * /opt/scripts/backup-mailcow.sh 2>&1 | logger -t mailcow-backup -``` - -### Offsite Backup to Vaults - -After local Kopia snapshots are created, sync to your offsite vaults: - -```bash -# Option 1: Kopia repository sync (if using multiple Kopia repos) -kopia repository sync-to filesystem --path /mnt/vault/mailcow-backup - -# Option 2: Rsync to vault -rsync -avz --delete /backup/kopia-repo/ /mnt/vault/mailcow-backup/ - -# Option 3: Rclone to remote vault -rclone sync /backup/kopia-repo/ vault:mailcow-backup/ -``` - -## Recovery Procedures - -### Understanding Two Recovery Methods - -We have **two restore methods** depending on the scenario: - -1. **Mailcow Native Restore** (Preferred): For component-level or same-server recovery -2. **Kopia Full Restore**: For complete disaster recovery to a new server - -### Method 1: Mailcow Native Restore (Recommended) - -Use this method when: -- Restoring on the same/similar server -- Restoring specific components (just email, just database, etc.) -- Recovering from local mailcow backups - -#### Step 1: List Available Backups - -```bash -cd /opt/mailcow-dockerized - -# Run the restore script -./helper-scripts/backup_and_restore.sh restore -``` - -The script will prompt: -``` -Backup location (absolute path, starting with /): /opt/mailcow-backups -``` - -#### Step 2: Select Backup - -The script displays available backups: -``` -Found project name mailcowdockerized -[ 1 ] - /opt/mailcow-backups/mailcow-2026-02-09-02-00-14/ -[ 2 ] - /opt/mailcow-backups/mailcow-2026-02-10-02-00-08/ -``` - -Enter the number of the backup to restore. - -#### Step 3: Select Components - -Choose what to restore: -``` -[ 0 ] - all -[ 1 ] - Crypt data -[ 2 ] - Rspamd data -[ 3 ] - Mail directory (/var/vmail) -[ 4 ] - Redis DB -[ 5 ] - Postfix data -[ 6 ] - SQL DB -``` - -**Important**: The script will: -- Stop mailcow containers automatically -- Restore selected components -- Handle permissions correctly -- Restart containers when done - -#### Example: Restore Only Email Data - -```bash -cd /opt/mailcow-dockerized -./helper-scripts/backup_and_restore.sh restore - -# When prompted: -# - Backup location: /opt/mailcow-backups -# - Select backup: 2 (most recent) -# - Select component: 3 (Mail directory) -``` - -#### Example: Restore Database Only - -```bash -cd /opt/mailcow-dockerized -./helper-scripts/backup_and_restore.sh restore - -# When prompted: -# - Backup location: /opt/mailcow-backups -# - Select backup: 2 (most recent) -# - Select component: 6 (SQL DB) -``` - -**Note**: For database restore, the script will modify `mailcow.conf` with the database credentials from the backup. Review the changes after restore. - -### Method 2: Complete Server Rebuild (Kopia Restore) - -Use this when recovering to a completely new server or when local backups are unavailable. - -#### Step 1: Prepare New Server - -```bash -# Update system -apt update && apt upgrade -y - -# Install Docker -curl -fsSL https://get.docker.com | sh -systemctl enable docker -systemctl start docker - -# Install Docker Compose -apt install docker-compose-plugin -y - -# Install Kopia -curl -s https://kopia.io/signing-key | apt-key add - -echo "deb https://packages.kopia.io/apt/ stable main" | tee /etc/apt/sources.list.d/kopia.list -apt update -apt install kopia -y - -# Create directory structure -mkdir -p /opt/mailcow-dockerized -mkdir -p /opt/mailcow-backups/database -``` - -#### Step 2: Restore Kopia Repository - -```bash -# Connect to your offsite vault -# If vault is mounted: -kopia repository connect filesystem --path /mnt/vault/mailcow-backup - -# If vault is remote: -kopia repository connect s3 --bucket=your-bucket --access-key=xxx --secret-access-key=xxx - -# List available snapshots -kopia snapshot list --tags mailcow -``` - -#### Step 3: Restore Configuration - -```bash -# Find and restore the config snapshot -kopia snapshot list --tags config - -# Restore to the Mailcow directory -kopia restore /opt/mailcow-dockerized/ - -# Verify critical files -ls -la /opt/mailcow-dockerized/mailcow.conf -ls -la /opt/mailcow-dockerized/docker-compose.yml -``` - -#### Step 4: Restore Mailcow Backups Directory - -```bash -# Restore the entire backup directory from Kopia -kopia snapshot list --tags tier1-backup - -# Restore the most recent backup -kopia restore /opt/mailcow-backups/ - -# Verify backups were restored -ls -la /opt/mailcow-backups/ -``` - -#### Step 5: Run Mailcow Native Restore - -Now use mailcow's built-in restore script: - -```bash -cd /opt/mailcow-dockerized - -# Run the restore script -./helper-scripts/backup_and_restore.sh restore - -# When prompted: -# - Backup location: /opt/mailcow-backups -# - Select the most recent backup -# - Select [ 0 ] - all (to restore everything) -``` - -The script will: -1. Stop all mailcow containers -2. Restore all components (vmail, mysql, redis, rspamd, postfix, crypt) -3. Update mailcow.conf with restored database credentials -4. Restart all containers - -**Alternative: Manual Restore** (if you prefer more control) - -```bash -cd /opt/mailcow-dockerized - -# Start containers to create volumes -docker compose up -d --no-start -docker compose down - -# Find the most recent backup directory -LATEST_BACKUP=$(ls -td /opt/mailcow-backups/mailcow-* | head -1) -echo "Restoring from: $LATEST_BACKUP" - -# Extract each component manually -cd "$LATEST_BACKUP" - -# Restore vmail (email data) -docker run --rm \ - -v mailcowdockerized_vmail-vol:/backup \ - -v "$PWD":/restore \ - debian:bookworm-slim \ - tar --use-compress-program='zstd -d' -xvf /restore/backup_vmail.tar.zst - -# Restore MySQL -docker run --rm \ - -v mailcowdockerized_mysql-vol:/backup \ - -v "$PWD":/restore \ - mariadb:10.11 \ - tar --use-compress-program='zstd -d' -xvf /restore/backup_mysql.tar.zst - -# Restore Redis -docker run --rm \ - -v mailcowdockerized_redis-vol:/backup \ - -v "$PWD":/restore \ - debian:bookworm-slim \ - tar --use-compress-program='zstd -d' -xvf /restore/backup_redis.tar.zst - -# Restore other components similarly (rspamd, postfix, crypt) -# ... - -# Copy mailcow.conf from backup -cp "$LATEST_BACKUP/mailcow.conf" /opt/mailcow-dockerized/mailcow.conf -``` - -#### Step 6: Start and Verify Mailcow - -```bash -cd /opt/mailcow-dockerized - -# Pull latest images (or use versions from backup if preferred) -docker compose pull - -# Start all services -docker compose up -d - -# Monitor logs -docker compose logs -f -``` - -#### Step 7: Post-Restore Verification - -```bash -# Check container status -docker compose ps - -# Test web interface -curl -I https://mail.yourdomain.com - -# Check mail log -docker compose logs -f postfix-mailcow - -# Verify database -docker compose exec mysql-mailcow mysql -u root -p$(grep DBROOT mailcow.conf | cut -d'=' -f2) -e "SHOW DATABASES;" - -# Check email storage -docker compose exec dovecot-mailcow ls -lah /var/vmail/ -``` - -### Scenario 2: Restore Individual Mailbox - -To restore a single user's mailbox without affecting others: - -#### Option A: Using Mailcow Backups (If Available) - -```bash -cd /opt/mailcow-dockerized - -# Temporarily mount the backup -BACKUP_DIR="/opt/mailcow-backups/mailcow-YYYY-MM-DD-HH-MM-SS" - -# Extract just the vmail archive to a temporary location -mkdir -p /tmp/vmail-restore -cd "$BACKUP_DIR" -tar --use-compress-program='zstd -d' -xvf backup_vmail.tar.zst -C /tmp/vmail-restore - -# Find the user's mailbox -# Structure: /tmp/vmail-restore/var/vmail/domain.com/user/ -ls -la /tmp/vmail-restore/var/vmail/yourdomain.com/ - -# Copy specific mailbox -rsync -av /tmp/vmail-restore/var/vmail/yourdomain.com/user@domain.com/ \ - /var/lib/docker/volumes/mailcowdockerized_vmail-vol/_data/yourdomain.com/user@domain.com/ - -# Fix permissions -docker run --rm \ - -v mailcowdockerized_vmail-vol:/vmail \ - debian:bookworm-slim \ - chown -R 5000:5000 /vmail/yourdomain.com/user@domain.com/ - -# Cleanup -rm -rf /tmp/vmail-restore - -# Restart Dovecot to recognize changes -docker compose restart dovecot-mailcow -``` - -#### Option B: Using Kopia Snapshot (If Local Backups Unavailable) - -```bash -# Mount the vmail snapshot temporarily -mkdir -p /mnt/restore -kopia mount /mnt/restore - -# Find the user's mailbox -# Structure: /mnt/restore/domain.com/user/ -ls -la /mnt/restore/yourdomain.com/ - -# Copy specific mailbox -rsync -av /mnt/restore/yourdomain.com/user@domain.com/ \ - /var/lib/docker/volumes/mailcowdockerized_vmail-vol/_data/yourdomain.com/user@domain.com/ - -# Fix permissions -chown -R 5000:5000 /var/lib/docker/volumes/mailcowdockerized_vmail-vol/_data/yourdomain.com/user@domain.com/ - -# Unmount -kopia unmount /mnt/restore - -# Restart Dovecot to recognize changes -docker compose restart dovecot-mailcow -``` - -### Scenario 3: Database Recovery Only - -If only the database is corrupted but email data is intact: - -#### Option A: Using Mailcow Native Restore (Recommended) - -```bash -cd /opt/mailcow-dockerized - -# Run the restore script -./helper-scripts/backup_and_restore.sh restore - -# When prompted: -# - Backup location: /opt/mailcow-backups -# - Select the most recent backup -# - Select [ 6 ] - SQL DB (database only) -``` - -The script will: -1. Stop mailcow -2. Restore the MySQL database from the mariabackup archive -3. Update mailcow.conf with the restored database credentials -4. Restart mailcow - -#### Option B: Manual Database Restore from Kopia - -If local backups are unavailable: - -```bash -cd /opt/mailcow-dockerized - -# Stop Mailcow -docker compose down - -# Start only MySQL -docker compose up -d mysql-mailcow - -# Wait for MySQL -sleep 30 - -# Restore from Kopia database dump -kopia snapshot list --tags database -kopia restore /tmp/db-restore/ - -# Import the dump -LATEST_DUMP=$(ls -t /tmp/db-restore/mailcow_*.sql | head -1) -docker compose exec -T mysql-mailcow mysql -u root -p$(grep DBROOT mailcow.conf | cut -d'=' -f2) < "$LATEST_DUMP" - -# Start all services -docker compose down -docker compose up -d - -# Verify -docker compose logs -f -``` - -### Scenario 4: Configuration Recovery Only - -If you only need to restore configuration files: - -#### Option A: From Mailcow Backup - -```bash -# Find the most recent backup -LATEST_BACKUP=$(ls -td /opt/mailcow-backups/mailcow-* | head -1) - -# Stop Mailcow -cd /opt/mailcow-dockerized -docker compose down - -# Backup current config (just in case) -cp mailcow.conf mailcow.conf.pre-restore -cp docker-compose.yml docker-compose.yml.pre-restore - -# Restore mailcow.conf from backup -cp "$LATEST_BACKUP/mailcow.conf" ./mailcow.conf - -# If you also need other config files from data/conf/, -# you would need to extract them from the backup archives - -# Restart -docker compose up -d -``` - -#### Option B: From Kopia Snapshot - -```bash -# Restore config snapshot to temporary location -kopia restore /tmp/mailcow-restore/ - -# Stop Mailcow -cd /opt/mailcow-dockerized -docker compose down - -# Backup current config (just in case) -cp mailcow.conf mailcow.conf.pre-restore -cp docker-compose.yml docker-compose.yml.pre-restore - -# Restore specific files -cp /tmp/mailcow-restore/mailcow.conf ./ -cp /tmp/mailcow-restore/docker-compose.yml ./ -cp -r /tmp/mailcow-restore/data/conf/* ./data/conf/ - -# Restart -docker compose up -d -``` - -## Verification and Testing - -### Regular Backup Verification - -Perform monthly restore tests to ensure backups are valid: - -```bash -# Test restore to temporary location -mkdir -p /tmp/backup-test -kopia snapshot list --tags mailcow -kopia restore /tmp/backup-test/ - -# Verify files exist and are readable -ls -lah /tmp/backup-test/ -cat /tmp/backup-test/mailcow.conf - -# Cleanup -rm -rf /tmp/backup-test/ -``` - -### Backup Monitoring Script - -Create `/opt/scripts/check-mailcow-backup.sh`: - -```bash -#!/bin/bash - -# Check last backup age -LAST_BACKUP=$(kopia snapshot list --tags mailcow --json | jq -r '.[0].startTime') -LAST_BACKUP_EPOCH=$(date -d "$LAST_BACKUP" +%s) -NOW=$(date +%s) -AGE_HOURS=$(( ($NOW - $LAST_BACKUP_EPOCH) / 3600 )) - -if [ $AGE_HOURS -gt 26 ]; then - echo "WARNING: Last Mailcow backup is $AGE_HOURS hours old" - # Send alert (email, Slack, etc.) - exit 1 -else - echo "OK: Last backup $AGE_HOURS hours ago" -fi -``` - -## Disaster Recovery Checklist - -When disaster strikes, follow this checklist: - -- [ ] Confirm scope of failure (server, storage, specific component) -- [ ] Gather server information (hostname, IP, DNS records) -- [ ] Access offsite backup vault -- [ ] Provision new server (if needed) -- [ ] Install Docker and dependencies -- [ ] Connect to Kopia repository -- [ ] Restore configurations first -- [ ] Restore database -- [ ] Restore email data -- [ ] Start services and verify -- [ ] Test email sending/receiving -- [ ] Verify webmail access -- [ ] Check DNS records and update if needed -- [ ] Document any issues encountered -- [ ] Update recovery procedures based on experience - -## Important Notes - -1. **DNS**: Keep DNS records documented separately. Recovery includes updating DNS if server IP changes. - -2. **SSL Certificates**: Let's Encrypt certificates are in the backup but may need renewal. Mailcow will handle this automatically. - -3. **Permissions**: Docker volumes have specific UID/GID requirements: - - vmail: `5000:5000` - - mysql: `999:999` - -4. **Testing**: Always test recovery procedures in a lab environment before trusting them in production. - -5. **Documentation**: Keep this guide and server details in a separate location (printed copy, password manager, etc.). - -6. **Retention Policy**: Review Kopia retention settings periodically to balance storage costs with recovery needs. - -## Backup Architecture Notes - -### Why Two Backup Layers? - -**Mailcow Native Backups** (Tier 1): -- ✅ Component-aware (knows about mailcow's structure) -- ✅ Uses mariabackup for consistent MySQL hot backups -- ✅ Fast, selective restore (can restore just one component) -- ✅ Architecture-aware (handles x86/ARM differences) -- ❌ No deduplication (full copies each time) -- ❌ Limited to local storage initially - -**Kopia Snapshots** (Tier 2): -- ✅ Deduplication and compression -- ✅ Efficient offsite replication to vaults -- ✅ Point-in-time recovery across multiple versions -- ✅ Disaster recovery to completely new infrastructure -- ❌ Less component-aware (treats as files) -- ❌ Slower for granular component restore - -### Storage Efficiency - -Using this two-tier approach: -- **Local**: Mailcow creates ~7 days of native backups (may be large, but short retention) -- **Offsite**: Kopia deduplicates these backups for long-term vault storage (much smaller) - -Example storage calculation (10GB mailbox): -- Local: 7 days × 10GB = ~70GB (before compression) -- Kopia (offsite): First backup ~10GB, subsequent backups only store changes (might be <1GB/day after dedup) - -### Compression Formats - -Mailcow's script creates `.tar.zst` (Zstandard) or `.tar.gz` (gzip) files: -- **Zstandard** (modern): Better compression ratio, faster (recommended) -- **Gzip** (legacy): Wider compatibility with older systems - -Verify your backup compression: -```bash -ls -lh /opt/mailcow-backups/mailcow-*/ -# Look for .tar.zst (preferred) or .tar.gz -``` - -### Cross-Architecture Considerations - -**Important for ARM/x86 Migration**: - -Mailcow's backup script is architecture-aware. When restoring: -- **Rspamd data** cannot be restored across different architectures (x86 ↔ ARM) -- **All other components** (vmail, mysql, redis, postfix, crypt) are architecture-independent - -If migrating between architectures: -```bash -# Restore everything EXCEPT rspamd -# Select components individually: vmail, mysql, redis, postfix, crypt -# Skip rspamd - it will rebuild its learning database over time -``` - -### Testing Your Backups - -**Monthly Test Protocol**: - -1. **Verify local backups exist**: - ```bash - ls -lh /opt/mailcow-backups/ - # Should see recent dated directories - ``` - -2. **Verify Kopia snapshots**: - ```bash - kopia snapshot list --tags mailcow - # Should see recent snapshots - ``` - -3. **Test restore in lab** (recommended quarterly): - - Spin up a test VM - - Restore from Kopia - - Run mailcow native restore - - Verify email delivery and webmail access - -## Additional Resources - -- [Mailcow Official Backup Documentation](https://docs.mailcow.email/backup_restore/b_n_r-backup/) -- [Kopia Documentation](https://kopia.io/docs/) -- [Docker Volume Backup Best Practices](https://docs.docker.com/storage/volumes/#back-up-restore-or-migrate-data-volumes) - -## Revision History - -| Date | Version | Changes | -|------|---------|---------| -| 2026-02-10 | 1.1 | Integrated mailcow native backup_and_restore.sh script as primary backup method | -| 2026-02-10 | 1.0 | Initial documentation | - ---- - -**Last Updated**: February 10, 2026 -**Maintained By**: System Administrator -**Review Schedule**: Quarterly