A modern, feature-rich tool for automated BTRFS snapshot backup management with TOML configuration, time-based retention policies, and robust SSH transfer support.
This project is a continuation of the btrfs-backup lineage:
- 2014: Originally created by Chris Lawrence
- 2017: Maintained and enhanced by Robert Schindler
- 2024-present: Continued development by Michael Berry as btrfs-backup-ng
See the LICENSE file for full copyright attribution.
- TOML Configuration: Clean, validated configuration files (no custom syntax)
- Subcommand CLI: Modern interface with
run,snapshot,transfer,prune,restore,verify,estimate,list,status - Disaster Recovery: Built-in restore command to pull backups back to local systems
- Backup Verification: Multi-level integrity checks (metadata, stream, full restore test)
- Space-Aware Operations: Pre-flight destination space checking with btrfs quota support
- Time-based Retention: Intuitive policies (hourly, daily, weekly, monthly, yearly)
- Guided Setup: Step-by-step configuration with Rich-formatted tables and prompts
- Auto-Detection: Automatically discovers btrfs subvolumes and snapper configurations
- Smart Recommendations: Suggests which volumes to back up based on classification
- Rollback Detection: Identifies systems booted from snapper snapshots
- Real-time Preview: Shows configuration as you build it
- Full Snapper Support: Back up and restore Snapper-managed snapshots
- Metadata Preservation: Preserves
info.xmlfor seamless Snapper restoration - Pre/Post Pairs: Supports single, pre, and post snapshot types
- Incremental Transfers: Efficient delta-based transfers between snapshots
- Generate Config: Auto-generate TOML config from existing Snapper setup
- Configuration Import: Convert btrbk.conf to TOML with full syntax support
- Timestamp Format Mapping: Correctly maps
short,long, andlong-isoformats - Systemd Migration: One-command migration from btrbk timer to btrfs-backup-ng
- Seamless Transition: Continue incremental backups from existing btrbk snapshots
- Wizard Integration: Detect and offer btrbk import in the interactive wizard
- Rich Progress Bars: Real-time transfer progress with speed, ETA, and percentage
- Parallel Execution: Concurrent volume and target transfers
- Stream Compression: zstd, gzip, lz4, pigz, lzop support
- Bandwidth Throttling: Rate limiting for remote transfers
- Robust SSH: Password fallback, sudo support, Paramiko integration
- Write to Files: Store btrfs send streams as files on any filesystem (NFS, SMB, cloud storage)
- Compression: gzip, pigz, zstd, lz4, xz, lzo, bzip2, pbzip2
- Encryption: GPG (public-key) or OpenSSL (symmetric, btrbk-compatible)
- Restore Support: Restore from raw backups back to btrfs
- btrbk Compatibility: Migrate raw_target_compress and raw_target_encrypt settings
- Transaction Logging: Structured JSON logs for auditing and automation
- Email & Webhook Notifications: Alerts on backup success/failure
- Systemd Integration: Built-in timer/service installation
- Doctor Command: Diagnose backup system health and auto-fix issues
- User-Friendly Defaults: Auto-mode for filesystem checks warns but continues
- Legacy Compatibility: Original CLI still works
git clone https://github.com/berrym/btrfs-backup-ng.git
cd btrfs-backup-ng
pip install -e .- Python 3.11+
- BTRFS utilities (
btrfs-progs) - SSH client (for remote backups)
# Compression support
sudo dnf install zstd lz4 pigz # Fedora/RHEL
sudo apt install zstd lz4 pigz # Debian/Ubuntu
# Bandwidth throttling
sudo dnf install pv # Fedora/RHEL
sudo apt install pv # Debian/UbuntuInstall shell completions for tab-completion support:
# Install for your shell (bash, zsh, or fish)
btrfs-backup-ng completions install --shell bash
btrfs-backup-ng completions install --shell zsh
btrfs-backup-ng completions install --shell fish
# System-wide installation (requires root)
sudo btrfs-backup-ng completions install --shell bash --system
# Show path to completion scripts
btrfs-backup-ng completions pathSee completions/README.md for manual installation instructions.
Install man pages for offline documentation:
# User installation (to ~/.local/share/man)
btrfs-backup-ng manpages install
# System-wide installation (requires root)
sudo btrfs-backup-ng manpages install --system
# Custom prefix
btrfs-backup-ng manpages install --prefix /opt/myapp
# Show path to man page files
btrfs-backup-ng manpages pathAfter user installation, add to your MANPATH:
export MANPATH="$HOME/.local/share/man:$MANPATH"View man pages:
man btrfs-backup-ng
man btrfs-backup-ng-restore
man btrfs-backup-ng-config# Option A: Interactive wizard (recommended for new users)
btrfs-backup-ng config init --interactive -o ~/.config/btrfs-backup-ng/config.toml
# Option B: Generate example config and edit manually
btrfs-backup-ng config init > ~/.config/btrfs-backup-ng/config.toml[global]
snapshot_dir = ".snapshots"
incremental = true
[global.retention]
min = "1d"
hourly = 24
daily = 7
weekly = 4
monthly = 12
[[volumes]]
path = "/home"
snapshot_prefix = "home-"
[[volumes.targets]]
path = "/mnt/backup/home"
[[volumes.targets]]
path = "ssh://backup@server:/backups/home"
ssh_sudo = true
compress = "zstd"
rate_limit = "50M"# Full backup (snapshot + transfer + prune)
btrfs-backup-ng run
# Or individual operations
btrfs-backup-ng snapshot
btrfs-backup-ng transfer
btrfs-backup-ng prune# Install systemd timer (hourly backups)
sudo btrfs-backup-ng install --timer=hourly
# Or custom schedule (every 15 minutes)
sudo btrfs-backup-ng install --oncalendar='*:0/15'
# User-level timer (no sudo needed)
btrfs-backup-ng install --user --timer=daily| Command | Description |
|---|---|
run |
Execute full backup (snapshot + transfer + prune) |
snapshot |
Create snapshots only |
transfer |
Transfer existing snapshots to targets |
prune |
Apply retention policies |
restore |
Restore snapshots from backup location |
verify |
Verify backup integrity (metadata, stream, or full test) |
estimate |
Estimate backup transfer sizes (use --check-space for space verification) |
doctor |
Diagnose backup system health and fix common issues |
list |
Show snapshots and backups |
status |
Show job status and statistics |
config validate |
Validate configuration file |
config init |
Generate example configuration (use -i for interactive wizard) |
config import |
Import btrbk configuration |
config detect |
Detect btrfs subvolumes on the system (use --wizard for guided setup) |
install |
Install systemd timer/service |
uninstall |
Remove systemd timer/service |
# Dry run (show what would be done)
btrfs-backup-ng run --dry-run
btrfs-backup-ng prune --dry-run
# Override compression and rate limit
btrfs-backup-ng transfer --compress=zstd --rate-limit=10M
# Progress bar control (auto-detected by default)
btrfs-backup-ng run --progress # Force progress bars
btrfs-backup-ng run --no-progress # Disable progress bars
# Parallel execution
btrfs-backup-ng run --parallel-volumes=2 --parallel-targets=3
# Specify config file
btrfs-backup-ng -c /path/to/config.toml run
# Verbose output
btrfs-backup-ng -v run
# View transaction history
btrfs-backup-ng status --transactions[global]
snapshot_dir = ".snapshots" # Relative to volume or absolute
timestamp_format = "%Y%m%d-%H%M%S" # Snapshot timestamp format
incremental = true # Use incremental transfers
log_file = "/var/log/btrfs-backup-ng.log" # Optional rotating log file
transaction_log = "/var/log/btrfs-backup-ng.jsonl" # Optional JSON transaction log
parallel_volumes = 2 # Concurrent volume backups
parallel_targets = 3 # Concurrent target transfers
quiet = false # Suppress non-essential output
verbose = false # Enable verbose outputTwo logging systems are available:
File Logging (log_file): Human-readable rotating log file (10MB max, 5 backups)
2026-01-04 13:10:51 [INFO] btrfs-backup-ng: Creating snapshot...
2026-01-04 13:10:57 [INFO] btrfs-backup-ng: Transfer completed successfully
Transaction Logging (transaction_log): Structured JSONL for auditing and automation
{"timestamp": "2026-01-04T18:10:57+00:00", "action": "transfer", "status": "completed", "snapshot": "home-20260104", "duration_seconds": 5.78, "size_bytes": 2175549440}View transaction history via CLI:
btrfs-backup-ng status --transactions # Show recent transactions
btrfs-backup-ng status -t -n 20 # Show last 20 transactionsbtrfs-backup-ng can send notifications on backup completion or failure via email (SMTP) or webhooks. This is particularly useful for automated/scheduled backups.
[global.notifications.email]
enabled = true
smtp_host = "smtp.example.com"
smtp_port = 587
smtp_tls = "starttls" # "ssl" (port 465), "starttls" (port 587), or "none"
smtp_user = "alerts@example.com"
smtp_password = "your-password"
from_addr = "btrfs-backup-ng@example.com"
to_addrs = ["admin@example.com", "ops@example.com"]
on_success = false # Don't notify on success (default)
on_failure = true # Notify on failure (default)SMTP Port Reference:
| Port | TLS Mode | Description |
|---|---|---|
| 465 | ssl |
Implicit TLS (SMTPS) |
| 587 | starttls |
Explicit TLS (submission) |
| 25 | none |
Plain text (local mail only) |
Example: Gmail SMTP
[global.notifications.email]
enabled = true
smtp_host = "smtp.gmail.com"
smtp_port = 587
smtp_tls = "starttls"
smtp_user = "your-email@gmail.com"
smtp_password = "your-app-password" # Use App Password, not account password
from_addr = "your-email@gmail.com"
to_addrs = ["your-email@gmail.com"]
on_failure = trueExample: Local Postfix/Sendmail
[global.notifications.email]
enabled = true
smtp_host = "localhost"
smtp_port = 25
smtp_tls = "none"
from_addr = "btrfs-backup-ng@myserver.local"
to_addrs = ["root@myserver.local"]
on_failure = trueSend JSON payloads to any HTTP endpoint (Slack, Discord, PagerDuty, custom services, etc.).
[global.notifications.webhook]
enabled = true
url = "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXX"
method = "POST" # POST or PUT
timeout = 30 # Request timeout in seconds
on_success = false
on_failure = true
# Optional custom headers
[global.notifications.webhook.headers]
Authorization = "Bearer your-token"
X-Custom-Header = "value"Webhook Payload Format:
The webhook receives a JSON payload with full backup details:
{
"event_type": "backup_complete",
"status": "failure",
"timestamp": "2026-01-04T18:30:00+00:00",
"hostname": "myserver",
"summary": "Backup failed: 1 volumes failed",
"volumes_processed": 2,
"volumes_failed": 1,
"snapshots_created": 1,
"transfers_completed": 1,
"transfers_failed": 1,
"duration_seconds": 45.2,
"errors": ["Transfer to ssh://backup@remote:/backups: Connection refused"]
}Example: Slack Incoming Webhook
Slack accepts the payload directly when using their Incoming Webhooks feature.
[global.notifications.webhook]
enabled = true
url = "https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXX"
on_failure = trueExample: Discord Webhook
Discord webhooks expect a specific format. Use a proxy or serverless function to transform the payload.
Example: ntfy.sh (Simple Push Notifications)
[global.notifications.webhook]
enabled = true
url = "https://ntfy.sh/your-topic"
method = "POST"
on_failure = true
[global.notifications.webhook.headers]
Title = "btrfs-backup-ng Alert"
Priority = "high"
Tags = "warning,backup"Notifications are sent for these events:
| Event | Commands | Status Values |
|---|---|---|
backup_complete |
run, transfer |
success, partial, failure |
prune_complete |
prune |
success, partial, failure |
Status meanings:
success: All operations completed without errorspartial: Some operations succeeded, some failedfailure: All operations failed
By default, notifications are only sent on failure. Configure per notification method:
# Email: notify on failure only (default)
[global.notifications.email]
enabled = true
on_success = false
on_failure = true
# Webhook: notify on both success and failure
[global.notifications.webhook]
enabled = true
on_success = true
on_failure = trueTo test your notification configuration, run a backup with intentionally bad settings:
# Create a test config with a bad target
cat > /tmp/test-notify.toml << 'EOF'
[global.notifications.email]
enabled = true
smtp_host = "localhost"
smtp_port = 25
from_addr = "test@localhost"
to_addrs = ["root@localhost"]
on_failure = true
[[volumes]]
path = "/nonexistent"
[[volumes.targets]]
path = "/also/nonexistent"
EOF
# Run with the test config (will fail and trigger notification)
sudo btrfs-backup-ng -c /tmp/test-notify.toml run- SMTP passwords in config files: Use file permissions (
chmod 600) to protect your configuration file - Webhook URLs: Treat webhook URLs as secrets; they often provide unauthenticated access
- Environment variables: Future versions may support reading secrets from environment variables
# Protect config file with sensitive credentials
sudo chmod 600 /etc/btrfs-backup-ng/config.toml
sudo chown root:root /etc/btrfs-backup-ng/config.toml[global.retention]
min = "1d" # Keep all snapshots for at least 1 day
hourly = 24 # Keep 24 hourly snapshots
daily = 7 # Keep 7 daily snapshots
weekly = 4 # Keep 4 weekly snapshots
monthly = 12 # Keep 12 monthly snapshots
yearly = 0 # Don't keep yearly (0 = disabled)Duration format: 30m (minutes), 2h (hours), 1d (days), 1w (weeks)
[[volumes]]
path = "/home" # Path to BTRFS subvolume
snapshot_prefix = "home-" # Prefix for snapshot names
snapshot_dir = ".snapshots" # Override global snapshot_dir
enabled = true # Enable/disable this volume
# Volume-specific retention (overrides global)
[volumes.retention]
daily = 14
weekly = 8[[volumes.targets]]
path = "/mnt/backup/home" # Local path
[[volumes.targets]]
path = "ssh://user@host:/backups" # SSH remote path
ssh_sudo = true # Use sudo on remote
ssh_port = 22 # SSH port
ssh_key = "~/.ssh/backup_key" # SSH private key
ssh_password_auth = true # Allow password fallback
compress = "zstd" # Compression (none|gzip|zstd|lz4|pigz|lzop)
rate_limit = "10M" # Bandwidth limit (K|M|G suffix)
require_mount = false # Require path to be a mount point (safety check)Raw targets write btrfs send streams directly to files instead of using btrfs receive. This enables backups to non-btrfs filesystems (NFS, SMB, cloud storage) with optional compression and encryption.
[[volumes.targets]]
path = "raw:///mnt/nas/backups/home" # Local raw target
compress = "zstd" # Optional compression
encrypt = "gpg" # GPG encryption
gpg_recipient = "backup@example.com" # GPG key recipient
[[volumes.targets]]
path = "raw+ssh://backup@server/backups" # Remote raw target via SSH
compress = "zstd"
# OpenSSL encryption (btrbk compatible)
[[volumes.targets]]
path = "raw:///mnt/backup"
compress = "gzip"
encrypt = "openssl_enc"
openssl_cipher = "aes-256-cbc" # Default cipherRaw target options:
| Option | Description |
|---|---|
compress |
Compression: gzip, pigz, zstd, lz4, xz, lzo, bzip2, pbzip2 |
encrypt |
Encryption: gpg, openssl_enc |
gpg_recipient |
GPG key recipient (required for gpg) |
gpg_keyring |
Optional GPG keyring path |
openssl_cipher |
OpenSSL cipher (default: aes-256-cbc) |
For OpenSSL encryption, set the passphrase via environment variable:
export BTRFS_BACKUP_PASSPHRASE="your_passphrase"
# or (btrbk compatible)
export BTRBK_PASSPHRASE="your_passphrase"File naming convention:
| Settings | Filename Extension |
|---|---|
| No compression/encryption | .btrfs |
| Compressed with zstd | .btrfs.zst |
| GPG encrypted | .btrfs.gpg |
| Compressed + GPG | .btrfs.zst.gpg |
| OpenSSL encrypted | .btrfs.enc |
| Compressed + OpenSSL | .btrfs.zst.enc |
Each stream file has a companion .meta file with JSON metadata for incremental chain tracking.
When backing up to external drives or removable media, there's a common pitfall: if the drive isn't mounted, backups will silently write to the mount point directory on your root filesystem, consuming disk space and not actually backing up your data.
The require_mount option prevents this by verifying that the target path is an active mount point before starting any transfers.
# External USB drive backup with mount verification
[[volumes.targets]]
path = "/mnt/usb-backup"
require_mount = true # Fail if /mnt/usb-backup is not a mount pointWhen to use require_mount = true:
- External USB drives
- Removable media (SD cards, etc.)
- Network mounts (NFS, SMB) that may be disconnected
- Any target that might not always be available
Example error when drive is not mounted:
ERROR: Target /mnt/usb-backup is not mounted.
Ensure the drive is connected and mounted, or set require_mount = false.
Complete external drive configuration example:
[[volumes]]
path = "/home"
snapshot_prefix = "home"
[[volumes.targets]]
path = "/mnt/external-backup/home"
require_mount = true # Safety check - fail if drive not mounted
# Also backup to remote server (no mount check needed for SSH)
[[volumes.targets]]
path = "ssh://backup@server:/backups/home"
ssh_sudo = trueNote: require_mount only applies to local targets. It has no effect on SSH targets.
btrfs-backup-ng searches for configuration files in the following locations, in priority order:
| Priority | Location | Description |
|---|---|---|
| 1 (highest) | -c /path/to/config.toml |
Explicit path via command line |
| 2 | ~/.config/btrfs-backup-ng/config.toml |
User-specific configuration |
| 3 | /etc/btrfs-backup-ng/config.toml |
System-wide configuration |
The first configuration file found is used. If no configuration file exists in any location and none is specified with -c, commands that require configuration will display an error with instructions.
For personal workstations or when running backups as a regular user:
# Create user config directory
mkdir -p ~/.config/btrfs-backup-ng
# Generate example configuration
btrfs-backup-ng config init > ~/.config/btrfs-backup-ng/config.toml
# Edit to match your setup
$EDITOR ~/.config/btrfs-backup-ng/config.toml
# Run backups (config is found automatically)
sudo btrfs-backup-ng runFor servers, shared systems, or when using systemd timers:
# Create system config directory
sudo mkdir -p /etc/btrfs-backup-ng
# Generate example configuration
btrfs-backup-ng config init | sudo tee /etc/btrfs-backup-ng/config.toml
# Edit to match your setup
sudo $EDITOR /etc/btrfs-backup-ng/config.toml
# Set appropriate permissions (readable by root only for security)
sudo chmod 600 /etc/btrfs-backup-ng/config.toml
# Run backups (config is found automatically)
sudo btrfs-backup-ng runWhen both user and system configurations exist:
- User config takes precedence over system config
- Use
-cto explicitly select a different configuration - The
config validatecommand shows which file is being used
# See which config file is active
btrfs-backup-ng config validate
# Output: Validating: /home/user/.config/btrfs-backup-ng/config.toml
# Force use of system config
sudo btrfs-backup-ng -c /etc/btrfs-backup-ng/config.toml runThe easiest way to create a new configuration is the interactive wizard:
# Run the wizard
btrfs-backup-ng config init --interactive
# Run the wizard and save directly to a file
btrfs-backup-ng config init -i -o ~/.config/btrfs-backup-ng/config.tomlThe wizard guides you through:
- Global settings: Snapshot directory, timestamp format, incremental mode
- Parallelism: Max concurrent volumes and targets
- Retention policy: How many hourly, daily, weekly, monthly, yearly snapshots to keep
- Notifications: Email (SMTP) and webhook settings
- Volumes: Which directories to back up
- Targets: Where to send backups (local paths or SSH URLs)
The wizard automatically suggests appropriate options:
- Prompts for
require_mountwhen targeting/mnt/paths (external drive safety) - Prompts for
ssh_sudowhen targeting SSH URLs
Press Ctrl+C at any time to cancel.
For manual editing, generate an example configuration with comments:
# Output to stdout
btrfs-backup-ng config init
# Save to file
btrfs-backup-ng config init -o config.tomlCheck your configuration for errors before running backups:
btrfs-backup-ng config validate
# Validate a specific file
btrfs-backup-ng config validate -c /path/to/config.tomlMigrate from btrbk by importing your existing configuration:
# Convert and output to stdout
btrfs-backup-ng config import /etc/btrbk/btrbk.conf
# Convert and save to file
btrfs-backup-ng config import /etc/btrbk/btrbk.conf -o config.tomlSee Migrating from btrbk for more details.
For production servers with systemd timers:
# 1. Create system-wide configuration (interactive wizard)
sudo mkdir -p /etc/btrfs-backup-ng
sudo btrfs-backup-ng config init -i -o /etc/btrfs-backup-ng/config.toml
sudo chmod 600 /etc/btrfs-backup-ng/config.toml
# Or generate example and edit manually:
# btrfs-backup-ng config init | sudo tee /etc/btrfs-backup-ng/config.toml
# sudo chmod 600 /etc/btrfs-backup-ng/config.toml
# 2. Edit configuration (if not using interactive wizard)
sudo $EDITOR /etc/btrfs-backup-ng/config.toml
# 3. Validate configuration
sudo btrfs-backup-ng config validate
# 4. Test manually first
sudo btrfs-backup-ng run --dry-run
sudo btrfs-backup-ng run
# 5. Install and enable systemd timer
sudo btrfs-backup-ng install --timer=hourly
sudo systemctl daemon-reload
sudo systemctl enable --now btrfs-backup-ng.timer
# 6. Verify timer is active
systemctl list-timers btrfs-backup-ng.timerbtrfs-backup-ng provides comprehensive migration support from btrbk, including configuration import with proper timestamp format handling and systemd timer migration.
# 1. Import your btrbk configuration
btrfs-backup-ng config import /etc/btrbk/btrbk.conf -o ~/.config/btrfs-backup-ng/config.toml
# 2. Review the generated config
cat ~/.config/btrfs-backup-ng/config.toml
# 3. Migrate systemd timers (disable btrbk, optionally enable btrfs-backup-ng)
sudo btrfs-backup-ng config migrate-systemd --dry-run # Preview changes
sudo btrfs-backup-ng config migrate-systemd # Apply changes
# 4. Test the new configuration
sudo btrfs-backup-ng run --dry-runThe importer handles btrbk's configuration syntax and converts it to TOML:
btrfs-backup-ng config import /etc/btrbk/btrbk.conf -o config.tomlThe importer will:
- Convert btrbk's custom syntax to TOML
- Map timestamp formats correctly (
short→%Y%m%d,long→%Y%m%dT%H%M,long-iso→%Y%m%dT%H%M%S%z) - Translate retention policies
- Convert SSH targets (including
backend btrfs-progs-sudo→ssh_sudo = true) - Warn about common btrbk pitfalls
- Suggest improvements
If you're using btrbk's systemd timer for scheduled backups, migrate to btrfs-backup-ng's timer:
# Check current systemd status
btrfs-backup-ng config migrate-systemd --dry-run
# Output shows:
# btrbk.timer (enabled) - will be disabled
# btrfs-backup-ng.timer - will be enabled (if installed)
# Apply the migration
sudo btrfs-backup-ng config migrate-systemdThis command:
- Stops and disables btrbk.timer
- Enables btrfs-backup-ng.timer (if installed)
- Prevents both tools from running simultaneously
If btrfs-backup-ng's timer isn't installed yet:
# Install the systemd timer first
sudo btrfs-backup-ng install --timer=hourly
# Then migrate
sudo btrfs-backup-ng config migrate-systemdThe detection wizard also offers btrbk migration when a btrbk config is found:
sudo btrfs-backup-ng config detect --wizardThe wizard will:
- Detect existing btrbk configuration
- Offer to import it (or start fresh)
- Display imported volumes in a table for review
- Allow customization before saving
- Offer to migrate systemd timers after saving
For a seamless transition that continues using btrbk's existing snapshots:
- Import with matching timestamp format: The importer preserves btrbk's timestamp format so existing snapshots are recognized
- Keep the same snapshot directory: By default, btrbk and btrfs-backup-ng both use
.snapshots - Incremental transfers continue: Existing snapshots serve as parents for new transfers
# Imported config preserves btrbk's timestamp format
[global]
timestamp_format = "%Y%m%dT%H%M" # Matches btrbk's "long" format
snapshot_dir = ".snapshots" # Same as btrbk default| Feature | btrbk | btrfs-backup-ng |
|---|---|---|
| Config format | Custom syntax | TOML (standard) |
| Config validation | Runtime errors | Pre-flight validation |
| Indentation | Ignored (confusing) | TOML is explicit |
| Timestamp formats | short/long/long-iso | strftime patterns |
| Language | Perl | Python |
| CLI output | Plain text | Rich formatting |
| SSH handling | External ssh | Native Paramiko option |
| Systemd integration | Manual setup | Built-in install/migrate |
For detailed migration information, see docs/MIGRATING-FROM-BTRBK.md.
This section provides a complete guide for setting up passwordless SSH backups to a remote server. For fully automated backups, you need:
- SSH key authentication (no password prompts)
- Passwordless sudo on the remote for btrfs commands
- A btrfs filesystem on the remote to receive backups
On the remote backup server, create a dedicated user for backups:
# Create backup user with no password (key-only auth)
sudo useradd -m -s /bin/bash backup
# Create the backup directory on a btrfs filesystem
sudo mkdir -p /mnt/backups
sudo chown backup:backup /mnt/backups
# Verify the backup location is on a btrfs filesystem
df -T /mnt/backups | grep btrfs || echo "WARNING: Not a btrfs filesystem!"btrfs send/receive requires root privileges. On the remote server, configure sudo:
Create a dedicated sudoers file in /etc/sudoers.d/ (preferred over editing /etc/sudoers directly):
# Create sudoers drop-in file for backup user
sudo visudo -f /etc/sudoers.d/btrfs-backupAdd one of these configurations:
# Option 1: Full btrfs access (simplest, recommended)
backup ALL=(ALL) NOPASSWD: /usr/bin/btrfs
# Option 2: Restricted to specific btrfs subcommands (more secure)
backup ALL=(ALL) NOPASSWD: /usr/bin/btrfs receive *
backup ALL=(ALL) NOPASSWD: /usr/bin/btrfs subvolume *
backup ALL=(ALL) NOPASSWD: /usr/bin/btrfs send *
backup ALL=(ALL) NOPASSWD: /usr/bin/btrfs filesystem *
Important: Set correct permissions on the sudoers file (required by most distros):
sudo chmod 440 /etc/sudoers.d/btrfs-backupVerify sudo works without password:
# Test on remote server (as backup user)
sudo -n btrfs --version
# Should print version without prompting for passwordOn your local machine, generate a dedicated SSH key for backups:
# Generate Ed25519 key (recommended)
ssh-keygen -t ed25519 -f ~/.ssh/btrfs-backup-key -C "btrfs-backup-ng"
# Or RSA if Ed25519 isn't supported
ssh-keygen -t rsa -b 4096 -f ~/.ssh/btrfs-backup-key -C "btrfs-backup-ng"Copy the public key to the remote server:
# Copy key to remote backup user
ssh-copy-id -i ~/.ssh/btrfs-backup-key backup@remote-server
# Test the connection
ssh -i ~/.ssh/btrfs-backup-key backup@remote-server 'echo "SSH works!"'Verify everything works end-to-end:
# Test SSH + sudo + btrfs (from local machine)
ssh -i ~/.ssh/btrfs-backup-key backup@remote-server 'sudo btrfs filesystem show /mnt/backups'If this command succeeds without prompting for any passwords, you're ready.
Create your configuration file:
[global]
snapshot_dir = ".snapshots"
log_file = "/var/log/btrfs-backup-ng.log"
transaction_log = "/var/log/btrfs-backup-ng.jsonl"
[[volumes]]
path = "/home"
snapshot_prefix = "home"
[[volumes.targets]]
path = "ssh://backup@remote-server:/mnt/backups/home"
ssh_sudo = true # Required for btrfs receive
ssh_key = "~/.ssh/btrfs-backup-key" # Path to private keybtrfs operations on the local machine also require root. When running with sudo, you need to preserve your SSH agent:
# Method 1: Use sudo -E to preserve environment (including SSH_AUTH_SOCK)
sudo -E btrfs-backup-ng run
# Method 2: Explicitly specify the SSH key in config (works without agent)
# Just ensure ssh_key is set in your target configuration
sudo btrfs-backup-ng runImportant: When sudo changes to root, it normally loses access to your user's SSH agent. Options:
| Method | How | Best For |
|---|---|---|
sudo -E |
Preserves SSH_AUTH_SOCK | Interactive use |
ssh_key in config |
Uses key file directly | Automated/systemd |
| Root's SSH key | Generate key for root user | Dedicated backup systems |
Local machine setup:
# 1. Generate SSH key
ssh-keygen -t ed25519 -f ~/.ssh/btrfs-backup-key -N "" -C "btrfs-backup-ng"
# 2. Copy to remote
ssh-copy-id -i ~/.ssh/btrfs-backup-key backup@backupserver
# 3. Create config
sudo mkdir -p /etc/btrfs-backup-ng
sudo tee /etc/btrfs-backup-ng/config.toml << 'EOF'
[global]
snapshot_dir = ".snapshots"
[[volumes]]
path = "/home"
[[volumes.targets]]
path = "ssh://backup@backupserver:/mnt/backups/home"
ssh_sudo = true
ssh_key = "/home/myuser/.ssh/btrfs-backup-key"
EOF
# 4. Test
sudo btrfs-backup-ng run --dry-run
sudo btrfs-backup-ng runRemote server setup:
# 1. Create backup user
sudo useradd -m backup
# 2. Configure passwordless sudo
echo 'backup ALL=(ALL) NOPASSWD: /usr/bin/btrfs' | sudo tee /etc/sudoers.d/btrfs-backup
sudo chmod 440 /etc/sudoers.d/btrfs-backup
# 3. Create backup directory (must be on btrfs)
sudo mkdir -p /mnt/backups/home
sudo chown backup:backup /mnt/backups/home"Permission denied" on SSH connect:
# Check key permissions (must be 600)
chmod 600 ~/.ssh/btrfs-backup-key
# Test SSH manually with verbose output
ssh -vvv -i ~/.ssh/btrfs-backup-key backup@remote-server"sudo: a password is required":
# On remote, check sudoers syntax
sudo visudo -c -f /etc/sudoers.d/btrfs-backup
# Test sudo as backup user
sudo -u backup sudo -n btrfs --version"ERROR: not a btrfs filesystem":
# On remote, verify backup location is btrfs
df -T /mnt/backups
# Must show "btrfs" as filesystem typeSSH works manually but fails in btrfs-backup-ng:
# Check if running with sudo drops SSH agent
sudo env | grep SSH_AUTH_SOCK # Should show your socket
# If empty, either use sudo -E or specify ssh_key in config
sudo -E btrfs-backup-ng runIf you cannot set up passwordless authentication, btrfs-backup-ng supports password prompts:
[[volumes.targets]]
path = "ssh://backup@remote-server:/mnt/backups"
ssh_sudo = true
ssh_password_auth = true # Enable SSH password promptsFor sudo passwords on remote:
# Set via environment variable (insecure - visible in process list)
export BTRFS_BACKUP_SUDO_PASSWORD="password"
sudo -E btrfs-backup-ng runWarning: Password-based authentication is not suitable for automated/unattended backups.
The restore command enables pulling snapshots from backup storage back to local systems. This is essential for disaster recovery, migration, and backup verification.
# List available snapshots at backup location
btrfs-backup-ng restore --list ssh://backup@server:/backups/home
btrfs-backup-ng restore --list /mnt/external-backup/home
# Restore latest snapshot
btrfs-backup-ng restore ssh://backup@server:/backups/home /mnt/restore
# Restore specific snapshot by name
btrfs-backup-ng restore ssh://backup@server:/backups/home /mnt/restore \
--snapshot home-20260104-120000
# Restore snapshot before a specific date
btrfs-backup-ng restore ssh://backup@server:/backups/home /mnt/restore \
--before "2026-01-01 12:00"
# Interactive selection (shows list, lets you pick)
btrfs-backup-ng restore ssh://backup@server:/backups/home /mnt/restore --interactive
# Restore ALL snapshots (full mirror)
btrfs-backup-ng restore ssh://backup@server:/backups/home /mnt/restore --all
# Dry run (show what would be restored)
btrfs-backup-ng restore --dry-run ssh://backup@server:/backups/home /mnt/restorebtrfs-backup-ng automatically handles incremental restore chains:
Backup has: [snap-1, snap-2, snap-3, snap-4]
↓ ↓ ↓ ↓
(full) (incr) (incr) (incr)
You request: snap-4
Restore chain: snap-1 → snap-2 → snap-3 → snap-4
(2.1 GB) (156 MB) (89 MB) (234 MB)
The restore command:
- Analyzes the parent chain required for the target snapshot
- Checks which parents already exist locally (can skip those)
- Restores snapshots in order (oldest first) to satisfy dependencies
- Uses incremental transfers when possible (much faster)
| Option | Description |
|---|---|
-l, --list |
List available snapshots at backup location |
-s, --snapshot NAME |
Restore specific snapshot by name |
--before DATETIME |
Restore snapshot closest to this time (YYYY-MM-DD [HH:MM:SS]) |
-a, --all |
Restore all snapshots (full mirror) |
-i, --interactive |
Interactive snapshot selection |
--dry-run |
Show what would be restored without making changes |
--no-incremental |
Force full transfers (skip incremental) |
--overwrite |
Overwrite existing snapshots instead of skipping |
--in-place |
Restore to original location (DANGEROUS, requires confirmation) |
--yes-i-know-what-i-am-doing |
Confirm dangerous operations like in-place restore |
--prefix PREFIX |
Snapshot prefix filter (include trailing hyphen, e.g., home-) |
--ssh-sudo |
Use sudo on remote for btrfs commands |
--ssh-key FILE |
SSH private key file for authentication |
--compress METHOD |
Compression for transfer (none, zstd, gzip, lz4, pigz, lzop) |
--rate-limit RATE |
Bandwidth limit (e.g., '10M', '1G') |
--no-fs-checks |
Skip btrfs subvolume verification (needed for backup directories) |
--progress |
Show progress bars (default in terminal) |
--no-progress |
Disable progress bars |
--status |
Show locks and incomplete restores at backup location |
--unlock [ID] |
Unlock stuck restore sessions ('all' or specific session ID) |
--cleanup |
Clean up partial/incomplete restores at destination |
Config-driven restore options:
| Option | Description |
|---|---|
-c, --config FILE |
Path to configuration file |
--volume PATH |
Restore backups for volume defined in config (e.g., /home) |
--target INDEX |
Target index to restore from (0-based, default: first target) |
--to PATH |
Destination path for config-driven restore |
--list-volumes |
List volumes and their backup targets from config |
Instead of specifying full paths, you can use your configuration file to restore from configured backup targets:
# List all configured volumes and their backup targets
btrfs-backup-ng restore --list-volumes
# List available snapshots for a configured volume
btrfs-backup-ng restore --volume /home --list
# Restore from configured backup target
btrfs-backup-ng restore --volume /home --to /mnt/restore
# Restore from second target (index 1)
btrfs-backup-ng restore --volume /home --target 1 --to /mnt/restore
# Interactive restore from config
btrfs-backup-ng restore --volume /home --to /mnt/restore --interactiveThis is especially useful when you have complex SSH paths or multiple backup targets - the config file stores all the details.
# External drive backup
btrfs-backup-ng restore /mnt/external-backup/home /mnt/restore
# With specific snapshot
btrfs-backup-ng restore /mnt/external-backup/home /mnt/restore \
--snapshot home-20260104-120000# SSH backup server
btrfs-backup-ng restore ssh://backup@server:/backups/home /mnt/restore \
--ssh-sudo \
--ssh-key ~/.ssh/backup_key
# With compression for slow links
btrfs-backup-ng restore ssh://backup@server:/backups/home /mnt/restore \
--compress=zstd --rate-limit=10MComplete example of recovering a system from backup:
# 1. Boot from live USB/recovery environment
# 2. Mount your btrfs filesystem
mount /dev/sda2 /mnt/newroot
# 3. Create restore target directory
mkdir -p /mnt/newroot/restored-home
# 4. List available backups
btrfs-backup-ng restore --list ssh://backup@server:/backups/home
# 5. Restore the latest snapshot
btrfs-backup-ng restore ssh://backup@server:/backups/home /mnt/newroot/restored-home \
--ssh-sudo
# 6. (Optional) Restore to specific point in time
btrfs-backup-ng restore ssh://backup@server:/backups/home /mnt/newroot/restored-home \
--before "2026-01-01" --ssh-sudo
# 7. Verify the restore
ls -la /mnt/newroot/restored-home/
# 8. Rename/move the restored snapshot to final location
# (depends on your specific setup)There are several ways to restore data from backups, each with different trade-offs. Choose the approach that best fits your situation and comfort level.
Best for: Most recovery scenarios, file recovery, verification before committing
This is the safest approach - restore to a separate location, verify the contents, then manually move or copy what you need.
# 1. Create a temporary restore directory (must be on btrfs)
sudo mkdir -p /mnt/btrfs-restore
# 2. Restore the snapshot you want
sudo btrfs-backup-ng restore /mnt/backup/home /mnt/btrfs-restore \
--snapshot home-20260104-120000 \
--prefix "home-" \
--no-fs-checks
# 3. Verify the restored snapshot contains what you expect
ls -la /mnt/btrfs-restore/home-20260104-120000/
diff -r /mnt/btrfs-restore/home-20260104-120000/Documents ~/Documents
# 4a. Copy specific files you need (safest)
cp -a /mnt/btrfs-restore/home-20260104-120000/Documents/important.doc ~/Documents/
# 4b. Or replace entire directory (after backing up current)
mv ~/Documents ~/Documents.old
cp -a /mnt/btrfs-restore/home-20260104-120000/Documents ~/Documents
# 5. Clean up when done
sudo btrfs subvolume delete /mnt/btrfs-restore/home-20260104-120000Pros:
- Completely safe - original data untouched until you're ready
- Can inspect and verify before committing
- Can selectively restore individual files or directories
- Easy to abort if something is wrong
Cons:
- Requires extra disk space for temporary restore
- More manual steps involved
- Slower for full system recovery
Best for: Full directory replacement while keeping a safety net
Use btrfs's native snapshot capability to create a backup of current state before replacing.
# 1. Create a snapshot of your CURRENT state (safety net)
sudo btrfs subvolume snapshot /home /home.before-restore
# 2. Restore the backup snapshot to a temporary location
sudo btrfs-backup-ng restore /mnt/backup/home /mnt/btrfs-restore \
--snapshot home-20260104-120000 \
--prefix "home-" \
--no-fs-checks
# 3. Verify the restore looks correct
ls -la /mnt/btrfs-restore/home-20260104-120000/
# 4. Rename current to old, restored to current
sudo mv /home /home.old
sudo mv /mnt/btrfs-restore/home-20260104-120000 /home
# 5. If everything works, clean up old versions later
sudo btrfs subvolume delete /home.old
sudo btrfs subvolume delete /home.before-restore
# 5b. If something went wrong, roll back:
sudo mv /home /home.failed-restore
sudo mv /home.before-restore /homePros:
- Full replacement with automatic rollback option
- Uses btrfs efficiency (snapshots are instant, space-efficient)
- Clear before/after comparison possible
Cons:
- Requires understanding of btrfs subvolume management
- Need to handle mount points if /home is a separate subvolume
- May require reboot or remount for mount point changes
Best for: Advanced users, scripted recovery, maximum control
Use raw btrfs commands for complete control over the restore process.
# 1. From local backup drive
sudo btrfs send /mnt/backup/home/home-20260104-120000 | sudo btrfs receive /mnt/restore/
# 2. From remote backup via SSH
ssh backup@server "sudo btrfs send /backups/home/home-20260104-120000" | \
sudo btrfs receive /mnt/restore/
# 3. With compression for slow networks
ssh backup@server "sudo btrfs send /backups/home/home-20260104-120000 | zstd" | \
zstd -d | sudo btrfs receive /mnt/restore/
# 4. Incremental restore (if you have the parent locally)
ssh backup@server "sudo btrfs send -p /backups/home/home-20260103-120000 \
/backups/home/home-20260104-120000" | sudo btrfs receive /mnt/restore/Pros:
- Maximum flexibility and control
- Can be easily scripted
- Works in minimal recovery environments
- No dependency on btrfs-backup-ng being installed
Cons:
- Must manually handle incremental parent chains
- No progress display or error recovery
- Easy to make mistakes with complex commands
- Must manually manage snapshot prefixes and naming
Best for: Disaster recovery when you're certain, automated recovery scripts
Direct replacement of the target location. This is the most dangerous but fastest approach.
# WARNING: This will OVERWRITE existing data at /home
# Make absolutely sure you have the right snapshot!
# 1. First, verify what you're about to restore
btrfs-backup-ng restore --list ssh://backup@server:/backups/home \
--prefix "home-" --no-fs-checks
# 2. Do a dry-run first
btrfs-backup-ng restore ssh://backup@server:/backups/home /home \
--in-place \
--snapshot home-20260104-120000 \
--prefix "home-" \
--dry-run
# 3. If dry-run looks correct, proceed with actual restore
btrfs-backup-ng restore ssh://backup@server:/backups/home /home \
--in-place \
--yes-i-know-what-i-am-doing \
--snapshot home-20260104-120000 \
--prefix "home-" \
--ssh-sudoPros:
- Fastest for full recovery
- Single command, minimal steps
- Handles incremental chains automatically
Cons:
- DESTRUCTIVE - existing data is overwritten
- No easy rollback if wrong snapshot chosen
- Requires explicit confirmation flag
- Not suitable for partial recovery
| Strategy | Safety | Speed | Disk Space | Complexity | Best For |
|---|---|---|---|---|---|
| Temporary location | Highest | Slow | Requires extra | Low | File recovery, verification |
| Rename with snapshots | High | Medium | Minimal extra | Medium | Full replacement with rollback |
| Manual btrfs commands | Medium | Fast | Minimal | High | Advanced users, scripting |
| In-place restore | Lowest | Fastest | None | Low | Disaster recovery, automation |
"I accidentally deleted some files": → Use Strategy 1 (temporary location), copy just the files you need
"My system is corrupted, I need to restore /home": → Use Strategy 2 (rename with snapshots) for safety, or Strategy 4 if you're confident
"I'm setting up a new machine from backups": → Use Strategy 1 or the restore command directly - there's nothing to lose
"I'm writing an automated disaster recovery script": → Use Strategy 3 (manual commands) or Strategy 4 (in-place) depending on your safety requirements
"I'm in a minimal recovery environment without btrfs-backup-ng": → Use Strategy 3 (manual btrfs send/receive)
By default, snapshots that already exist locally are skipped:
# Skip existing (default)
btrfs-backup-ng restore ssh://...:/backups/home /mnt/restore
# Overwrite existing snapshots
btrfs-backup-ng restore ssh://...:/backups/home /mnt/restore --overwriteIf a restore operation is interrupted or fails, use these recovery commands:
# See what locks exist at the backup location
btrfs-backup-ng restore --status /mnt/backup/home --no-fs-checks --prefix "home-"
# For SSH backups
btrfs-backup-ng restore --status ssh://backup@server:/backups/home \
--no-fs-checks --prefix "home-" --ssh-sudoOutput shows:
- Restore locks - From incomplete restore operations
- Other locks - From backup/transfer operations (not touched by unlock)
- Number of available snapshots
If a restore was interrupted, locks may remain on snapshots preventing future operations:
# Unlock ALL restore sessions (recommended)
btrfs-backup-ng restore --unlock all /mnt/backup/home --no-fs-checks
# Unlock a specific session by ID
btrfs-backup-ng restore --unlock abc123 /mnt/backup/home --no-fs-checks
# For SSH backups
btrfs-backup-ng restore --unlock all ssh://backup@server:/backups/home \
--no-fs-checks --ssh-sudoNote: --unlock only removes restore locks. Backup and transfer locks are preserved to prevent accidental data loss.
If a restore failed mid-transfer, partial subvolumes may remain at the destination:
# Scan for partial restores (dry-run first)
btrfs-backup-ng restore --cleanup /mnt/restore --dry-run
# Actually clean up partial subvolumes (requires confirmation)
btrfs-backup-ng restore --cleanup /mnt/restoreThe cleanup command detects:
- Empty subvolumes
- Subvolumes containing only metadata directories
- Subvolumes with
.partialsuffix
# 1. Check what's stuck
btrfs-backup-ng restore --status /mnt/backup/home --no-fs-checks --prefix "home-"
# 2. Unlock any stuck restore sessions
btrfs-backup-ng restore --unlock all /mnt/backup/home --no-fs-checks
# 3. Clean up any partial restores at destination
btrfs-backup-ng restore --cleanup /mnt/restore
# 4. Retry the restore
btrfs-backup-ng restore /mnt/backup/home /mnt/restore \
--prefix "home-" --no-fs-checks"Source does not seem to be a btrfs subvolume":
# Backup directories are regular directories containing snapshot subvolumes
# Use --no-fs-checks to skip subvolume verification when listing/restoring
btrfs-backup-ng restore --list /mnt/backup/home --no-fs-checks --prefix "home-"
# This is normal - the backup location is a directory, not a subvolume itself"Destination is not on a btrfs filesystem":
# The restore target must be on btrfs
df -T /mnt/restore | grep btrfs
# If not btrfs, create or mount a btrfs filesystem first"No snapshots found at backup location":
# List available snapshots to see what's there
btrfs-backup-ng restore --list ssh://backup@server:/backups/home --no-fs-checks
# IMPORTANT: The prefix must include the trailing hyphen!
# If snapshots are named "home-20260104-120000", use --prefix "home-"
btrfs-backup-ng restore --list /mnt/backup --no-fs-checks --prefix "home-"
# Without the correct prefix, date parsing will fail and snapshots won't be found"Could not parse date from snapshot name":
# This usually means the prefix doesn't match or is missing the trailing hyphen
# Snapshot name format: {prefix}{YYYYMMDD-HHMMSS}
# Example: home-20260104-120000 requires --prefix "home-"
# Check your snapshot names first
ls /mnt/backup/home/
# Output: home-20260104-120000 home-20260104-140000 ...
# Then use the correct prefix (including the hyphen)
btrfs-backup-ng restore --list /mnt/backup/home --prefix "home-" --no-fs-checks"Permission denied" during restore:
# For SSH backups, ensure ssh_sudo is enabled
btrfs-backup-ng restore ssh://...:/backups/home /mnt/restore --ssh-sudo
# Local restore requires root for btrfs receive
sudo btrfs-backup-ng restore /mnt/backup /mnt/restore --no-fs-checks"btrfs send/receive failed" when snapshot already exists:
# By default, existing snapshots are skipped but may show errors
# Use --overwrite to replace existing snapshots
btrfs-backup-ng restore /mnt/backup /mnt/restore --overwrite --no-fs-checks
# Or restore to a clean directory
mkdir /mnt/restore-new
btrfs-backup-ng restore /mnt/backup /mnt/restore-new --no-fs-checksTransfer is very slow:
# Use compression for remote restores
btrfs-backup-ng restore ssh://...:/backups/home /mnt/restore --compress=zstd
# Check if incremental is working (should show "incremental from X")
btrfs-backup-ng restore ssh://...:/backups/home /mnt/restore -v
# Limit bandwidth if needed
btrfs-backup-ng restore ssh://...:/backups/home /mnt/restore --rate-limit=50M-
The
--no-fs-checksflag is usually required when listing or restoring from backup directories. Backup locations are regular directories containing snapshot subvolumes, not subvolumes themselves. -
The
--prefixmust include the trailing hyphen that separates the prefix from the timestamp. If your snapshots are namedhome-20260104-120000, use--prefix "home-"not--prefix "home". -
Restore destination must be on btrfs. The
btrfs receivecommand requires a btrfs filesystem to create subvolumes. -
Incremental restore chains are automatic. When restoring a snapshot that depends on parent snapshots, all required parents are restored first in the correct order.
-
Root privileges are typically required for
btrfs receive. Usesudofor local restores or--ssh-sudofor remote restores.
Verify that your backups are valid and restorable with the verify command. A backup you can't restore from is worthless - regular verification ensures your disaster recovery will work when you need it.
| Level | Speed | What It Checks |
|---|---|---|
metadata |
Fast | Snapshot existence, parent chain completeness |
stream |
Medium | btrfs send stream can be generated (no data transfer) |
full |
Slow | Actually restores to temp location and verifies |
# Verify local backup
btrfs-backup-ng verify /mnt/backup/home --no-fs-checks
# Verify remote backup over SSH
btrfs-backup-ng verify ssh://backup@server:/backups/home --ssh-sudo --no-fs-checksTests that btrfs send stream can be generated without transferring data:
btrfs-backup-ng verify /mnt/backup/home --level stream --no-fs-checksThe definitive test - actually restores snapshot to a temporary location:
# For local backups (temp dir created automatically)
btrfs-backup-ng verify /mnt/backup/home --level full --no-fs-checks
# For remote backups (must specify local btrfs temp dir)
btrfs-backup-ng verify ssh://server:/backups/home --level full \
--temp-dir /mnt/btrfs-test --ssh-sudo --no-fs-checksbtrfs-backup-ng verify /mnt/backup/home --snapshot home-20260104-120000 --no-fs-checks# Get machine-readable output
btrfs-backup-ng verify /mnt/backup/home --json --no-fs-checks
# Example: Alert on failure
if ! btrfs-backup-ng verify /mnt/backup/home --quiet --no-fs-checks; then
echo "Backup verification failed!" | mail -s "Backup Alert" admin@example.com
fi| Option | Description |
|---|---|
--level |
Verification level: metadata, stream, or full |
--snapshot NAME |
Verify only specific snapshot |
--temp-dir PATH |
Temp directory for full verification (must be on btrfs) |
--no-cleanup |
Keep restored snapshots after full verification |
--prefix PREFIX |
Filter snapshots by prefix |
--ssh-sudo |
Use sudo for remote btrfs commands |
--ssh-key FILE |
SSH private key file |
--no-fs-checks |
Skip btrfs subvolume verification |
--json |
Output in JSON format |
-q, --quiet |
Suppress progress output |
btrfs-backup-ng can back up and restore Snapper-managed snapshots. This is useful when you already use Snapper for local snapshot management (common on openSUSE, Fedora, and Arch systems) and want to replicate those snapshots to remote backup storage.
- Leverage existing snapshots: Back up snapshots that Snapper already creates (timeline, pre/post zypper, etc.)
- No duplicate snapshots: Don't create separate backup snapshots when Snapper already has them
- Preserve metadata: Snapper's info.xml is preserved, enabling proper restoration back into Snapper
- Incremental transfers: Efficient delta-based transfers between snapshots
- Seamless restore: Restored snapshots integrate directly with your existing Snapper configuration
# 1. Detect your snapper configurations
btrfs-backup-ng snapper detect
# 2. List snapshots for a specific config
btrfs-backup-ng snapper list --config root
# 3. Backup all eligible snapshots to a target
sudo btrfs-backup-ng snapper backup root /mnt/backup/root
# 4. Or backup to a remote server
sudo btrfs-backup-ng snapper backup root ssh://backup@server:/backups/root
# 5. Generate TOML config for automated backups
btrfs-backup-ng snapper generate-config root --target /mnt/backupThe snapper subcommand provides dedicated tools for working with Snapper-managed snapshots:
| Command | Description |
|---|---|
snapper detect |
Discover Snapper configurations on the system |
snapper list |
List snapshots for one or all configs |
snapper backup |
Back up snapshots to a target location |
snapper restore |
Restore snapshots from backup |
snapper status |
Show backup status for configs |
snapper generate-config |
Generate TOML configuration |
# Discover all snapper configs
btrfs-backup-ng snapper detect
# JSON output for scripting
btrfs-backup-ng snapper detect --jsonExample output:
Found 2 snapper configuration(s):
root:
Subvolume: /
Snapshots dir: /.snapshots
Status: OK
home:
Subvolume: /home
Snapshots dir: /home/.snapshots
Status: OK
# List all snapshots for all configs
btrfs-backup-ng snapper list
# List snapshots for a specific config
btrfs-backup-ng snapper list --config root
# Filter by snapshot type
btrfs-backup-ng snapper list --config root --type single
btrfs-backup-ng snapper list --type pre --type post
# JSON output
btrfs-backup-ng snapper list --jsonExample output:
Config: root (/)
------------------------------------------------------------
NUM TYPE DATE DESCRIPTION
------ ------ ------------------- --------------------
559 single 2026-01-08 14:00:00 timeline
560 single 2026-01-08 15:00:00 timeline
561 pre 2026-01-08 16:30:00 zypper install
562 post 2026-01-08 16:31:00 zypper install
# Backup all eligible snapshots
sudo btrfs-backup-ng snapper backup root /mnt/backup/root
# Backup to remote server with compression
sudo btrfs-backup-ng snapper backup root ssh://backup@server:/backups/root
# Backup a specific snapshot
sudo btrfs-backup-ng snapper backup root /mnt/backup/root --snapshot 559
# Filter by type (only timeline snapshots)
sudo btrfs-backup-ng snapper backup root /mnt/backup/root --type single
# Skip snapshots younger than 1 hour
sudo btrfs-backup-ng snapper backup root /mnt/backup/root --min-age 1h
# Dry run - see what would be backed up
sudo btrfs-backup-ng snapper backup root /mnt/backup/root --dry-run
# With compression and rate limiting
sudo btrfs-backup-ng snapper backup root ssh://server:/backups \
--compress zstd --rate-limit 50M# List available backups at a location
btrfs-backup-ng snapper restore --list /mnt/backup/root
btrfs-backup-ng snapper restore --list ssh://server:/backups/root
# Restore a specific snapshot
sudo btrfs-backup-ng snapper restore /mnt/backup/root --snapshot 559 root
# Restore multiple snapshots
sudo btrfs-backup-ng snapper restore /mnt/backup/root --snapshot 559 --snapshot 560 root
# Restore all backups
sudo btrfs-backup-ng snapper restore /mnt/backup/root --all root
# Dry run
sudo btrfs-backup-ng snapper restore /mnt/backup/root --snapshot 559 root --dry-runThe restore command:
- Lists available backups at the source location
- Transfers snapshots using incremental
btrfs send/receive - Creates the snapshot in your local Snapper directory (e.g.,
/.snapshots/{new_num}/) - Generates
info.xmlfrom the backup metadata - The snapshot is immediately visible to Snapper
# Show local snapshot counts
btrfs-backup-ng snapper status
# Check what's backed up to a target
btrfs-backup-ng snapper status --target /mnt/backup/root
btrfs-backup-ng snapper status --target ssh://server:/backups/root
# Status for specific config
btrfs-backup-ng snapper status --config root
# JSON output
btrfs-backup-ng snapper status --jsonGenerate TOML configuration for automated snapper backups:
# Generate config for all snapper configs
btrfs-backup-ng snapper generate-config
# Generate for specific configs
btrfs-backup-ng snapper generate-config root home
# With target path
btrfs-backup-ng snapper generate-config root --target ssh://server:/backups
# Save to file
btrfs-backup-ng snapper generate-config root --target /mnt/backup -o snapper.toml
# Append to existing config
btrfs-backup-ng snapper generate-config root --target /mnt/backup --append ~/.config/btrfs-backup-ng/config.toml
# Filter snapshot types
btrfs-backup-ng snapper generate-config root --type single --type pre
# With SSH sudo
btrfs-backup-ng snapper generate-config root --target ssh://server:/backups --ssh-sudoFor automated backups, add snapper volumes to your config.toml:
[[volumes]]
path = "/"
source = "snapper" # Use snapper as snapshot source
[volumes.snapper]
config_name = "root" # Snapper config name
include_types = ["single", "pre", "post"] # Snapshot types to backup
min_age = "1h" # Skip snapshots younger than 1 hour
[[volumes.targets]]
path = "ssh://backup@server:/backups/root"
ssh_sudo = true
compress = "zstd"Then run backups normally:
# This will backup snapper snapshots for configured volumes
sudo btrfs-backup-ng run| Option | Description | Default |
|---|---|---|
config_name |
Snapper config name (e.g., root, home) or auto to detect |
auto |
include_types |
Snapshot types to include: single, pre, post |
["single", "pre", "post"] |
exclude_cleanup |
Cleanup algorithms to skip: number, timeline, empty-pre-post |
[] |
min_age |
Minimum snapshot age before backing up (e.g., 30m, 1h, 1d) |
0 |
Snapshot Types:
single: Standalone snapshots (manual or timeline)pre: Pre-action snapshots (before zypper/dnf operations)post: Post-action snapshots (after zypper/dnf operations)
Why use min_age?
Pre/post snapshot pairs may be incomplete for a short time during package operations. Setting min_age = "1h" ensures you only backup completed pairs.
Snapper backups preserve the native Snapper directory structure:
/backups/root/.snapshots/
├── 559/
│ ├── info.xml # Snapper metadata (type, description, date, etc.)
│ └── snapshot/ # Read-only btrfs subvolume
├── 560/
│ ├── info.xml
│ └── snapshot/
└── 561/
├── info.xml
└── snapshot/
This layout enables:
- Direct restoration back into Snapper-managed volumes
- Easy browsing of backup contents
- Preservation of all Snapper metadata
Both backup and restore operations use incremental btrfs send/receive when possible:
Backup transfer:
Snapshot 559: 2.1 GB (full, no parent)
Snapshot 560: 156 MB (incremental from 559)
Snapshot 561: 89 MB (incremental from 560)
Restore transfer:
Snapshot 559: 2.1 GB (full)
Snapshot 560: 156 MB (incremental, parent 559 exists locally)
Snapshot 561: 89 MB (incremental, parent 560 exists locally)
The commands automatically detect available parents and use the most efficient transfer method.
# 1. Verify snapper is configured
btrfs-backup-ng snapper detect
# 2. Test manual backup
sudo btrfs-backup-ng snapper backup root /mnt/external-drive/backups/root
# 3. Generate configuration
btrfs-backup-ng snapper generate-config root \
--target ssh://backup@nas:/backups \
--type single \
--min-age 1h \
-o ~/.config/btrfs-backup-ng/config.toml
# 4. Validate the configuration
btrfs-backup-ng config validate
# 5. Test automated backup
sudo btrfs-backup-ng run --dry-run
sudo btrfs-backup-ng run
# 6. Set up systemd timer for hourly backups
sudo btrfs-backup-ng install --timer=hourly# 1. Boot from recovery media
# 2. Mount your btrfs root filesystem
mount /dev/sda2 /mnt/newroot
# 3. List available backups
btrfs-backup-ng snapper restore --list ssh://backup@nas:/backups/root
# 4. Restore the snapshot you need
sudo btrfs-backup-ng snapper restore \
ssh://backup@nas:/backups/root \
--snapshot 560 \
root
# 5. The snapshot is now in /.snapshots/
# Use snapper rollback or manual subvolume operations to restore
snapper -c root rollback 560| Feature | Native Mode | Snapper Integration |
|---|---|---|
| Snapshot creation | btrfs-backup-ng creates | Snapper creates |
| Snapshot naming | {prefix}-{timestamp} |
Snapper's numbered format |
| Metadata | None | info.xml preserved |
| Pre/post pairs | Not applicable | Fully supported |
| Retention | btrfs-backup-ng policies | Snapper policies (local), btrfs-backup-ng (remote) |
| Best for | Systems without Snapper | Snapper-managed systems |
When to use Snapper integration:
- You already use Snapper for local snapshots
- You want to preserve Snapper metadata
- You need pre/post transaction pairs
- You're on openSUSE, Fedora, or Arch with Snapper configured
When to use native mode:
- You don't use Snapper
- You want btrfs-backup-ng to manage everything
- Simpler setup without Snapper dependency
The doctor command analyzes your backup system and diagnoses potential problems. It checks configuration, snapshot health, transfer state, and system resources, with optional auto-fix capabilities.
# Run full diagnostics
btrfs-backup-ng doctor
# JSON output for scripting/monitoring
btrfs-backup-ng doctor --json
# Check specific category only
btrfs-backup-ng doctor --check config
btrfs-backup-ng doctor --check snapshots
btrfs-backup-ng doctor --check transfers
btrfs-backup-ng doctor --check system
# Only show problems (suppress OK messages)
btrfs-backup-ng doctor --quiet
# Check specific volume
btrfs-backup-ng doctor --volume /homeSome issues can be automatically fixed:
# Auto-fix safe issues (stale locks, temp files)
btrfs-backup-ng doctor --fix
# Interactive fix (confirm each fix)
btrfs-backup-ng doctor --fix --interactivebtrfs-backup-ng Doctor
=======================================================
Configuration
[OK] Config file valid
[OK] Volume /home exists and is btrfs subvolume
[WARN] Compression program 'pigz' not found
Snapshots
[OK] Volume /home: 24 snapshots, chain intact
Transfers
[WARN] Stale lock: home-20260110 (process 12345 not running)
[FIXABLE] Run with --fix to remove
System
[OK] Destination: 524 GiB available (52%)
[OK] Systemd timer active, next: 2h 15m
[OK] Last backup: 2h ago
=======================================================
Summary: 8 passed, 2 warnings, 0 errors
Fixable: 1 issue (run with --fix)
| Category | Checks |
|---|---|
| config | Config file exists and valid, volume paths exist, targets reachable, compression programs available |
| snapshots | Orphaned snapshots, missing snapshots, broken parent chains |
| transfers | Stale locks (dead processes), incomplete transfers, recent failures |
| system | Destination space, quota limits, systemd timer status, backup age |
| Code | Meaning |
|---|---|
| 0 | Healthy (all OK or INFO) |
| 1 | Warnings present |
| 2 | Errors or critical issues |
| Option | Description |
|---|---|
--json |
Output in JSON format |
--check CATEGORY |
Check specific category: config, snapshots, transfers, system |
--fix |
Auto-fix safe issues |
--interactive |
Confirm each fix before applying |
--quiet |
Only show problems |
--volume PATH |
Check specific volume only |
Before running backups, you can estimate how much data will be transferred using the estimate command. This helps with:
- Planning bandwidth usage and backup windows
- Verifying expected transfer sizes
- Checking destination space availability (including btrfs quota limits)
- Scripting and automation decisions
# Estimate transfer size for direct paths
btrfs-backup-ng estimate /mnt/snapshots /mnt/backup
# Estimate for remote backup
btrfs-backup-ng estimate /home/.snapshots ssh://backup@server:/backups/homeThe --check-space flag adds destination space verification to the estimate, including btrfs quota awareness:
# Estimate with space check
btrfs-backup-ng estimate /mnt/snapshots /mnt/backup --check-space
# With custom safety margin (default is 10%)
btrfs-backup-ng estimate /mnt/snapshots /mnt/backup --check-space --safety-margin 20Example output with space check:
Backup Size Estimate
============================================================
Source: /home/.snapshots
Destination: /mnt/backup/home
Snapshots to transfer: 3
...
Total data to transfer: 479.80 MiB
Full size (uncompressed): 2.34 GiB
Estimation time: 1.23s
Destination Space Check
------------------------------------------------------------
Filesystem space: 850.00 GiB available of 1.00 TiB
Quota limit: 100.00 GiB (45.50 GiB used, 54.50 GiB remaining)
Effective limit: 54.50 GiB (quota is more restrictive)
Required: 479.80 MiB (+ 10% safety margin = 527.78 MiB)
Status: OK - Sufficient space available
When quotas are enabled on the destination, the space check uses the more restrictive of filesystem free space or quota remaining.
# List configured volumes and their targets
btrfs-backup-ng estimate --list-volumes
# Estimate for a specific volume from config
btrfs-backup-ng estimate --volume /home
# Estimate for a specific target (0-based index)
btrfs-backup-ng estimate --volume /home --target 1
# With space check
btrfs-backup-ng estimate --volume /home --check-space# Get machine-readable output with space check
btrfs-backup-ng estimate --volume /home --check-space --jsonThe JSON output includes a space_check object with:
available_bytes,total_bytes- Filesystem spacequota_enabled,quota_limit_bytes,quota_used_bytes- Quota informationeffective_available_bytes- The actual available space (min of fs and quota)required_with_margin_bytes- Required space including safety marginsufficient- Boolean indicating if transfer will fit
Backup Size Estimate
============================================================
Source: /home/.snapshots
Destination: ssh://backup@server:/backups/home
Snapshots already at destination: 45
Snapshots to transfer: 3
Snapshot Size Type Parent
---------------------------------------- ------------ ------------ --------------------
home-20260104-080000 156.23 MiB incremental home-20260103-200000
home-20260104-120000 89.45 MiB incremental home-20260104-080000
home-20260104-160000 234.12 MiB incremental home-20260104-120000
------------------------------------------------------------
Total data to transfer: 479.80 MiB
Full size (uncompressed): 2.34 GiB
Estimation time: 1.23s
| Option | Description |
|---|---|
--volume PATH |
Estimate for volume defined in config |
--target INDEX |
Target index to estimate for (0-based) |
-c, --config FILE |
Path to configuration file |
--prefix PREFIX |
Snapshot prefix filter |
--ssh-sudo |
Use sudo on remote host |
--ssh-key FILE |
SSH private key file |
--check-space |
Check if destination has sufficient space |
--safety-margin PERCENT |
Safety margin percentage (default: 10%) |
--json |
Output in JSON format |
The estimate command uses multiple methods to determine sizes:
- btrfs subvolume show - Uses exclusive data size (most accurate, requires quotas)
- btrfs filesystem du - Handles reflinks and deduplication correctly
- du - Fallback for when btrfs commands aren't available
For incremental transfers, the command uses btrfs send --no-data to estimate the delta size without actually transferring data.
The config detect command scans your system for btrfs subvolumes and helps you set up backup configurations:
# Scan for subvolumes (requires root for full scan)
sudo btrfs-backup-ng config detect
# Output in JSON format for scripting
sudo btrfs-backup-ng config detect --json
# Launch interactive wizard with detected volumes
sudo btrfs-backup-ng config detect --wizard============================================================
Btrfs Subvolume Detection Results
============================================================
Found 19 btrfs filesystem(s)
Found 262 subvolume(s)
----------------------------------------
Recommended for backup:
----------------------------------------
[1] /home
Type: user data
Suggested prefix: home
[2] /home/user/.config/google-chrome
Type: user data
Suggested prefix: home-user-.config-google-chrome
----------------------------------------
Optional (lower priority):
----------------------------------------
- /opt (system data)
- /var/log (variable)
- /var/cache (variable)
----------------------------------------
Excluded (snapshots/system):
----------------------------------------
- 185 snapshot subvolumes
- /var/lib/machines (system internal)
----------------------------------------
Next steps:
----------------------------------------
To create a configuration based on these results:
btrfs-backup-ng config detect --wizard
Or create a configuration manually:
btrfs-backup-ng config init --interactive
The detection categorizes subvolumes as:
- Recommended: User data directories like
/homethat should be backed up - Optional: System data that may or may not need backup
- Excluded: Existing snapshots and system-internal subvolumes
The original command-line interface still works:
# Basic backup
btrfs-backup-ng /source/subvolume /destination/path
# Remote backup
btrfs-backup-ng /home ssh://backup@server:/backups/home
# With options
btrfs-backup-ng --ssh-sudo --num-snapshots 10 /source ssh://user@host:/destLegacy mode is auto-detected when the first argument is a path.
btrfs-backup-ng can install systemd timer and service units for automated backups. There are two installation modes:
| Mode | Install Location | Runs As | Use Case |
|---|---|---|---|
| System-wide | /etc/systemd/system/ |
root | Servers, production systems |
| User-level | ~/.config/systemd/user/ |
current user | Personal workstations |
System-wide installation runs backups as root, which is required for backing up system volumes.
Prerequisites:
- Configuration file at
/etc/btrfs-backup-ng/config.toml(or user config) - Root privileges (sudo)
Installation:
# Install with a preset timer
sudo btrfs-backup-ng install --timer=hourly # Every hour at :00
sudo btrfs-backup-ng install --timer=daily # Daily at 02:00
sudo btrfs-backup-ng install --timer=weekly # Weekly on Sunday at 02:00
# Or use a custom OnCalendar specification
sudo btrfs-backup-ng install --oncalendar='*:0/15' # Every 15 minutes
sudo btrfs-backup-ng install --oncalendar='*:0/5' # Every 5 minutes
sudo btrfs-backup-ng install --oncalendar='*-*-* 02:00:00' # Daily at 2am
sudo btrfs-backup-ng install --oncalendar='Mon *-*-* 03:00' # Monday at 3amEnable and start the timer:
sudo systemctl daemon-reload
sudo systemctl enable --now btrfs-backup-ng.timerGenerated files:
/etc/systemd/system/btrfs-backup-ng.service- The backup service unit/etc/systemd/system/btrfs-backup-ng.timer- The timer unit
User-level installation runs backups as the current user. Useful for personal workstations where sudo is available but system-wide installation isn't desired.
Note: User-level services require the user to be logged in (or lingering enabled).
# Install user timer
btrfs-backup-ng install --user --timer=hourly
# Enable and start
systemctl --user daemon-reload
systemctl --user enable --now btrfs-backup-ng.timer
# Optional: Enable lingering so timers run even when logged out
loginctl enable-linger $USERGenerated files:
~/.config/systemd/user/btrfs-backup-ng.service~/.config/systemd/user/btrfs-backup-ng.timer
| Preset | OnCalendar Value | Description |
|---|---|---|
hourly |
*:00 |
Every hour at minute 00 |
daily |
*-*-* 02:00:00 |
Every day at 2:00 AM |
weekly |
Sun *-*-* 02:00:00 |
Every Sunday at 2:00 AM |
The --oncalendar option accepts any valid systemd calendar specification:
| Specification | Description |
|---|---|
*:0/5 |
Every 5 minutes |
*:0/15 |
Every 15 minutes |
*:0/30 |
Every 30 minutes |
hourly |
Every hour |
*-*-* 02:00:00 |
Daily at 2:00 AM |
*-*-* 00/6:00:00 |
Every 6 hours |
Mon,Thu *-*-* 03:00:00 |
Monday and Thursday at 3:00 AM |
*-*-01 04:00:00 |
First day of each month at 4:00 AM |
Test your specification with: systemd-analyze calendar '*:0/15'
Check timer status:
# System-wide
systemctl status btrfs-backup-ng.timer
systemctl list-timers btrfs-backup-ng.timer
# User-level
systemctl --user status btrfs-backup-ng.timer
systemctl --user list-timers btrfs-backup-ng.timerView logs:
# System-wide - recent logs
journalctl -u btrfs-backup-ng.service -n 50
# System-wide - follow logs in real-time
journalctl -u btrfs-backup-ng.service -f
# System-wide - logs since last boot
journalctl -u btrfs-backup-ng.service -b
# User-level
journalctl --user -u btrfs-backup-ng.service -n 50Manually trigger a backup:
# System-wide
sudo systemctl start btrfs-backup-ng.service
# User-level
systemctl --user start btrfs-backup-ng.serviceThe uninstall command removes the timer and service files:
# System-wide
sudo btrfs-backup-ng uninstall
# User-level
btrfs-backup-ng uninstallNote: Uninstall checks both system and user locations and removes files from wherever they're found.
# 1. Create and configure (using interactive wizard)
sudo mkdir -p /etc/btrfs-backup-ng
sudo btrfs-backup-ng config init -i -o /etc/btrfs-backup-ng/config.toml
sudo chmod 600 /etc/btrfs-backup-ng/config.toml
# Or manually: btrfs-backup-ng config init | sudo tee /etc/btrfs-backup-ng/config.toml
# sudo $EDITOR /etc/btrfs-backup-ng/config.toml
# 2. Validate and test
sudo btrfs-backup-ng config validate
sudo btrfs-backup-ng run --dry-run
sudo btrfs-backup-ng run
# 3. Install timer (hourly backups)
sudo btrfs-backup-ng install --timer=hourly
# 4. Enable timer
sudo systemctl daemon-reload
sudo systemctl enable --now btrfs-backup-ng.timer
# 5. Verify
systemctl list-timers btrfs-backup-ng.timer
sudo btrfs-backup-ng statusbtrfs-backup-ng performs filesystem verification checks to ensure sources are valid btrfs subvolumes and destinations are on btrfs filesystems. The --fs-checks option controls this behavior:
| Mode | Behavior | Use Case |
|---|---|---|
auto (default) |
Warns about issues but continues | Normal operation - user-friendly |
strict |
Errors out on any check failure | Production systems requiring validation |
skip |
Bypasses all filesystem checks | Backup directories, special setups |
# Default auto mode - warns but continues
btrfs-backup-ng estimate /mnt/snapshots /mnt/backup
# Strict mode - fail on any check issue
btrfs-backup-ng run --fs-checks=strict
# Skip checks entirely (useful for listing backup directories)
btrfs-backup-ng restore --list /mnt/backup/home --fs-checks=skip
# Backwards-compatible aliases
btrfs-backup-ng verify /mnt/backup --no-fs-checks # Same as --fs-checks=skipAuto mode (default):
- Normal backup operations
- When you want warnings but not hard failures
- First-time setup to identify potential issues
Strict mode:
- Automated/unattended backups where failures should stop execution
- Production environments with strict data integrity requirements
- When you need to ensure all paths are valid btrfs subvolumes
Skip mode:
- Listing or restoring from backup directories (which are regular directories, not subvolumes)
- Working with non-standard btrfs setups
- When you've verified the setup manually
In auto mode, you'll see warnings like:
WARNING: /mnt/backup/home does not seem to be a btrfs subvolume - continuing anyway (auto mode)
In strict mode, the same issue causes an error:
ERROR: /mnt/backup/home does not seem to be a btrfs subvolume. Use --no-fs-checks to override.
btrfs-backup-ng -v run # Verbose
btrfs-backup-ng -vv run # Debug levelPermission denied on remote:
# Check sudo configuration
ssh user@remote 'sudo -n btrfs --version'
# Enable ssh_sudo in config
[[volumes.targets]]
path = "ssh://user@remote:/backup"
ssh_sudo = trueSnapshot directory doesn't exist:
# btrfs-backup-ng creates it automatically, but ensure parent exists
mkdir -p /path/to/.snapshotsTransfer fails with compression:
# Verify compression tool is installed on both ends
which zstd
ssh user@remote 'which zstd'Rate limiting not working:
# Install pv (pipe viewer)
sudo dnf install pvbtrfs-backup-ng config validategit clone https://github.com/berrym/btrfs-backup-ng.git
cd btrfs-backup-ng
pip install -e .
python -m btrfs_backup_ng --helpsrc/btrfs_backup_ng/
__main__.py # Entry point
config/ # TOML configuration system
schema.py # Config dataclasses
loader.py # TOML loading and validation
cli/ # Subcommand modules
dispatcher.py # Command routing
run.py, snapshot.py, transfer.py, prune.py, ...
core/ # Core operations
operations.py # send_snapshot, sync_snapshots
transfer.py # Compression, throttling
planning.py # Transfer planning
endpoint/ # Endpoint implementations
local.py # Local filesystem
ssh.py # SSH remote
shell.py # Shell commands
retention.py # Time-based retention logic
btrbk_import.py # btrbk config importer
- CLI Reference - Complete command and option reference
- Migrating from btrbk - Guide for btrbk users
- Changelog - Version history and release notes
Ready-to-use configuration examples in examples/:
| File | Description |
|---|---|
minimal.toml |
Simple local backup |
remote-backup.toml |
Multi-volume SSH backup |
server.toml |
Server with frequent snapshots |
snapper.toml |
Snapper integration examples |
config.toml |
Full reference with all options |
MIT License - see LICENSE for details.
Contributions are welcome! Please:
- Follow PEP 8 style guidelines
- Add tests for new features
- Update documentation as needed
- Maintain backward compatibility
- btrbk - The Perl-based tool that inspired many features
- btrfs-progs - BTRFS utilities
- Snapper - Snapshot management tool