🔧 Entrypoint: - Detect wrong ownership on /var/lib/tor and /var/lib/tor/keys at startup with actionable chown commands before Tor fails cryptically in Phase 4 - Accept DEBUG=TRUE, DEBUG=1, DEBUG=yes (case-insensitive) - Fix signal trap bug: inner cleanup_verify_tmp no longer overwrites the global TERM/INT handler (could skip graceful shutdown) 🛡️ Security: - Deprecate all versions < v1.1.5 (CVE-2025-15467, OpenSSL CVSS 9.8) - Add deprecation notice to README and SECURITY.md - Update lifecycle tables in CHANGELOG and SECURITY 🐛 Bug Fixes: - Fix bootstrap detection in migrate-from-official.sh (parsed non-existent "bootstrap_percent" field — now "bootstrap") - Fix health JSON docs across 4 files: uptime_seconds → uptime, add missing pid/errors fields, correct reachable type to string - Fix validate.yml: bash -n → sh -n (POSIX script, not bash) 📚 Documentation: - Add "Bind Mount Ownership" troubleshooting section to README - Fix chown 1000:1000 typo → 100:101 in TROUBLESHOOTING-BRIDGE-MIGRATION.md - Add [1.1.6] changelog entry - Update version references across 20+ files to v1.1.6 - Update 47x alpine:3.22.2 → 3.23.3 across migration docs/scripts - Fix tool count 4 → 5 in DEPLOYMENT, ARCHITECTURE, TROUBLESHOOTING - Remove 5 broken links (CLAUDE.md, CONTRIBUTORS.md, SECURITY-AUDIT-REPORT.md) - Fix stale image tags (:1.1.1/:1.1.2 → :latest) in 4 files - Rewrite PR template as clean reusable form ⚙️ Workflow (release.yml): - Fix duplicate title in release body (name + body both had ## 🧅 header) - Fix trailing --- not being stripped from changelog extract - Fix Full Changelog link comparing current tag to itself - Extract Alpine version from Dockerfile instead of hardcoding - Add fetch-depth: 0 for git history in release-notes job - Fix fallback commit range when no conventional commits found 🐳 Dockerfiles: - Fix stale base.name label (alpine:3.23.0 → alpine:3.23.3) - Fix trailing whitespace after backslash in Dockerfile.edge 📋 Templates: - Update cosmos-compose and docker-compose versions to 1.1.6
15 KiB
🔐 Backup & Recovery Guide - Tor Guard Relay
Complete instructions for backing up and restoring your Tor relay's identity, keys, and configuration data.
Table of Contents
Why Backups Matter
Your Tor relay's identity is permanent. Once established, it becomes part of the Tor network's fabric. Losing these keys means:
- 🚫 Loss of your relay's fingerprint
- 📉 Loss of reputation built over time
- 🔄 New relay starting from zero
- ⏰ 8+ days to regain guard flag
Backup your keys immediately after first successful bootstrap.
What to Backup
🔑 Critical Files (Preserve Forever)
Located in /var/lib/tor/:
| File | Purpose | Restore Impact |
|---|---|---|
keys/ed25519_master_id_secret_key |
Master identity key | CRITICAL - Defines relay identity |
keys/ed25519_signing_secret_key |
Signing key | CRITICAL - Signs all operations |
keys/secret_onion_key |
Onion key | CRITICAL - Onion address generation |
fingerprint |
Your relay fingerprint | Reference only (can regenerate) |
📋 Important Files (Backup Regularly)
| File | Purpose | Restore Impact |
|---|---|---|
cached-consensus |
Current Tor consensus | Nice to have (rebuilds automatically) |
cached-descriptors |
Relay descriptors | Nice to have (rebuilds automatically) |
state |
Relay state file | Optional (recreated on startup) |
⚙️ Configuration (Backup Before Changes)
| File | Location | Purpose |
|---|---|---|
relay.conf |
Host machine | Your relay configuration |
torrc |
/etc/tor/torrc (in container) |
Mounted copy of relay.conf |
Backup Methods
Quick Backup (Simplest)
Copy the keys directory straight out of a running container:
docker cp <container-name>:/var/lib/tor/keys ./RelayKeyBackup
That's it. The RelayKeyBackup/ folder now contains your relay's identity keys. Store it somewhere safe.
Method 1: Docker Volume Backup (Recommended)
Pros: Complete, easy to restore, version-controlled
Cons: Requires disk space for full volume copy
Step 1: Stop the Relay Gracefully
# Stop relay (allows clean shutdown)
docker stop guard-relay
# Wait for graceful shutdown
sleep 5
# Verify stopped
docker ps | grep guard-relay
Step 2: Backup the Volume
# Create backup directory
mkdir -p ~/tor-relay-backups/$(date +%Y-%m-%d)
# Backup the tor-guard-data volume
docker run --rm \
-v tor-guard-data:/data \
-v ~/tor-relay-backups/$(date +%Y-%m-%d):/backup \
alpine tar czf /backup/tor-data-$(date +%s).tar.gz -C /data .
# Verify backup created
ls -lh ~/tor-relay-backups/$(date +%Y-%m-%d)/
Output:
-rw-r--r-- 1 user user 2.5M Jan 1 12:00 tor-data-1704110400.tar.gz
Step 3: Backup the Logs Volume
# Backup logs for audit trail
docker run --rm \
-v tor-guard-logs:/data \
-v ~/tor-relay-backups/$(date +%Y-%m-%d):/backup \
alpine tar czf /backup/tor-logs-$(date +%s).tar.gz -C /data .
# Verify
ls -lh ~/tor-relay-backups/$(date +%Y-%m-%d)/
Step 4: Restart the Relay
# Restart relay
docker start guard-relay
# Monitor startup
docker logs -f guard-relay
Method 2: Direct Key Extraction
Pros: Minimal, extracts only critical keys
Cons: Manual process, easier to miss files
Extract Keys While Running
# Create secure backup directory
mkdir -p ~/tor-relay-backups/keys-only
chmod 700 ~/tor-relay-backups/keys-only
# Extract keys directly from container
docker exec guard-relay tar czf - -C /var/lib/tor/keys . | \
tar xzf - -C ~/tor-relay-backups/keys-only/
# Verify contents
ls -la ~/tor-relay-backups/keys-only/
Expected output:
ed25519_master_id_secret_key
ed25519_signing_secret_key
secret_onion_key
Secure the Backup
# Set restrictive permissions
chmod 600 ~/tor-relay-backups/keys-only/*
# Verify ownership
ls -la ~/tor-relay-backups/keys-only/
Method 3: Automated Daily Backup
Pros: Hands-off, versioned history
Cons: Requires cron setup, disk space
Create Backup Script
#!/bin/bash
# Save as: /usr/local/bin/backup-tor-relay.sh
set -euo pipefail
BACKUP_DIR="/backups/tor-relay"
RETENTION_DAYS=30
CONTAINER="guard-relay"
TIMESTAMP=$(date +%Y-%m-%d_%H-%M-%S)
# Create backup directory
mkdir -p "$BACKUP_DIR/$TIMESTAMP"
# Stop relay gracefully
echo "🛑 Stopping relay..."
docker stop "$CONTAINER" || true
sleep 5
# Backup data volume
echo "💾 Backing up data volume..."
docker run --rm \
-v tor-guard-data:/data \
-v "$BACKUP_DIR/$TIMESTAMP":/backup \
alpine tar czf /backup/tor-data.tar.gz -C /data .
# Backup logs volume
echo "📝 Backing up logs..."
docker run --rm \
-v tor-guard-logs:/data \
-v "$BACKUP_DIR/$TIMESTAMP":/backup \
alpine tar czf /backup/tor-logs.tar.gz -C /data .
# Extract fingerprint for reference
docker start "$CONTAINER"
sleep 10
docker exec "$CONTAINER" cat /var/lib/tor/fingerprint > "$BACKUP_DIR/$TIMESTAMP/fingerprint.txt" || true
# Create manifest
cat > "$BACKUP_DIR/$TIMESTAMP/MANIFEST.txt" << EOF
Tor Guard Relay Backup
Timestamp: $TIMESTAMP
Container: $CONTAINER
Relay: $(grep Nickname /opt/tor-relay/relay.conf | cut -d' ' -f2)
Files:
- tor-data.tar.gz (Relay identity and state)
- tor-logs.tar.gz (Tor logs for audit)
- fingerprint.txt (Relay fingerprint reference)
EOF
# Cleanup old backups (keep last 30 days)
echo "🧹 Cleaning up old backups..."
find "$BACKUP_DIR" -maxdepth 1 -type d -mtime +$RETENTION_DAYS -exec rm -rf {} \;
# Summary
echo "✅ Backup complete: $BACKUP_DIR/$TIMESTAMP"
du -sh "$BACKUP_DIR/$TIMESTAMP"
Make Executable
chmod +x /usr/local/bin/backup-tor-relay.sh
Add to Cron (Daily at 2 AM)
# Edit crontab
crontab -e
# Add line:
0 2 * * * /usr/local/bin/backup-tor-relay.sh >> /var/log/tor-backup.log 2>&1
Monitor Backup Logs
# View backup logs
tail -f /var/log/tor-backup.log
# Check backup history
ls -lah /backups/tor-relay/
Method 4: Off-Site Backup (Cloud/External)
Pros: Disaster recovery, geographic redundancy
Cons: Security risk if not encrypted, potential costs
Encrypt Backup Before Upload
# Generate encryption key (save this somewhere secure!)
openssl rand -base64 32 > ~/tor-relay-backup.key
# Encrypt backup before upload
gpg --symmetric --cipher-algo AES256 \
--output tor-data-encrypted.tar.gz.gpg \
tor-data-$(date +%s).tar.gz
# Upload to cloud (e.g., AWS S3)
aws s3 cp tor-data-encrypted.tar.gz.gpg s3://my-backups/tor-relay/
Decrypt When Needed
# Decrypt backup
gpg --decrypt tor-data-encrypted.tar.gz.gpg > tor-data-restored.tar.gz
# Verify integrity
tar tzf tor-data-restored.tar.gz | head -10
Recovery Procedures
Scenario 1: Container Corruption
Problem: Container is running but data is corrupted
Recovery time: 15 minutes
Steps
# 1. Stop the relay
docker stop guard-relay
# 2. Remove corrupted volume
docker volume rm tor-guard-data
# 3. Create new volume
docker volume create tor-guard-data
# 4. Restore from backup
docker run --rm \
-v tor-guard-data:/data \
-v ~/tor-relay-backups/2024-01-01:/backup \
alpine tar xzf /backup/tor-data-1704110400.tar.gz -C /data
# 5. Verify permissions
docker exec guard-relay chown -R tor:tor /var/lib/tor
# 6. Restart relay
docker start guard-relay
# 7. Monitor startup
docker logs -f guard-relay
Scenario 2: Server Failure - Full Restore
Problem: Entire server lost, migrating to new hardware
Recovery time: 30 minutes
Steps
# 1. Prepare new server with Docker
# 2. Copy backup to new server
scp -r ~/tor-relay-backups/2024-01-01 user@new-server:/tmp/
# 3. On new server, create volume and restore
docker volume create tor-guard-data
docker run --rm \
-v tor-guard-data:/data \
-v /tmp/2024-01-01:/backup \
alpine tar xzf /backup/tor-data-1704110400.tar.gz -C /data
# 4. Copy relay configuration
scp /opt/tor-relay/relay.conf user@new-server:/opt/tor-relay/
# 5. On new server, start relay
docker run -d \
--name guard-relay \
--network host \
-v /opt/tor-relay/relay.conf:/etc/tor/torrc:ro \
-v tor-guard-data:/var/lib/tor \
-v tor-guard-logs:/var/log/tor \
--restart unless-stopped \
r3bo0tbx1/onion-relay:latest
# 6. Verify relay is using old identity
docker exec guard-relay fingerprint
# Should match original fingerprint!
Scenario 3: Key Loss - Emergency Recovery
Problem: All keys lost, only backup exists
Recovery time: 5 minutes
Restore Keys Only
# Stop relay
docker stop guard-relay
# Clear tor data
docker run --rm \
-v tor-guard-data:/data \
alpine rm -rf /data/*
# Restore from backup
docker run --rm \
-v tor-guard-data:/data \
-v ~/tor-relay-backups/keys-only:/backup \
alpine bash -c "cp -r /backup/* /data/"
# Fix permissions
docker exec guard-relay chown -R tor:tor /var/lib/tor
docker exec guard-relay chmod 700 /var/lib/tor/keys
# Restart
docker start guard-relay
# Verify identity recovered
docker exec guard-relay fingerprint
Migration Guide
Move Relay to New Server (Same Identity)
Goal: Keep relay fingerprint, move to new hardware
Pre-Migration Checklist
- ✅ Recent full backup created
- ✅ New server prepared with Docker
- ✅ Network firewall rules ready
- ✅ DNS/IP planning done
- ✅ Maintenance window scheduled
Step-by-Step Migration
# === ON OLD SERVER ===
# 1. Create final backup
docker stop guard-relay
sleep 5
docker run --rm \
-v tor-guard-data:/data \
-v ~/tor-relay-backups/migration:/backup \
alpine tar czf /backup/tor-data-final.tar.gz -C /data .
# 2. Verify backup
ls -lh ~/tor-relay-backups/migration/
# === TRANSFER TO NEW SERVER ===
# 3. Copy backup securely
scp -r ~/tor-relay-backups/migration user@new-server:/tmp/
# 4. Copy relay configuration
scp /opt/tor-relay/relay.conf user@new-server:/opt/tor-relay/
# === ON NEW SERVER ===
# 5. Create volume and restore
docker volume create tor-guard-data
docker run --rm \
-v tor-guard-data:/data \
-v /tmp/migration:/backup \
alpine tar xzf /backup/tor-data-final.tar.gz -C /data
# 6. Start relay on new server
docker run -d \
--name guard-relay \
--network host \
-v /opt/tor-relay/relay.conf:/etc/tor/torrc:ro \
-v tor-guard-data:/var/lib/tor \
-v tor-guard-logs:/var/log/tor \
--restart unless-stopped \
r3bo0tbx1/onion-relay:latest
# 7. Verify startup and identity
docker logs -f guard-relay
docker exec guard-relay fingerprint
# === FINAL VERIFICATION ===
# 8. Check on Tor Metrics (should recognize old fingerprint within hours)
# https://metrics.torproject.org/rs.html
# 9. After verification, on old server:
docker stop guard-relay
docker rm guard-relay
Verification After Migration
# Check logs for successful bootstrap
docker logs guard-relay 2>&1 | grep "Bootstrapped 100"
# Verify fingerprint matches backup
docker exec guard-relay fingerprint
# Compare with: cat ~/tor-relay-backups/migration/fingerprint.txt
# Monitor for 24 hours for any issues
docker stats guard-relay --no-stream
Zero-Downtime Migration (Advanced)
Goal: Migrate relay without downtime by running dual servers
Setup
# 1. OLD SERVER: Create backup
docker stop guard-relay
docker run --rm \
-v tor-guard-data:/data \
-v ~/tor-relay-backups/dual:/backup \
alpine tar czf /backup/tor-data.tar.gz -C /data .
# 2. NEW SERVER: Restore backup and start
docker volume create tor-guard-data
docker run --rm \
-v tor-guard-data:/data \
-v /tmp/dual:/backup \
alpine tar xzf /backup/tor-data.tar.gz -C /data
docker run -d \
--name guard-relay \
--network host \
-v /opt/tor-relay/relay.conf:/etc/tor/torrc:ro \
-v tor-guard-data:/var/lib/tor \
-v tor-guard-logs:/var/log/tor \
--restart unless-stopped \
r3bo0tbx1/onion-relay:latest
# 3. Verify NEW server is running
docker logs guard-relay | grep "Bootstrapped"
# 4. Wait 30 minutes for NEW server to stabilize
sleep 1800
# 5. OLD SERVER: Restart old relay
docker start guard-relay
# 6. Both servers now running same relay identity
# Tor network handles this gracefully
# 7. OLD SERVER: After 24 hours, shut down
docker stop guard-relay
Best Practices
✅ DO
- ✅ Backup immediately after bootstrap - Preserve identity
- ✅ Use strong encryption for off-site backups
- ✅ Test restores regularly - Backups are worthless if unverifiable
- ✅ Document fingerprints - Keep reference copy of fingerprint
- ✅ Automate backups - Set and forget with cron
- ✅ Store backups securely - Encrypt sensitive data
- ✅ Keep multiple copies - Local + off-site minimum
- ✅ Version your backups - Date-stamped directories
❌ DON'T
- ❌ Don't backup
/etc/tor/torrc- Mount as read-only from host - ❌ Don't share backup media unencrypted - Keys are sensitive
- ❌ Don't rely on single backup - 3-2-1 rule applies
- ❌ Don't ignore backup failures - Monitor logs
- ❌ Don't delete old backups immediately - Keep 30+ days
📊 3-2-1 Backup Rule
Maintain at minimum:
- 3 copies of your data
- Original (running relay)
- Backup 1 (local storage)
- Backup 2 (off-site)
- 2 different media types
- NVMe/SSD
- USB external drive
- 1 off-site copy
- Cloud storage (encrypted)
- Or remote server
Troubleshooting
Backup Failed: "Volume is in use"
# Problem: Cannot backup running volume
# Solution: Stop relay first
docker stop guard-relay
sleep 5
# Retry backup command
Restore Failed: "File permissions denied"
# Problem: Restored files have wrong ownership
# Solution: Fix permissions
docker exec guard-relay chown -R tor:tor /var/lib/tor
docker exec guard-relay chmod 700 /var/lib/tor/keys
Fingerprint Changed After Restore
# Problem: Restored relay has different fingerprint
# Cause: Keys weren't backed up, only state
# Solution: Use Method 2 (Direct Key Extraction) for future backups
# For now, accept new identity:
docker exec guard-relay fingerprint
# This is your new permanent fingerprint
Reference
Backup Command Cheat Sheet:
# Quick backup (stop relay)
docker stop guard-relay && \
docker run --rm -v tor-guard-data:/data -v ~/backups:/backup alpine tar czf /backup/tor-$(date +%s).tar.gz -C /data . && \
docker start guard-relay
# Quick restore
docker run --rm -v tor-guard-data:/data -v ~/backups:/backup alpine tar xzf /backup/tor-1704110400.tar.gz -C /data
# Verify backup integrity
tar tzf ~/backups/tor-1704110400.tar.gz | head -20
# Calculate backup size
du -sh ~/backups/tor-1704110400.tar.gz