Verbatim M-DISC Cold Storage
Overview
Verbatim M-DISC provides immutable cold storage for critical recovery credentials. The inorganic data layer survives conditions that destroy conventional optical media.
| Property | Value |
|---|---|
Media |
Verbatim M-DISC DVD-R or BD-R |
Device |
|
Lifespan |
1000+ years (inorganic layer) |
Encryption |
age (before burning) |
Frequency |
Annual (or when keys change) |
Storage |
Fireproof safe, bank deposit box, off-site family |
What to Archive
Use the full 100GB BD-R capacity. M-DISC survives 1000+ years - archive everything irreplaceable.
| Priority | Contents | Size |
|---|---|---|
P0 - Critical |
GPG keys, SSH keys, age keys, LUKS headers, gopass stores |
~100 MB |
P1 - Irreplaceable |
|
~120 MB+ |
P2 - Work |
|
~20 GB |
P3 - Configs |
|
~700 MB |
P4 - Reference |
|
Variable |
Total: ~21-25 GB (fits easily on 100GB BD-R with room for growth)
Prerequisites
# Install burning tools
# DVD-R M-Disc: cdrtools (wodim)
# BD-R M-Disc: dvd+rw-tools (growisofs) - REQUIRED for Blu-ray!
sudo pacman -S cdrtools dvd+rw-tools
Detect Drive Capabilities
# Check drive type using dvd+rw-mediainfo (BD-aware tool)
dvd+rw-mediainfo /dev/sr0 2>&1 | awk '
/INQUIRY/ {print "Drive: " $0}
/Mounted Media/ {print $0; if (/BD/) print ">>> BD-R: Use growisofs (Step 17b)"; else print ">>> DVD-R: Use wodim (Step 17a)"}
/Disc status/ {print $0}
/Free Blocks/ {print $0}
'
Expected output (blank BD-R M-DISC):
Drive: INQUIRY: [ASUS][BW-16D1HT][3.10]
Mounted Media: 41h, BD-R SRM
>>> BD-R: Use growisofs (Step 17b)
Disc status: blank
Free Blocks: 11826176*2KB
If Disc status: complete, the disc is already burned. Insert a blank disc.
|
wodim is for CD/DVD burning only. BD-R requires growisofs from dvd+rw-tools.
|
Procedure
Step 1: Create Staging Directory
mkdir -p ~/cold-storage-$(date +%Y%m%d)
cd ~/cold-storage-$(date +%Y%m%d)
Step 2: Export GPG Keys
gpg --export-secret-keys --armor > gpg-secret-keys.asc
gpg --export --armor > gpg-public-keys.asc
gpg --export-ownertrust > gpg-ownertrust.txt
Step 3: Copy SSH Keys
mkdir ssh-keys
cp ~/.ssh/id_* ssh-keys/ 2>/dev/null || true
cp ~/.ssh/config ssh-keys/ 2>/dev/null || true
Step 4: Copy age Keys
mkdir age-keys
cp ~/.secrets/.metadata/keys/master.age.key age-keys/
cp ~/.secrets/.metadata/keys/master.age.pub age-keys/
Step 6: Copy Password Stores
cp -r ~/.password-store ./password-store/
cp -r ~/.local/share/gopass/stores/v3 ./gopass-v3/
Step 8: Copy atelier (All Repos/Projects)
# Excludes: oil.nvim temp dirs, FUSE trash, node_modules, __pycache__
rsync -av --exclude='oil' --exclude='.Trash-*' \
--exclude='node_modules' --exclude='__pycache__' \
--exclude='.venv' --exclude='venv' \
~/atelier/ ./atelier/
Step 11: Create Manifest
cat > MANIFEST.txt << EOF
=== COLD STORAGE ARCHIVE ===
Date: $(date +%Y-%m-%d)
Host: $(hostname)
User: $USER
Contents:
- gpg-secret-keys.asc GPG secret keyring
- gpg-public-keys.asc GPG public keyring
- gpg-ownertrust.txt GPG trust database
- ssh-keys/ SSH keypairs
- age-keys/ age encryption keys (dsec master)
- luks-headers/ LUKS header backups (age-encrypted)
- password-store/ gopass v2 database (GPG encrypted)
- gopass-v3/ gopass v3 database (GPG encrypted)
- Pictures/ Personal photos
- atelier/ All repos, projects, documentation
- mozilla/ Firefox profiles, bookmarks, sessions
- Documents/ Personal documents
Recovery Order:
1. gpg --import gpg-secret-keys.asc
2. gpg --import-ownertrust gpg-ownertrust.txt
3. Copy ssh-keys/* to ~/.ssh/, chmod 600
4. Copy age-keys/* to ~/.secrets/.metadata/keys/
5. Copy password-store/ to ~/.password-store/
6. Copy gopass-v3/ to ~/.local/share/gopass/stores/v3/
7. Copy Pictures/ to ~/Pictures/
8. Copy atelier/ to ~/atelier/
9. Copy mozilla/ to ~/.mozilla/
10. Copy Documents/ to ~/Documents/
11. Decrypt LUKS headers: age -d -i master.age.key header.img.age > header.img
12. Restore LUKS header: cryptsetup luksHeaderRestore /dev/nvmeXnYpZ --header-backup-file header.img
EOF
Step 12: Create Tarball
cd ~
tar -cvf cold-storage-$(date +%Y%m%d).tar cold-storage-$(date +%Y%m%d)/
Step 13: Encrypt with age
age -e -R ~/.config/age/recipients.txt \
-o cold-storage-$(date +%Y%m%d).tar.age \
cold-storage-$(date +%Y%m%d).tar
Step 14: Create Recovery Instructions
cat > RECOVERY-README.txt << 'EOF'
╔══════════════════════════════════════════════════════════════════════════════╗
║ COLD STORAGE ARCHIVE - RECOVERY ║
╠══════════════════════════════════════════════════════════════════════════════╣
║ ║
║ CONTENTS: ║
║ - P0-CRITICAL-*.tar.age GPG, SSH, pass, secrets, age keys (RESTORE FIRST)║
║ - FULL-BACKUP-*.tar.age Complete archive (deduplicated) ║
║ ║
║ FULL-BACKUP INCLUDES: ║
║ ~/.secrets, ~/.gnupg, ~/.password-store, ~/.ssh, ~/.pki ║
║ ~/.config, ~/.local, ~/.ansible, ~/.claude, ~/.terraform.d ║
║ ~/bin, ~/.mozilla, ~/atelier, ~/Documents, ~/Pictures ║
║ Shell history files ║
║ ║
║ DECRYPT: ║
║ age -d -i ~/.secrets/.metadata/keys/master.age.key FILE.age > FILE.tar ║
║ tar -xvf FILE.tar ║
║ ║
║ OR PIPE DIRECTLY: ║
║ age -d -i ~/.secrets/.metadata/keys/master.age.key FILE.age | tar -xvf - ║
║ ║
║ RESTORE ORDER: ║
║ 1. Restore P0-CRITICAL first (you need keys to decrypt other things) ║
║ 2. Restore FULL-BACKUP (extracts to original paths) ║
║ 3. Run: chown -R $USER:$USER ~ ║
║ ║
║ IF YOU LOST YOUR AGE KEY: ║
║ These files are UNRECOVERABLE. Store age key in multiple locations. ║
║ ║
╚══════════════════════════════════════════════════════════════════════════════╝
EOF
Step 16: Create ISO
# -iso-level 3 required for files >4GB (FULL-BACKUP can be 30GB+)
genisoimage -V "BACKUP-$(date +%Y-%m-%d)" -iso-level 3 -J -r \
-o COLD-STORAGE-$(date +%Y%m%d).iso \
*.tar.age RECOVERY-README.txt SHA256SUMS.txt
Storage Locations (3 Copies Minimum)
| Copy | Location | Protection |
|---|---|---|
Copy 1 |
Home fireproof safe |
Fire, immediate access |
Copy 2 |
Bank safe deposit box |
Theft, flood, fire |
Copy 3 |
Trusted family (different city) |
Geographic disaster |
|
Label each disc clearly:
|
Verification Schedule
| Frequency | Action |
|---|---|
Annual |
Burn new disc with current keys |
After key rotation |
Burn new disc immediately |
Every 5 years |
Verify existing discs still readable |
Troubleshooting
| Issue | Solution |
|---|---|
|
Check device: |
|
Run with sudo: |
|
BD-R drive detected. Use |
|
File >4GB requires |
|
Output to directory with space (not |
Disc not detected after burn |
Wait 10 seconds, try |
Checksum mismatch |
Bad burn - use new disc, lower speed |
Exploring M-DISC Contents
Advanced commands for examining archived discs.
Mount and Basic Exploration
# Mount the disc (read-only by default)
sudo mkdir -p /mnt/cdrom
sudo mount /dev/sr0 /mnt/cdrom
# List contents with details
ls -la /mnt/cdrom/
# Total size of everything on disc
du -sh /mnt/cdrom/
Find Files with Sizes
# List all files with human-readable sizes using find + awk
find /mnt/cdrom -type f -exec ls -lh {} \; | awk '{print $5, $9}'
# Find largest files, sorted descending
find /mnt/cdrom -type f -exec du -h {} \; | sort -rh | head -10
Identify File Types
# Check what type of files are on disc
file /mnt/cdrom/*
# Example output:
# /mnt/cdrom/FULL-BACKUP-2026-01-14.tar.age: age encrypted file, X25519 recipient
# /mnt/cdrom/P0-CRITICAL-2026-01-14.tar.age: age encrypted file, X25519 recipient
# /mnt/cdrom/RECOVERY-README.txt: Unicode text, UTF-8 text
# /mnt/cdrom/SHA256SUMS.txt: ASCII text
Read Text Files with Line Numbers
# Read first 50 lines of a file
awk 'NR>=1 && NR<=50' /mnt/cdrom/RECOVERY-README.txt
# Read lines 10-30
awk 'NR>=10 && NR<=30' /mnt/cdrom/RECOVERY-README.txt
# Count total lines
wc -l /mnt/cdrom/RECOVERY-README.txt
Extract Disc Metadata
# Get volume label
blkid /dev/sr0 | grep -oP 'LABEL="\K[^"]+'
# Get all block device info
blkid /dev/sr0
# Check disc type and capacity
lsblk /dev/sr0
Verify Checksums
# View stored checksums
cat /mnt/cdrom/SHA256SUMS.txt
# Verify all files against checksums
cd /mnt/cdrom && sha256sum -c SHA256SUMS.txt
# Manually verify specific file
sha256sum /mnt/cdrom/FULL-BACKUP-*.tar.age
Search Inside Archives (Without Extracting)
# List contents of tar.age (decrypt + list in one pipe)
age -d -i ~/.secrets/.metadata/keys/master.age.key /mnt/cdrom/P0-CRITICAL-*.tar.age | tar -tvf - | head -50
# Search for specific file in archive
age -d -i ~/.secrets/.metadata/keys/master.age.key /mnt/cdrom/FULL-BACKUP-*.tar.age | tar -tvf - | grep -i "ssh"
Unmount and Eject
# Unmount
sudo umount /mnt/cdrom
# Eject disc
eject /dev/sr0
# Close tray (if supported)
eject -t /dev/sr0
Quick Reference Table
| Task | Command |
|---|---|
Mount disc |
|
List with sizes |
|
Largest files |
|
File types |
|
Volume label |
|
Read lines N-M |
|
Verify checksums |
|
List tar.age contents |
|
Unmount + eject |
|