Skip to content

Latest commit

 

History

History
1629 lines (1229 loc) · 53.9 KB

CLI-REFERENCE.md

File metadata and controls

1629 lines (1229 loc) · 53.9 KB

btrfs-backup-ng CLI Reference

Complete reference for all btrfs-backup-ng commands and options.

Global Options

These options can be used with any command:

-h, --help          Show help message and exit
-V, --version       Show version and exit
-c, --config FILE   Path to configuration file
-v, --verbose       Show debug output on the console
-q, --quiet         Suppress non-essential output
--debug             Enable debug output
--btrfs-debug       Run btrfs send and receive with -vv and log every line they
                    print, one per file operation, as it arrives; implies
                    --debug. Same as [global] btrfs_debug = true

Where SSH Options Live

Two kinds of command, two places to put SSH settings:

command shape examples SSH options come from
names a target on the command line restore, verify, estimate, snapper backup, snapper restore, raw list, raw verify --ssh-sudo, --ssh-key, --ssh-auth-sock, --ssh-host-key-policy
runs against the configuration run, snapshot, transfer, prune the target's own keys in the config file

This is deliberate, not an omission. A config-driven command can fan out to several targets in one invocation -- an offsite server and a local NAS, with different users and different keys -- so a single --ssh-key on the command line would have no unambiguous meaning. Those commands therefore read ssh_sudo, ssh_key, ssh_auth_sock and ssh_host_key_policy from each [[volumes.targets]] entry, where they can differ per target:

[[volumes.targets]]
path = "ssh://backup@offsite:/mnt/backups"
ssh_key = "/home/user/.ssh/id_ed25519_offsite"
ssh_sudo = true

[[volumes.targets]]
path = "raw+ssh://archive@nas:/srv/archive"
ssh_key = "/home/user/.ssh/id_ed25519_nas"

Where a command names one target, the flag is unambiguous and is accepted.

Note that --ssh-sudo does NOT mean the same thing on every scheme: on ssh:// it elevates only btrfs, while on raw+ssh:// it elevates every remote command. See the Permissions section of the README for what each scheme needs.

Commands

run

Execute all configured backup jobs (snapshot + transfer + prune).

btrfs-backup-ng run [OPTIONS]

Options:

Option Description
--dry-run Show what would be done without making changes
--parallel-volumes N Max concurrent volume backups (overrides config)
--parallel-targets N Max concurrent target transfers per volume
--compress METHOD Compression method: none, gzip, zstd, lz4, pigz, lzop
--rate-limit RATE Bandwidth limit (e.g., '10M', '1G', '500K')
--progress Force progress bar display
--no-progress Disable progress bar display

Examples:

# Full backup run
btrfs-backup-ng run

# Dry run to see what would happen
btrfs-backup-ng run --dry-run

# With compression and rate limiting
btrfs-backup-ng run --compress=zstd --rate-limit=50M

# Parallel execution
btrfs-backup-ng run --parallel-volumes=3 --parallel-targets=2

snapshot

Create snapshots only, without transferring to targets.

btrfs-backup-ng snapshot [OPTIONS]

Options:

Option Description
--dry-run Show what would be done without making changes
--volume PATH Only snapshot specific volume(s), can be repeated

Examples:

# Snapshot all configured volumes
btrfs-backup-ng snapshot

# Snapshot specific volume only
btrfs-backup-ng snapshot --volume=/home

# Dry run
btrfs-backup-ng snapshot --dry-run

transfer

Transfer existing snapshots to targets without creating new snapshots.

btrfs-backup-ng transfer [OPTIONS]

Options:

Option Description
--dry-run Show what would be done without making changes
--volume PATH Only transfer specific volume(s)
--compress METHOD Compression method (overrides config)
--rate-limit RATE Bandwidth limit (overrides config)

Examples:

# Transfer all pending snapshots
btrfs-backup-ng transfer

# Transfer with compression
btrfs-backup-ng transfer --compress=zstd

# Transfer specific volume with rate limiting
btrfs-backup-ng transfer --volume=/home --rate-limit=10M

prune

Apply retention policies to clean up old snapshots and backups.

btrfs-backup-ng prune [OPTIONS]

Options:

Option Description
--dry-run Show what would be deleted without making changes
--yes, -y Skip the interactive confirmation prompt (for automation)
--force Prune even under a degenerate policy that would keep only the latest snapshot

Confirmation prompt. On an interactive terminal, a real (non---dry-run) prune first prints the full deletion plan and asks for confirmation before deleting anything:

About to delete 5 snapshot(s)/backup(s):
  source /home -- 2:
    - home-20260101-000000
    - home-20260102-000000
  target /mnt/backup/home -- 3:
    - home-20260103-000000
    - home-20260104-000000
    - home-20260105-000000
Proceed with deleting 5? [y/N]

Declining aborts with zero deletions. In a non-interactive context (cron, a pipe, a script) the prompt is skipped and the prune proceeds — use --yes to skip the prompt in an interactive terminal too.

Degenerate-policy guard. A retention policy that would keep only the latest snapshot — all time buckets set to 0 and min ≤ 1 day — is almost always a misconfiguration that would delete your entire history. Prune refuses such a policy (deleting nothing, with a non-zero exit) unless you pass --force, and this refusal applies even non-interactively so a fat-fingered config cannot silently wipe your backups. A legitimate short-window policy (e.g. min = "30d" with all buckets 0) is not degenerate and prunes normally.

Examples:

# Apply retention policies (prompts for confirmation on an interactive terminal)
btrfs-backup-ng prune

# See what would be deleted, change nothing
btrfs-backup-ng prune --dry-run

# Non-interactive / automation: skip the prompt
btrfs-backup-ng prune --yes

# Intentionally prune under a keep-only-latest policy
btrfs-backup-ng prune --force

list

Show snapshots and backups across all configured volumes and targets.

btrfs-backup-ng list [OPTIONS]

Options:

Option Description
--volume PATH Only list specific volume(s)
--json Output in JSON format

Examples:

# List all snapshots
btrfs-backup-ng list

# List specific volume
btrfs-backup-ng list --volume=/home

# JSON output for scripting
btrfs-backup-ng list --json

status

Show job status, last run times, statistics, and transaction history.

btrfs-backup-ng status [OPTIONS]

Options:

Option Description
-t, --transactions Show recent transaction history
-n, --limit N Number of transactions to show (default: 10)

Examples:

# Basic status
btrfs-backup-ng status

# Status with transaction history
btrfs-backup-ng status --transactions

# Show last 20 transactions
btrfs-backup-ng status -t -n 20

Output includes:

  • Volume and target health status
  • Snapshot counts
  • Transaction statistics (if transaction_log configured)
  • Recent transaction history (with --transactions flag)

config

Configuration management subcommands.

config validate

Validate the configuration file.

btrfs-backup-ng config validate

Examples:

# Validate default config location
btrfs-backup-ng config validate

# Validate specific config file
btrfs-backup-ng -c /path/to/config.toml config validate

Besides the file itself, config validate checks, on this machine and without root, that each enabled volume's source can be backed up: it can be read from this account, exists, and is a directory on btrfs. A native source must also be a subvolume root -- btrfs subvolume snapshot refuses anything else -- and a directory inside a subvolume is reported with the subvolume that holds it. A snapper source (source = "snapper") is not required to be one: snapper takes its snapshots, and with config_name = "auto" any path at or below a snapper subvolume selects that config. Remote targets are not probed; doctor does that. doctor asks the same question of each source.

Exit status:

  • 0 -- the file is valid and its volumes are usable here
  • 1 -- no config file was found, or the file itself is invalid and must be edited
  • 2 -- the file is valid, but this machine cannot back up these volumes: a source is missing, cannot be read from this account, is not a directory, is not on btrfs, or (native sources) is not a subvolume root

Note — unknown keys are reported. Loading the config warns about any key a parser does not recognize, e.g. Unknown config key 'ssh_prot' in [volumes[0].targets[0]] (ignored). A misspelled or misplaced key (a typo, or a value under the wrong table) is silently ignored by the loader, which would quietly void a safety setting such as retention, encrypt, or ssh_host_key_policy — the warning surfaces it so you can fix it. The warning is non-fatal; the rest of the config still loads. Any command that loads the config prints these warnings; config validate is the quickest way to check.

config init

Generate an example configuration file.

btrfs-backup-ng config init [OPTIONS]

Options:

Option Description
-o, --output FILE Output file (default: stdout)

Examples:

# Print example config to stdout
btrfs-backup-ng config init

# Save to file
btrfs-backup-ng config init -o ~/.config/btrfs-backup-ng/config.toml

config import

Import a btrbk configuration file and convert to TOML.

btrfs-backup-ng config import FILE [OPTIONS]

Arguments:

Argument Description
FILE Path to btrbk.conf file

Options:

Option Description
-o, --output FILE Output file (default: stdout)

Examples:

# Convert and print 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.toml

config detect

Scan the system for btrfs subvolumes and suggest backup configurations.

btrfs-backup-ng config detect [OPTIONS]

Options:

Option Description
--json Output in JSON format for scripting
-w, --wizard Launch interactive wizard with detected volumes

Examples:

# Scan for subvolumes (requires root for full access)
sudo btrfs-backup-ng config detect

# Output in JSON format
sudo btrfs-backup-ng config detect --json

# Launch wizard with detected volumes
sudo btrfs-backup-ng config detect --wizard

The detect command categorizes subvolumes as:

  • Recommended: User data like /home that should be backed up
  • Optional: System data (/opt, /var/log) that may or may not need backup
  • Excluded: Existing snapshots and system-internal subvolumes

install

Install systemd timer and service for automated backups.

btrfs-backup-ng install [OPTIONS]

Options:

Option Description
--timer PRESET Use preset interval: hourly, daily, weekly
--oncalendar SPEC Custom OnCalendar specification
--user Install as user service (no root required)

Timer Presets:

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

Installation Modes:

Mode Location Requires
System-wide (default) /etc/systemd/system/ root (sudo)
User-level (--user) ~/.config/systemd/user/ None

Examples:

# System-wide installation (requires root)
sudo btrfs-backup-ng install --timer=hourly
sudo btrfs-backup-ng install --timer=daily
sudo btrfs-backup-ng install --timer=weekly

# Custom schedules
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 3am

# User-level installation (no root required)
btrfs-backup-ng install --user --timer=hourly

After installation, enable the timer:

# System-wide
sudo systemctl daemon-reload
sudo systemctl enable --now btrfs-backup-ng.timer

# User-level
systemctl --user daemon-reload
systemctl --user enable --now btrfs-backup-ng.timer

Verify timer is active:

systemctl list-timers btrfs-backup-ng.timer          # System-wide
systemctl --user list-timers btrfs-backup-ng.timer   # User-level

uninstall

Remove installed systemd timer and service files.

btrfs-backup-ng uninstall

Examples:

# Remove system-wide installation
sudo btrfs-backup-ng uninstall

# Remove user-level installation
btrfs-backup-ng uninstall

estimate

Estimate backup transfer sizes and optionally check destination space.

btrfs-backup-ng estimate [OPTIONS] SOURCE DESTINATION
btrfs-backup-ng estimate --volume PATH [--target INDEX] [--check-space]

Options:

Option Description
--volume PATH Estimate for volume defined in config
--target INDEX Target index to estimate for (0-based)
--prefix PREFIX Snapshot prefix filter
--check-space Check if destination has sufficient space
--safety-margin PERCENT Safety margin percentage (default: 10%)
--ssh-sudo Use sudo on remote host
--ssh-key FILE SSH private key file
--ssh-auth-sock PATH Explicit ssh-agent socket (overrides auto-discovery)
--fs-checks MODE Filesystem check mode: auto, strict, skip
--no-fs-checks Skip filesystem checks (alias for --fs-checks=skip)
--json Output in JSON format

Examples:

# Basic estimate
btrfs-backup-ng estimate /mnt/snapshots /mnt/backup

# Estimate for configured volume
btrfs-backup-ng estimate --volume /home

# Estimate with destination space check
btrfs-backup-ng estimate --volume /home --check-space

# Space check with custom safety margin
btrfs-backup-ng estimate /mnt/snapshots /mnt/backup --check-space --safety-margin 20

# JSON output for scripting
btrfs-backup-ng estimate --volume /home --check-space --json

Space Check Output:

When --check-space is enabled, the output includes destination space information:

  • Filesystem free space
  • Quota limits (if btrfs quotas are enabled)
  • Effective available space (the more restrictive of fs or quota)
  • Required space with safety margin
  • Status (OK or INSUFFICIENT)

doctor

Diagnose backup system health and optionally fix common issues.

btrfs-backup-ng doctor [OPTIONS]

Options:

Option Description
--json Output in JSON format for scripting
--check CATEGORY Check specific category: config, snapshots, transfers, system
--fix Auto-fix safe issues (stale locks, temp files)
--interactive Confirm each fix before applying (requires --fix)
-q, --quiet Only show problems (suppress OK messages)
--volume PATH Check specific volume only

Categories:

Category What It Checks
config Config file exists and valid, volume sources usable (the same check as config validate), targets reachable, compression programs available
snapshots Orphaned snapshots, missing snapshots, broken parent chains
transfers Stale locks (dead processes), incomplete transfers, recent failures in transaction log
system Destination space, quota limits, systemd timer status, backup age

Exit Codes:

Code Meaning
0 Healthy (all OK or INFO findings)
1 Warnings present
2 Errors or critical issues

Examples:

# Full diagnostics
btrfs-backup-ng doctor

# JSON output for monitoring integration
btrfs-backup-ng doctor --json

# Check only configuration
btrfs-backup-ng doctor --check config

# Check only snapshot health
btrfs-backup-ng doctor --check snapshots

# Auto-fix safe issues
btrfs-backup-ng doctor --fix

# Interactive fix (confirm each)
btrfs-backup-ng doctor --fix --interactive

# Only show problems
btrfs-backup-ng doctor --quiet

# Check specific volume
btrfs-backup-ng doctor --volume /home

Example Output:

btrfs-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

=======================================================
Summary: 7 passed, 2 warnings, 0 errors
Fixable: 1 issue (run with --fix)

JSON Output:

{
  "timestamp": "2026-01-06T10:30:00Z",
  "duration_seconds": 2.5,
  "summary": {"ok": 7, "warnings": 2, "errors": 0, "fixable": 1},
  "findings": [
    {
      "category": "transfers",
      "severity": "warn",
      "check": "stale_locks",
      "message": "Stale lock detected",
      "details": {"snapshot": "home-20260110", "pid": 12345},
      "fixable": true,
      "fix_description": "Remove stale lock file"
    }
  ]
}

transfers (Experimental)

Note: The chunked transfer feature is experimental. It provides resumable transfers for large snapshots but has not been extensively tested in production environments.

Manage chunked and resumable transfers. This command allows you to list, inspect, resume, pause, and clean up incomplete transfers.

btrfs-backup-ng transfers [SUBCOMMAND] [OPTIONS]

transfers list

List all incomplete transfer sessions.

btrfs-backup-ng transfers list [OPTIONS]

Options:

Option Description
--json Output in JSON format
--all Include completed and failed transfers

Example Output:

Incomplete Transfers
======================================================================
ID         Status       Snapshot                  Progress     Age
----------------------------------------------------------------------
abc123     paused       home-20260108             5/10 (50%)   2h ago
def456     failed       root-20260109             3/8 (37%)    1d ago
----------------------------------------------------------------------
Total: 2 incomplete transfer(s)

transfers show

Show detailed information about a specific transfer.

btrfs-backup-ng transfers show TRANSFER_ID [OPTIONS]

Options:

Option Description
--json Output in JSON format

Example:

btrfs-backup-ng transfers show abc123

transfers resume

Resume a paused or failed transfer.

btrfs-backup-ng transfers resume TRANSFER_ID [OPTIONS]

Options:

Option Description
--dry-run Show what would be done without resuming
--rate-limit RATE Bandwidth limit (e.g., '10M', '1G')
--progress Show progress bars
--no-progress Disable progress bars

Example:

btrfs-backup-ng transfers resume abc123

transfers pause

Pause an active transfer.

btrfs-backup-ng transfers pause TRANSFER_ID

transfers cleanup

Clean up stale, completed, or failed transfers.

btrfs-backup-ng transfers cleanup [OPTIONS]

Options:

Option Description
--dry-run Show what would be cleaned without making changes
--force Remove even active transfers
--older-than DURATION Only cleanup transfers older than duration (e.g., '7d', '24h')
TRANSFER_ID Clean up a specific transfer

Examples:

# See what would be cleaned
btrfs-backup-ng transfers cleanup --dry-run

# Clean up stale transfers
btrfs-backup-ng transfers cleanup

# Clean up a specific transfer
btrfs-backup-ng transfers cleanup abc123

# Force cleanup of all transfers older than 7 days
btrfs-backup-ng transfers cleanup --older-than 7d --force

transfers operations

List backup operations and their status.

btrfs-backup-ng transfers operations [OPTIONS]

Options:

Option Description
--json Output in JSON format
--all Include archived operations

snapper

Manage snapper-managed snapshots. This command provides integration with snapper for discovering, backing up, and restoring snapper snapshots.

btrfs-backup-ng snapper SUBCOMMAND [OPTIONS]

snapper detect

Detect snapper configurations on the system.

btrfs-backup-ng snapper detect [OPTIONS]

Options:

Option Description
--json Output in JSON format

Example Output:

Found 2 snapper configuration(s):

  root:
    Subvolume:     /
    Snapshots dir: /.snapshots
    Status:        OK

  home:
    Subvolume:     /home
    Snapshots dir: /home/.snapshots
    Status:        OK

snapper list

List snapshots for snapper configurations.

btrfs-backup-ng snapper list [OPTIONS]

Options:

Option Description
--config NAME List snapshots for specific config only
--type TYPE Filter by snapshot type (single, pre, post)
--json Output in JSON format

Examples:

# List all snapshots
btrfs-backup-ng snapper list

# List only 'root' config snapshots
btrfs-backup-ng snapper list root

# List only 'single' type snapshots
btrfs-backup-ng snapper list --type single

snapper backup

Backup snapper snapshots to a destination.

btrfs-backup-ng snapper backup CONFIG TARGET [OPTIONS]

Arguments:

Argument Description
CONFIG Snapper configuration name (e.g., 'root', 'home')
TARGET Destination: a local path or ssh://user@host:/path for btrfs destinations, or a raw target (raw:///path, raw+ssh://user@host/path) for non-btrfs destinations. See Raw Targets. The target must exist; the .snapshots tree is created below it.

Options:

Option Description
--dry-run Show what would be done without making changes
--snapshot NUM Backup specific snapshot by number
--type TYPE Filter by snapshot type (can be repeated)
--min-age DURATION Only backup snapshots older than duration (e.g., '1h')
--compress METHOD Compression method (none, zstd, gzip, lz4)
--rate-limit RATE Bandwidth limit (e.g., '10M', '1G')
--ssh-sudo Use sudo on remote host
--ssh-key FILE SSH private key file
--ssh-auth-sock PATH Explicit ssh-agent socket (overrides auto-discovery)
--progress Show progress bars
--no-progress Disable progress bars

Examples:

# Backup all snapshots for 'root' config to local path
btrfs-backup-ng snapper backup root /mnt/backup/root

# Backup to remote server
btrfs-backup-ng snapper backup root ssh://backup@server:/backups/root

# Backup only single-type snapshots with compression
btrfs-backup-ng snapper backup root /mnt/backup --type single --compress zstd

# Backup snapshots older than 1 hour
btrfs-backup-ng snapper backup root /mnt/backup --min-age 1h

# Backup to a raw target on a non-btrfs destination (USB, NFS, SMB)
btrfs-backup-ng snapper backup root raw:///mnt/usb/backups/root --compress zstd

# Backup to a remote raw target over SSH
btrfs-backup-ng snapper backup root raw+ssh://backup@server/mnt/nas/root --compress zstd

# Dry run to see what would be backed up
btrfs-backup-ng snapper backup root /mnt/backup --dry-run

Backup Directory Layout:

Backups use snapper's native directory layout:

/mnt/backup/root/.snapshots/
├── 559/
│   ├── info.xml
│   └── snapshot/     # btrfs subvolume
├── 560/
│   ├── info.xml
│   └── snapshot/
└── 561/
    ├── info.xml
    └── snapshot/

snapper status

Show backup sync status between source and destination.

btrfs-backup-ng snapper status [OPTIONS]

Options:

Option Description
--config NAME Check specific config only
--target PATH Check backup status at target
--json Output in JSON format

Examples:

# Show local snapshot counts
btrfs-backup-ng snapper status

# Show backup status for specific target
btrfs-backup-ng snapper status --target /mnt/backup/root

snapper restore

Restore snapper snapshots from backup.

btrfs-backup-ng snapper restore SOURCE [CONFIG] [OPTIONS]

Arguments (positional, in this order):

Argument Description
SOURCE Backup source: a local path, ssh://user@host:/path (btrfs), or raw://path / raw+ssh://user@host/path (non-btrfs)
CONFIG Local snapper config to restore into (e.g. root, home). Required to restore; optional with --list (listing only reads the source)

Options:

Option Description
-l, --list List available backups instead of restoring
-s, --snapshot NUM Restore snapshot by number (can be repeated). On a reused (duplicate) number, restores the newest by date and warns
--backup-name NAME Restore the exact raw backup by name from --list (can be repeated); reaches an older copy when a snapper number was reused
--date DATE Restrict the selection to backups whose date matches DATE (YYYY-MM-DD[ HH:MM:SS] prefix; an ISO-8601 T separator is accepted); disambiguates a reused --snapshot NUM
-a, --all Restore all backups
--from-config NAME Only restore backups that were made from this snapper config (overrides the target CONFIG for matching)
--dry-run Show what would be done without making changes
--ssh-sudo Use sudo on the remote host (ssh:// / raw+ssh://); required to read a root-owned target written with --ssh-sudo
--ssh-key FILE SSH private key file
--ssh-auth-sock PATH Explicit ssh-agent socket (overrides auto-discovery; useful under sudo)
--ssh-host-key-policy {accept-new,strict} Host-key verification policy (default accept-new)
--skip-remote-lock Proceed when the pin on the backup cannot be recorded on its location (a remote lock directory or a local lock file); for a location you can read but not write. A medium mounted read-only needs it not: no pin is asked for there
--gpg-keyring PATH GPG keyring to decrypt an encrypted raw snapper backup (must match the keyring it was encrypted for)
--openssl-cipher CIPHER OpenSSL cipher fallback for a legacy raw backup whose .meta sidecar does not record one (modern sidecars are authoritative)
--json Output in JSON format (for --list)

Examples:

# List available backups
btrfs-backup-ng snapper restore /mnt/backup/root --list

# Restore specific snapshot
btrfs-backup-ng snapper restore /mnt/backup/root root --snapshot 559

# Restore multiple snapshots
btrfs-backup-ng snapper restore /mnt/backup/root root --snapshot 559 --snapshot 560

# Restore all snapshots
btrfs-backup-ng snapper restore /mnt/backup/root root --all

# Dry run
btrfs-backup-ng snapper restore /mnt/backup/root root --all --dry-run

# Restore a root-owned raw+ssh:// backup (written with --ssh-sudo): pass it again
btrfs-backup-ng snapper restore raw+ssh://user@host/backups/root root --snapshot 559 --ssh-sudo

# Reused snapper number: restore an OLDER copy by exact name (from --list) ...
btrfs-backup-ng snapper restore /mnt/backup/root root --backup-name root-559-20240101-120000

# ... or by number + date
btrfs-backup-ng snapper restore /mnt/backup/root root --snapshot 559 --date 2024-01-01

Note — snapper daemon cache. A restored snapshot is written directly into .snapshots/{N}/ (not via snapper create), so snapperd does not see it until it reloads. Restart it (sudo systemctl restart snapperd, or reboot) after a restore before using snapper diff, snapper undochange, or a rollback against the restored snapshot; the restore command prints a reminder. The on-disk snapshot is complete and correct — this only affects the daemon's in-memory view. Restored slots use snapper's native 0755 permissions on .snapshots/{N}/.

Note — duplicate snapper numbers. Snapper reuses snapshot numbers after a prune, so a raw:// / raw+ssh:// target can accumulate two retained backups with the same number (they differ by the date in the backup name). --snapshot N restores the newest of them by date and warns. To reach an older copy, use --backup-name NAME (the unique name is shown by --list, and in --list --json as backup_name) or narrow --snapshot N with --date YYYY-MM-DD. --list shows a NAME column whenever names are available.

snapper generate-config

Generate TOML configuration for snapper-managed volumes.

btrfs-backup-ng snapper generate-config [OPTIONS]

Options:

Option Description
--config NAME Generate for specific config(s) (can be repeated)
--target PATH Include target in generated config
--type TYPE Snapshot types to include (default: single)
--min-age DURATION Minimum snapshot age (default: 1h)
--ssh-sudo Enable ssh_sudo in target config
-o, --output FILE Write to file instead of stdout
--append FILE Append to existing config file
--json Output in JSON format

Examples:

# Generate config for all snapper volumes
btrfs-backup-ng snapper generate-config

# Generate for specific config with target
btrfs-backup-ng snapper generate-config root --target ssh://backup@server:/backups

# Write to file
btrfs-backup-ng snapper generate-config -o ~/.config/btrfs-backup-ng/snapper.toml

# Append to existing config
btrfs-backup-ng snapper generate-config --append ~/.config/btrfs-backup-ng/config.toml

Example Output:

# Snapper volume configuration
# Generated by: btrfs-backup-ng snapper generate-config

[[volumes]]
path = "/"
source = "snapper"

[volumes.snapper]
config_name = "root"
include_types = ["single"]
min_age = "1h"

[[volumes.targets]]
path = "ssh://backup@server:/backups/root"
ssh_sudo = true

raw

Inspect and maintain raw-target backups directly. Raw targets hold btrfs send streams as files, each with an authoritative .meta sidecar.

btrfs-backup-ng raw SUBCOMMAND [OPTIONS]

Mutating operations on a local raw:// target (a backup's commit, prune, backfill-metadata, and encrypt) hold an exclusive per-target lock so they cannot race each other; concurrent operations serialize rather than corrupt one another. The lock is an empty .btrfs-backup-ng.lock file kept in the target directory — it is created 0600, is safe to leave in place, and is never listed as a backup. Because the lock lives inside the target directory, that directory must not be writable by untrusted users. raw+ssh:// targets are serialized too. The lock is a directory under <target>/.btrfs-backup-ng.locks/, created with mkdir — atomic, so exactly one of any number of racing creators wins — and kept alive by a heartbeat, so a holder that dies is broken by the next contender rather than blocking the target until someone cleans up by hand. It holds across processes and across machines. See Remote Locks.

raw list

List the backups a raw target holds, read from each stream's .meta sidecar (falling back to filename inference for legacy streams written before sidecars existed).

btrfs-backup-ng raw list TARGET [OPTIONS]

Arguments:

Argument Description
TARGET Raw target: raw://PATH, raw+ssh://[USER@]HOST/PATH, or a plain path (treated as raw://)

Options:

Option Description
--json Output the authoritative sidecar documents as JSON for scripting
--ssh-sudo Use sudo for remote commands on a raw+ssh:// target

Example:

btrfs-backup-ng raw list raw:///mnt/usb/backups
btrfs-backup-ng raw list raw+ssh://backup@nas/backups --ssh-sudo

Example Output:

Raw target: raw:///mnt/usb/backups  (2 snapshots)
  NAME                             CREATED               SIZE  ENC         COMPRESS CIPHER       ORIGIN
  root.20260101T120000             2026-01-01T12:00:00Z  1.2 GiB  openssl_enc zstd     aes-256-cbc  native-write
  home.20260101T120000             2026-01-01T12:00:00Z  340 MiB  -           -        -            native-write

raw verify

Verify raw backups by recomputing each stream's sha256 and comparing it to the checksum recorded in its .meta sidecar. For a raw+ssh:// target the hash is computed on the remote host, so streams are not re-downloaded.

btrfs-backup-ng raw verify TARGET [OPTIONS]

Arguments:

Argument Description
TARGET Raw target: raw://PATH, raw+ssh://[USER@]HOST/PATH, or a plain path

Options:

Option Description
--snapshot NAME Verify only the named snapshot
--json Output per-snapshot results as JSON
--ssh-sudo Use sudo for remote commands on a raw+ssh:// target

Per-snapshot status: ok (matches), corrupt (the stored stream differs from what was backed up), error (the stream could not be read/hashed), or unverifiable (no checksum was recorded — a legacy backup, or a best-effort write-time seal that failed). Exit status is 1 if any snapshot is corrupt or errored, else 0.

raw verify confirms a stream still matches the checksum recorded in its own .meta sidecar — an integrity/consistency check, not an authenticity one: an attacker who can rewrite both the stream and its sidecar can make a corrupted backup report ok. For a raw+ssh:// target the hash is recomputed on the (untrusted) remote host, so an ok verdict only confirms the recorded checksum and cannot detect corruption or tampering introduced by a compromised target. For tamper-evident verification, verify a locally-mounted copy (raw://), where the stream is hashed with your own host's kernel after dropping its page cache.

Example Output:

Raw target: raw:///mnt/usb/backups  (verifying 2 snapshots)
  OK            root.20260101T120000
  CORRUPT       home.20260101T120000
  1 ok, 1 corrupt, 0 error, 0 unverifiable

raw backfill-metadata

Write authoritative .meta sidecars for legacy raw streams that have none — backups written before sidecars existed (or by another tool such as btrbk). New backups already write their sidecar automatically; this is only for pre-existing streams.

btrfs-backup-ng raw backfill-metadata TARGET [OPTIONS]

Arguments:

Argument Description
TARGET Raw target: raw://PATH, raw+ssh://[USER@]HOST/PATH, or a plain path

Options:

Option Description
--dry-run Show which streams would be backfilled without writing anything
--json Output per-stream results as JSON
--ssh-sudo Use sudo for remote commands on a raw+ssh:// target

Each backfilled sidecar records the pipeline inferred from the filename (compression/encryption) and a sha256 of the stream as it exists now, and is stamped provenance_origin=backfill with stream_completeness=unknown. A legacy stream's completeness cannot be proven (it might have been truncated), so a backfilled sidecar is marked as a reconstructed, non-authoritative record — the checksum lets raw verify detect later corruption, but does not confirm the original backup was complete. Exit status is 1 if any sidecar write failed, else 0.

On a local raw:// target, backfill-metadata holds the per-target lock while it works, so a concurrent backup or prune serializes against it; it also re-checks for a sidecar immediately before writing, so a concurrently-committed backup is never overwritten. A raw+ssh:// target takes the same lock, recorded on the remote target itself, so a concurrent backup or prune — from another process, or another machine — serializes against it rather than racing it.

Example Output:

Raw target: raw:///mnt/usb/backups  (2 legacy streams without a sidecar)
  BACKFILLED      root.20240115T120000
  BACKFILLED      home.20240115T120000
  2 backfilled, 0 error
  Note: backfilled sidecars are marked stream_completeness=unknown -- a legacy stream cannot be proven complete.

raw encrypt

Encrypt raw streams that were written as plaintext — remediation for backups affected by GHSA-vr25-6vrh-869j, where an encrypt= config on a raw target could silently write plaintext (fixed in 0.8.4). Runs locally only so the passphrase/keys never leave the host; for a remote target, mount it and use a raw:// path.

btrfs-backup-ng raw encrypt TARGET --encrypt {gpg|openssl_enc} [OPTIONS]

Options:

Option Description
--encrypt {gpg,openssl_enc} Encryption method to apply (required)
--gpg-recipient KEYID GPG recipient (required with --encrypt gpg)
--openssl-cipher CIPHER OpenSSL cipher for openssl_enc (default aes-256-cbc)
--shred Remove each plaintext file after a verified decrypt proof
--yes Do not prompt before removing plaintext (for scripts; with --shred)
--dry-run Show which plaintext streams would be encrypted, without changes
--json Per-stream results as JSON

For each plaintext stream, raw encrypt writes an encrypted copy alongside it (a new authoritative sidecar with provenance.origin=remediation and remediated_from), then runs a live decrypt-to-identical proof — it decrypts the new stream and confirms it is byte-identical to the plaintext.

The plaintext is removed only when that proof passes and --shred was given (by default the plaintext is kept). For gpg, the proof needs the secret key on this host; if it is absent, the encrypted copy is written but the plaintext is kept.

Erasure caveat. --shred removes the plaintext with a plain unlink. On copy-on-write filesystems (btrfs) and on SSDs, this does not securely erase the underlying blocks (btrfs writes new blocks; SSD controllers remap them). True physical erasure relies on device-level trim/discard or full-disk encryption at rest, which this tool cannot provide — and prior exposure (older backup media, indexes) cannot be undone.

Example:

# encrypt plaintext backups with openssl (BTRFS_BACKUP_PASSPHRASE set), keep plaintext:
btrfs-backup-ng raw encrypt raw:///mnt/usb/backups --encrypt openssl_enc
# ...then remove the plaintext after the verified proof:
btrfs-backup-ng raw encrypt raw:///mnt/usb/backups --encrypt openssl_enc --shred

Paths That Must Exist, and Paths That Are Created

Stated by what a path is, not by where it was typed, so there are no exceptions. A backup location (a source, a target on any transport, an absolute snapshot base -- from the configuration file, from snapper backup TARGET, or from legacy mode's arguments) is a statement that something is there and is never created. An output location (where a command was asked to put something new) is created. The program's own state is created. Anything below a location that exists is created one level at a time, never the location itself.

MUST EXIST (refused, nothing created)      CREATED ON REQUEST (output locations)
  [[volumes]] path                           restore DESTINATION / --to
  [[volumes.targets]] path  (all schemes)    verify --temp-dir
  snapshot_dir when absolute                 config init -o, completions install,
  snapper backup TARGET                        manpages install
  legacy SOURCE, DESTINATION, absolute -f

CREATED BELOW AN EXISTING LOCATION, ONE LEVEL AT A TIME
  <target>/.btrfs-backup-ng/snapshots        the target's own bookkeeping
  <volume>/<snapshot_dir>                    a RELATIVE snapshot_dir
  <snapshot_dir>/<volume name>               under an ABSOLUTE snapshot_dir
  <target>/.snapshots/<n>                    a snapper backup's slots
  <target>/.btrfs-backup-ng.locks            locks on a remote target

The refusal names the path and the remedy. Create a missing directory once by hand (mkdir -p PATH, or ssh HOST 'mkdir -p PATH' for a remote target), or mount the filesystem that should hold it, and run again.


Filesystem Checks

The --fs-checks option controls how btrfs-backup-ng validates source and destination paths:

Mode Behavior
auto (default) Warn about issues but continue operation
strict Error out on any check failure
skip Bypass all filesystem checks

The --no-fs-checks flag is an alias for --fs-checks=skip.

Examples:

# Default auto mode - warns but continues
btrfs-backup-ng run

# Strict mode for production
btrfs-backup-ng run --fs-checks=strict

# Skip checks for backup directories
btrfs-backup-ng restore --list /mnt/backup --no-fs-checks

Legacy Mode

When the first argument is a path (not a subcommand), btrfs-backup-ng runs in legacy mode for backwards compatibility. The source, the destination and an absolute -f/--snapshot-folder are backup locations: they must exist and are not created; a relative folder is created under the source. See Paths That Must Exist, and Paths That Are Created.

btrfs-backup-ng [OPTIONS] SOURCE DESTINATION

Legacy Options:

Option Description
-n, --num-snapshots N Number of snapshots to keep
-f, --snapshot-folder DIR Folder the snapshots are taken into: relative to the source, or absolute (must exist). Default .snapshots inside the source
--accept-full-send Start a new chain in --snapshot-folder although <source>/.snapshots already holds one, accepting one full send to every destination. Needed once at most; see below
--no-incremental Disable incremental transfers
--ssh-sudo Use sudo on SSH remote
--ssh-username USER SSH username
--ssh-identity-file FILE SSH private key
--convert-rw Convert to read-write before deletion
--fs-checks MODE Filesystem check mode: auto, strict, skip
--no-fs-checks Skip filesystem checks (alias for --fs-checks=skip)
--no-check-space Disable pre-flight space checking
--force Proceed despite insufficient space warnings
--safety-margin PERCENT Safety margin for space checks (default: 10%)

Examples:

# Basic local backup
btrfs-backup-ng /home /mnt/backup/home

# Remote backup with SSH
btrfs-backup-ng /home ssh://backup@server:/backups/home

# With options
btrfs-backup-ng --ssh-sudo --num-snapshots 10 /home ssh://user@host:/backup

Until this release -f/--snapshot-folder had no effect on placement: every legacy-mode snapshot went to <source>/.snapshots. That is now the default, so a run that never passed -f sees no change. A run that did pass -f, and whose chain is therefore in <source>/.snapshots while the named folder is empty, is refused rather than started as a new chain (which would make the next transfer a full send). The refusal lists the remedies: move the chain into the folder (mv <source>/.snapshots/<prefix>* <folder>/, same filesystem), pass -f <source>/.snapshots to keep it where it is, or add --accept-full-send once.


SSH Remote Backups

When backing up to a remote host via SSH, there are several authentication scenarios.

SSH Authentication

Key-based authentication (recommended):

For key-based SSH authentication, load your SSH key into ssh-agent (ssh-add). Backups run as root (btrfs send/receive need it), and sudo strips SSH_AUTH_SOCK — but a passphrase-protected key can only sign via its agent. btrfs-backup-ng therefore auto-discovers your ssh-agent socket across common locations (including ~/.ssh/agent/, /tmp/ssh-*, /run/user/<uid>/keyring/ssh, gpg/gcr agents) and uses it, preferring a socket that actually has keys loaded. In most setups a plain sudo btrfs-backup-ng run just works.

If your agent lives somewhere unusual, or you run several agents, pin the socket explicitly (highest precedence, then a preserved SSH_AUTH_SOCK, then auto-discovery):

# 1. Explicit socket in the target config:
#    [[volumes.targets]]
#    ssh_auth_sock = "/path/to/agent.sock"
#
# 2. Environment override (works for every command):
export BTRFS_BACKUP_SSH_AUTH_SOCK="$SSH_AUTH_SOCK"
sudo --preserve-env=BTRFS_BACKUP_SSH_AUTH_SOCK btrfs-backup-ng run
#
# 3. Preserve your existing agent socket through sudo:
sudo --preserve-env=SSH_AUTH_SOCK btrfs-backup-ng run   # or: sudo -E ...
#
# 4. On one-off commands: restore/verify/estimate/snapper accept --ssh-auth-sock PATH

The tool also detects SSH keys in ~/.ssh/ (id_ed25519, id_rsa, id_ecdsa) for the original user when running via sudo. If authentication fails, the error message names the exact remediation for your situation. A passphrase-less key avoids the agent requirement entirely.

Password authentication:

Set ssh_password_auth = true in your target configuration to enable interactive SSH password prompts:

[[volumes.targets]]
path = "ssh://user@host:/backup"
ssh_password_auth = true

Sudo on Remote Host

btrfs commands require root privileges. For remote backups, configure sudo access on the remote host.

Passwordless sudo (recommended for automated backups):

Add this to /etc/sudoers.d/btrfs-backup on the remote host:

# Allow btrfs commands without password for backup user
username ALL=(ALL) NOPASSWD: /usr/bin/btrfs

Then enable sudo in your target configuration:

[[volumes.targets]]
path = "ssh://user@host:/backup"
ssh_sudo = true

Password-based sudo:

If passwordless sudo is not configured, the tool will prompt for the sudo password interactively, or you can set it via environment variable:

export BTRFS_BACKUP_SUDO_PASSWORD="your-password"
btrfs-backup-ng run

Complete Example for Unattended Backups

For fully automated backups without any password prompts:

  1. Local machine: Run with sudo -E to preserve SSH agent
  2. Remote machine: Configure passwordless sudo for btrfs
# On remote host, create /etc/sudoers.d/btrfs-backup:
# myuser ALL=(ALL) NOPASSWD: /usr/bin/btrfs

# On local machine, run backup:
sudo -E btrfs-backup-ng run

Configuration:

[[volumes.targets]]
path = "ssh://myuser@backupserver:/mnt/backups/myvolume"
ssh_sudo = true

SSH Security

btrfs-backup-ng verifies the remote host's SSH key on every connection.

Host-key policy

Policy Behavior
accept-new (default) Trust a host's key on first contact and pin it; refuse to connect if that key later changes (the usual man-in-the-middle signal).
strict Only connect to hosts already present in known_hosts; an unknown host is refused.

Set it in a target's config or per run:

[[volumes.targets]]
path = "ssh://backup@server:/mnt/backups/vol"
ssh_host_key_policy = "strict"     # or "accept-new" (default)
btrfs-backup-ng restore --ssh-host-key-policy strict ...

An unrecognized value is a configuration error (fails closed — it never silently downgrades).

Under sudo

When running under sudo, host keys are verified and pinned against the invoking operator's ~/.ssh/known_hosts (not root's), so the trust you established interactively still applies. Operator ssh_opts (for example an explicit StrictHostKeyChecking) take precedence over the tool's default.

Predictable-path hardening

SSH ControlMaster sockets are created in an unpredictable, private (0700) directory (preferring $XDG_RUNTIME_DIR) and removed when the connection closes, so a local user cannot hijack the multiplexed connection to your backup host. The internal command lock and the raw+ssh remote metadata write likewise avoid predictable, symlink-plantable paths.

The command lock (btrfs-backup-ng-<uid>/command.lock under $XDG_RUNTIME_DIR or the temp directory) is left in place after a run, deliberately. It is an flock, which excludes by inode: removing the file while another process waits on it would let that process and a newcomer each hold a lock on a different file at the same path, and both would proceed.

Upgrade note: raw+ssh:// targets now set an explicit accept-new policy (previously they inherited the ambient SSH default, which under batch mode accidentally refused unknown hosts). If you relied on that refuse-unknown behavior, set ssh_host_key_policy = "strict".


Environment Variables

Variable Description
BTRFS_BACKUP_SUDO_PASSWORD Sudo password for remote hosts (avoids interactive prompt)
BTRFS_BACKUP_SSH_PASSWORD Enable SSH password authentication
BTRFS_BACKUP_PASSWORDLESS_ONLY Only use passwordless sudo (fail if password required)
BTRFS_BACKUP_LOG_LEVEL Console log level when nothing else says (DEBUG, INFO, WARNING, ERROR). The lowest-precedence source: --debug/--btrfs-debug/-q/-v win over it, and [global] quiet/verbose/btrfs_debug in the configuration win over it too. Any other value is ignored, with a warning.
SSH_AUTH_SOCK SSH agent socket (preserve with sudo -E)

Exit Codes

Code Description
0 Success
1 Error (configuration, runtime, or transfer failure)

Configuration File Locations

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 and none is specified, commands that require configuration will display an error.

Setup Examples

User configuration:

mkdir -p ~/.config/btrfs-backup-ng
btrfs-backup-ng config init > ~/.config/btrfs-backup-ng/config.toml

System-wide configuration:

sudo mkdir -p /etc/btrfs-backup-ng
btrfs-backup-ng config init | sudo tee /etc/btrfs-backup-ng/config.toml
sudo chmod 600 /etc/btrfs-backup-ng/config.toml

Check which config is active:

btrfs-backup-ng config validate
# Shows: Validating: /path/to/active/config.toml

Compression Methods

Method Description Speed Ratio
none No compression Fastest 1:1
lz4 Very fast compression Very Fast Low
lzop Fast compression Fast Low
pigz Parallel gzip Fast Medium
gzip Standard compression Medium Medium
zstd Modern compression (recommended) Medium High

Raw Targets

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.

URL Schemes

Scheme Description
raw:///path Local raw target
raw+ssh://user@host/path Remote raw target via SSH

Configuration 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)

Example Configuration

[[volumes]]
path = "/home"

# Raw target with compression and GPG encryption
[[volumes.targets]]
path = "raw:///mnt/nas/backups/home"
compress = "zstd"
encrypt = "gpg"
gpg_recipient = "backup@example.com"

# Raw target over SSH
[[volumes.targets]]
path = "raw+ssh://backup@server/backups/home"
compress = "zstd"

Encryption Methods

GPG (public-key, recommended):

[[volumes.targets]]
path = "raw:///mnt/backup"
encrypt = "gpg"
gpg_recipient = "backup@example.com"

OpenSSL (symmetric, for btrbk migration):

[[volumes.targets]]
path = "raw:///mnt/backup"
encrypt = "openssl_enc"

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

Raw backups create predictable filenames:

snapshot-name.btrfs                    # No compression/encryption
snapshot-name.btrfs.zst                # Compressed with zstd
snapshot-name.btrfs.gpg                # GPG encrypted
snapshot-name.btrfs.zst.gpg            # Compressed + GPG encrypted
snapshot-name.btrfs.enc                # OpenSSL encrypted
snapshot-name.btrfs.zst.enc            # Compressed + OpenSSL encrypted

Each stream file has a companion .meta file with JSON metadata for incremental chain tracking.


Rate Limit Format

Bandwidth limits use a number with optional suffix:

Suffix Multiplier
(none) Bytes per second
K Kilobytes per second (1024)
M Megabytes per second (1024^2)
G Gigabytes per second (1024^3)

Examples: 500K, 10M, 1G, 52428800 (50MB in bytes)


Duration Format

Retention durations use a number with suffix:

Suffix Duration
m Minutes
h Hours
d Days
w Weeks

Examples: 30m, 6h, 1d, 2w