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

/dev/sr0

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

~/Pictures/ - personal photos, family memories

~120 MB+

P2 - Work

~/atelier/ - all repos, projects, documentation, code

~20 GB

P3 - Configs

~/.mozilla/ (Firefox profiles, bookmarks, sessions)

~700 MB

P4 - Reference

~/Documents/ - legal, financial, personal documents

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 5: Copy LUKS Headers

mkdir luks-headers
cp ~/.secrets/luks-headers/*.age luks-headers/

Step 6: Copy Password Stores

cp -r ~/.password-store ./password-store/
cp -r ~/.local/share/gopass/stores/v3 ./gopass-v3/

Step 7: Copy Pictures

cp -r ~/Pictures ./Pictures/

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 9: Copy Firefox Profiles

cp -r ~/.mozilla ./mozilla/

Step 10: Copy Documents

cp -r ~/Documents ./Documents/ 2>/dev/null || true

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 15: Create SHA256 Checksums

sha256sum *.tar.age > SHA256SUMS.txt
cat SHA256SUMS.txt

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

Step 17: Burn to M-DISC

Step 17a: DVD-R M-DISC (wodim)

wodim -v dev=/dev/sr0 speed=4 COLD-STORAGE-$(date +%Y%m%d).iso

Step 17b: BD-R M-DISC (growisofs)

# For Blu-ray drives - REQUIRED if "Can write BD: 1"
growisofs -dvd-compat -Z /dev/sr0=COLD-STORAGE-$(date +%Y%m%d).iso
wodim does NOT support BD-R. If you get "Unspecified command not implemented", use growisofs.

Step 18: Verify Burn

# Eject and re-insert disc
eject /dev/sr0

# Mount and verify
sudo mkdir -p /mnt/cdrom
sudo mount /dev/sr0 /mnt/cdrom

# Compare checksums - MUST match
sha256sum /mnt/cdrom/*.age
cat /mnt/cdrom/SHA256SUMS.txt

sudo umount /mnt/cdrom

Step 19: Cleanup Staging

# Shred sensitive plaintext
shred -vzn 3 cold-storage-$(date +%Y%m%d).tar

# Remove staging directory
rm -rf ~/cold-storage-$(date +%Y%m%d)/

# Keep the .age and .iso for additional copies

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:

  • Date burned

  • "ENCRYPTED - age"

  • "RECOVERY.txt on disc"

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

wodim: No such file or directory

Check device: ls -la /dev/sr0

Cannot open SCSI driver

Run with sudo: sudo wodim …​

wodim: Unspecified command not implemented for this drive

BD-R drive detected. Use growisofs instead of wodim (Step 17b)

genisoimage: Value too large for defined data type

File >4GB requires -iso-level 3 flag (see Step 16)

genisoimage: Disk quota exceeded

Output to directory with space (not /tmp/). Use $HOME/mdisc-backup/ or similar.

Disc not detected after burn

Wait 10 seconds, try eject && eject -t

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

sudo mount /dev/sr0 /mnt/cdrom

List with sizes

find /mnt/cdrom -type f -exec ls -lh {} \; | awk '{print $5, $9}'

Largest files

find /mnt/cdrom -type f -exec du -h {} \; | sort -rh | head -10

File types

file /mnt/cdrom/*

Volume label

blkid /dev/sr0 | grep -oP 'LABEL="\K[^"]+'

Read lines N-M

awk 'NR>=N && NR⇐M' FILE

Verify checksums

cd /mnt/cdrom && sha256sum -c SHA256SUMS.txt

List tar.age contents

age -d -i KEY FILE.tar.age | tar -tvf -

Unmount + eject

sudo umount /mnt/cdrom && eject