Seagate Backup SSD
Overview
Secondary external SSD for weekly redundancy. Mirrors the primary Seagate drive for 2-copy USB protection. Uses simpler flat directory structure (no subvolumes) for straightforward recovery.
| Property | Value |
|---|---|
Capacity |
2TB USB-C |
Encryption |
LUKS2 (aes-xts-plain64, 256-bit) |
Filesystem |
btrfs with zstd compression (no subvolumes) |
Mount Point |
|
Frequency |
Weekly (sync from primary) |
Passphrase |
|
Why Two Drives
Defense in Depth
| Risk | Mitigation |
|---|---|
Drive failure |
Second drive has identical data |
Ransomware |
Offline drive can’t be encrypted by malware |
Theft |
Off-site drive survives home break-in |
User error |
Weekly sync means max 7 days data loss |
Fire/flood |
Geographic separation (one off-site) |
Primary vs Backup Differences
| Aspect | Primary | Backup |
|---|---|---|
Structure |
btrfs subvolumes (@secrets, @configs, etc.) |
Flat directories (secrets/, configs/, etc.) |
Mount |
4 separate mounts per subvolume |
Single mount at root |
Snapshots |
Yes (@snapshots subvolume) |
No (mirror only) |
Use case |
Daily incremental backup |
Weekly full mirror |
Recovery |
Granular (mount individual subvols) |
Simple (mount, copy) |
Why flat on backup? Simplicity for disaster recovery. When panicking, mount + cp is easier than subvolume mounts.
Directory Structure
/mnt/seagate-backup/
├── secrets/ # Mirror of primary @secrets
│ ├── .secrets/
│ ├── gnupg/
│ ├── password-store/
│ ├── ssh/
│ └── pki/
├── configs/ # Mirror of primary @configs
│ ├── dotconfig/
│ ├── dotlocal/
│ ├── ansible/
│ ├── claude/
│ └── bin/
├── backups/ # Mirror of primary @backups
│ └── mozilla/
└── storage/ # Mirror of primary @storage
├── atelier/
├── Documents/
└── Pictures/
Script Internals
seagate-backup-mount
Simpler than primary - single mount, no subvolumes:
#!/bin/bash
set -euo pipefail
DEVICE="${1:-/dev/sda1}" # Default device (different from primary)
MAPPER_NAME="seagate-backup" # Different mapper name
MOUNT_BASE="/mnt/seagate-backup"
# Idempotent check
if mountpoint -q "$MOUNT_BASE" 2>/dev/null; then
echo "Already mounted at $MOUNT_BASE/"
ls -la $MOUNT_BASE/
exit 0
fi
# Open LUKS - prompts for passphrase
echo "Opening LUKS volume..."
sudo cryptsetup open "$DEVICE" "$MAPPER_NAME"
# Single mount at root (no subvolumes)
sudo mkdir -p "$MOUNT_BASE"
sudo mount /dev/mapper/$MAPPER_NAME $MOUNT_BASE
# Set ownership
sudo chown $USER:$USER $MOUNT_BASE
sudo chown -R $USER:$USER $MOUNT_BASE/* 2>/dev/null || true
echo "Mounted at $MOUNT_BASE/"
ls -la $MOUNT_BASE/
Key difference from primary: No -o subvol=@name. Mounts btrfs root directly.
seagate-sync-to-backup
Mirrors all 4 directories from primary to backup:
#!/bin/bash
set -euo pipefail
PRIMARY="/mnt/seagate"
BACKUP="/mnt/seagate-backup"
# Verify BOTH drives mounted
if ! mountpoint -q "$PRIMARY/secrets" 2>/dev/null; then
echo "Primary not mounted. Run seagate-primary-mount first."
exit 1
fi
if ! mountpoint -q "$BACKUP" 2>/dev/null; then
echo "Backup not mounted. Run seagate-backup-mount first."
exit 1
fi
echo "=== SEAGATE SYNC: PRIMARY → BACKUP ==="
echo "Started: $(date)"
# Mirror each directory
# rsync from primary subvolume mounts to backup flat dirs
echo ">>> Syncing secrets..."
rsync -av --delete $PRIMARY/secrets/ $BACKUP/secrets/
echo ">>> Syncing configs..."
rsync -av --delete $PRIMARY/configs/ $BACKUP/configs/
echo ">>> Syncing backups..."
rsync -av --delete $PRIMARY/backups/ $BACKUP/backups/
echo ">>> Syncing storage..."
rsync -av --delete $PRIMARY/storage/ $BACKUP/storage/
echo "=== SYNC COMPLETE ==="
echo "Finished: $(date)"
# Show sizes for verification
echo ""
echo "Primary:"
du -sh $PRIMARY/secrets $PRIMARY/configs $PRIMARY/backups $PRIMARY/storage
du -sh $PRIMARY/
echo ""
echo "Backup:"
du -sh $BACKUP/secrets $BACKUP/configs $BACKUP/backups $BACKUP/storage
du -sh $BACKUP/
Why sync instead of backup script?
-
Primary already has clean, --delete’d mirrors
-
Sync just copies those mirrors to second drive
-
Faster than re-running all rsync from source
-
Ensures backup matches primary exactly
Weekly Workflow
Complete Sequence
# 1. Run daily backup to primary first
seagate-primary-mount
seagate-primary-backup
# 2. Mount backup drive
seagate-backup-mount
# 3. Sync primary → backup
seagate-sync-to-backup
# 4. Verify sizes match
# Primary and backup should show identical sizes
# 5. Unmount both
seagate-primary-umount
seagate-backup-umount
Expected Output
=== SEAGATE SYNC: PRIMARY → BACKUP ===
Started: Tue Feb 17 08:34:00 PM PST 2026
>>> Syncing secrets...
>>> Syncing configs...
>>> Syncing backups...
>>> Syncing storage...
=== SYNC COMPLETE ===
Finished: Tue Feb 17 08:36:00 PM PST 2026
Primary:
235M /mnt/seagate/secrets
6.9G /mnt/seagate/configs
692M /mnt/seagate/backups
20G /mnt/seagate/storage
28G /mnt/seagate/
Backup:
235M /mnt/seagate-backup/secrets
6.9G /mnt/seagate-backup/configs
692M /mnt/seagate-backup/backups
20G /mnt/seagate-backup/storage
28G /mnt/seagate-backup/
Off-Site Rotation Strategy
For true disaster protection, rotate one drive off-site:
Rotation Schedule
| Week | Primary Drive | Backup Drive | Action |
|---|---|---|---|
Week 1 |
Home (daily use) |
Off-site (bank/family) |
Daily backups to primary only |
Week 2 |
Home (daily use) |
Bring home for sync |
Sync primary → backup, return off-site |
Week 3 |
Home (daily use) |
Off-site |
Daily backups continue |
Week 4 |
Home (daily use) |
Bring home for sync |
Sync, return off-site |
Off-Site Options
| Location | Pros | Cons |
|---|---|---|
Bank safe deposit box |
Fire/flood/theft proof, secure |
Limited access hours, monthly cost |
Trusted family (different city) |
Geographic separation, free |
Requires trust, travel for rotation |
Office desk drawer |
Convenient access |
Same metro area (shared disaster risk) |
|
Label drives clearly: "SEAGATE-1 PRIMARY" and "SEAGATE-2 BACKUP". Use different colored labels or tape. |
Initial Setup
One-time setup for the backup drive:
# 1. Create LUKS2 container
sudo cryptsetup luksFormat --type luks2 \
--cipher aes-xts-plain64 \
--key-size 512 \
--hash sha256 \
--iter-time 5000 \
/dev/sdX1
# 2. Open and format (no subvolumes - flat btrfs)
sudo cryptsetup open /dev/sdX1 seagate-backup
sudo mkfs.btrfs -L seagate-backup /dev/mapper/seagate-backup
# 3. Mount and create directories
sudo mount /dev/mapper/seagate-backup /mnt/seagate-backup
sudo mkdir -p /mnt/seagate-backup/{secrets,configs,backups,storage}
sudo chown -R $USER:$USER /mnt/seagate-backup/*
# 4. Initial sync from primary (primary must be mounted)
seagate-sync-to-backup
# 5. Unmount
sudo umount /mnt/seagate-backup
sudo cryptsetup close seagate-backup
LUKS Header Backup
# Backup header
sudo cryptsetup luksHeaderBackup /dev/sdX1 \
--header-backup-file seagate-ssd2-$(date +%Y%m%d).img
# Encrypt with age
age -e -R ~/.config/age/recipients.txt \
-o ~/.secrets/luks-headers/seagate-ssd2-$(date +%Y%m%d).img.age \
seagate-ssd2-$(date +%Y%m%d).img
# Shred plaintext
shred -vzn 3 seagate-ssd2-$(date +%Y%m%d).img
Recovery from Backup Drive
If primary is lost or primary workstation destroyed:
Mount Backup
# On new/recovered system
sudo cryptsetup open /dev/sdX1 seagate-backup
sudo mount /dev/mapper/seagate-backup /mnt/seagate-backup
Restore Critical Secrets
# 1. Restore dsec/age
mkdir -p ~/.secrets
rsync -av /mnt/seagate-backup/secrets/.secrets/ ~/.secrets/
chmod 700 ~/.secrets
chmod 600 ~/.secrets/.metadata/keys/*
# 2. Restore GPG
rsync -av /mnt/seagate-backup/secrets/gnupg/ ~/.gnupg/
chmod 700 ~/.gnupg
chmod 600 ~/.gnupg/*
# 3. Restore gopass
rsync -av /mnt/seagate-backup/secrets/password-store/ ~/.password-store/
mkdir -p ~/.local/share/gopass/stores
rsync -av /mnt/seagate-backup/secrets/password-store/ ~/.local/share/gopass/stores/root/
# 4. Restore SSH
rsync -av /mnt/seagate-backup/secrets/ssh/ ~/.ssh/
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_* ~/.ssh/config 2>/dev/null || true