From 861f3663e597f948f5b3d47c5eb8f234f4d33032 Mon Sep 17 00:00:00 2001 From: Administrator Date: Wed, 11 Feb 2026 12:40:15 +0000 Subject: [PATCH] docs: update backup-mailcow --- backup-mailcow.md | 870 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 870 insertions(+) create mode 100644 backup-mailcow.md diff --git a/backup-mailcow.md b/backup-mailcow.md new file mode 100644 index 0000000..a0e0da3 --- /dev/null +++ b/backup-mailcow.md @@ -0,0 +1,870 @@ +--- +title: Mailcow Backup and Restore Strategy +description: Mailcow backup +published: true +date: 2026-02-11T12:40:05.470Z +tags: +editor: markdown +dateCreated: 2026-02-11T01:20:59.127Z +--- + +# 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