BTRFS-BACKUP-NG-RAW(1)

πŸ“¦ version 0.9.20, in fedora 43, fedora 44

🌸 btrfs-backup-ng-raw 🌸

okay so bestie, welcome to BTRFS-BACKUP-NG-RAW(1) β€” the part of the manual that's all about poking at raw backup targets directly. no job config, no middleman, just you and the target directory being besties 🫢 let's get into it.


πŸ’– NAME πŸ’–

btrfs-backup-ng-raw β€” inspect and maintain raw-target backups. that's the whole vibe, literally πŸ’…


✨ SYNOPSIS ✨

btrfs-backup-ng raw list TARGET [--json] [--ssh-sudo]
btrfs-backup-ng raw verify TARGET [--snapshot NAME] [--json] [--ssh-sudo]
btrfs-backup-ng raw backfill-metadata TARGET [--dry-run] [--json] [--ssh-sudo]
btrfs-backup-ng raw encrypt TARGET --encrypt METHOD [--shred] [OPTIONS]

πŸ¦‹ DESCRIPTION πŸ¦‹

bestie, this thing operates directly on a raw backup target. raw targets β€” that's raw:// and raw+ssh:// β€” store btrfs send streams as files, which is the whole point: you're backing up onto non-btrfs storage (NFS, SMB, cloud, another OS, you name it). every single stream gets written next to an authoritative .meta sidecar that records its pipeline (compression, encryption, cipher), its size, its provenance, and its creation time. no cap, it's all written down πŸ“

these subcommands read and maintain such a target directly, independently of any configured backup job. like, you don't need a job to exist. iconic.

πŸ”’ the lock situation (local targets)

on a local raw:// target, the mutating operations β€” a backup's commit, prune, backfill-metadata, and encrypt β€” hold an exclusive per-target lock so they serialize instead of racing each other. the lock is an empty .btrfs-backup-ng.lock file sitting in the target directory, created with mode 0600. it's safe to just leave it there, and it's never listed as a backup. periodt.

⚠️ because the lock lives inside the target directory, that directory must not be writable by untrusted users. like, seriously. that's not a vibe, that's a rule πŸ’€

remote raw+ssh:// maintenance is not yet lock-protected. noted.


πŸŽ€ SUBCOMMANDS πŸŽ€

πŸ“‹ list

list the backups a raw target holds. inventory check, but make it fashion ✨

btrfs-backup-ng raw list TARGET [--json] [--ssh-sudo]

it enumerates the target's backups from each stream's .meta sidecar, and falls back to filename inference for legacy streams written before sidecars even existed (yes, ancient artifacts, we love them). the human-readable output lists each backup's name, creation time, size, and pipeline β€” that's encryption method, compression, openssl cipher, and metadata origin. all of it, bestie.

  • TARGET β€” the raw target you want to inspect. could be raw://PATH (local), raw+ssh://[USER@]HOST/PATH (remote over SSH), or just a plain path (which gets treated as raw://).
  • --json β€” outputs the authoritative sidecar documents as JSON, for when you're scripting and need machine-readable everything πŸ–₯️
  • --ssh-sudo β€” use sudo for the remote commands on a raw+ssh:// target.

πŸ” verify

verify raw backups against their recorded checksums. the trust-but-verify era πŸ•΅οΈ

btrfs-backup-ng raw verify TARGET [--snapshot NAME] [--json] [--ssh-sudo]

it recomputes each stream's sha256 and compares it to the value recorded in its .meta sidecar. for a raw+ssh:// target the hash is computed on the remote host, so your streams don't get re-downloaded (we're not doing that today, it's giving bandwidth crime). each backup gets reported as one of:

  • ok β€” it matches
  • corrupt β€” the stored stream differs from what was backed up
  • error β€” the stream could not be read or hashed
  • unverifiable β€” no checksum was recorded. that's a legacy backup, or a best-effort write-time seal that failed

real talk though: verify confirms a stream still matches the checksum in its own sidecar β€” that's an integrity/consistency check, not authenticity. an attacker who can rewrite both the stream and its sidecar can make a corrupted backup report ok. 😭 and 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://). that's the move.

  • --snapshot NAME β€” verify only the named snapshot. one and only one, main character πŸ’…
  • --json β€” output per-snapshot results in JSON format.
  • --ssh-sudo β€” use sudo for the remote commands on a raw+ssh:// target.

🧚 backfill-metadata

write authoritative .meta sidecars for LEGACY streams that have none. it's a glow-up for old files πŸ¦‹

btrfs-backup-ng raw backfill-metadata TARGET [--dry-run] [--json] [--ssh-sudo]

this is for 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. each backfilled sidecar records the pipeline inferred from the filename and a sha256 of the stream as it exists now, and it gets stamped provenance_origin=backfill with stream_completeness=unknown. because, bestie, a legacy stream's completeness cannot be proven β€” so a backfilled sidecar is a reconstructed, non-authoritative record. the checksum lets raw verify detect later corruption, not confirm the original backup was complete. 🫠

on a local raw:// target this holds the per-target lock, so a concurrent backup or prune serializes against it. on a raw+ssh:// target the lock is a no-op (remote locking is not yet implemented) and the command warns you β€” so run remote maintenance while the target is idle, okay?

  • --dry-run β€” show which streams would be backfilled without writing anything. a little preview, a little peek πŸ‘€
  • --json β€” output per-stream results in JSON format.
  • --ssh-sudo β€” use sudo for the remote commands on a raw+ssh:// target.

πŸ” encrypt

encrypt raw streams that were written as plaintext β€” this is the remediation for GHSA-vr25-6vrh-869j.

btrfs-backup-ng raw encrypt TARGET --encrypt METHOD [--gpg-recipient KEYID] [--openssl-cipher CIPHER] [--shred] [--yes] [--dry-run] [--json]

it runs LOCALLY only, so the passphrase/keys never leave this host β€” for a remote target, mount it and use a raw:// path. for each plaintext stream, it writes an encrypted copy alongside it (new sidecar, provenance.origin=remediation), then runs a LIVE decrypt-to-identical proof: it decrypts the new stream and confirms it's byte-identical to the plaintext. the plaintext is removed ONLY when that proof passes AND --shred was given; by default it's kept. for gpg the proof needs the secret key on this host β€” if it's absent, the encrypted copy is written but the plaintext is kept. no drama, just facts.

about --shred: it removes the plaintext with a plain unlink. on copy-on-write filesystems (btrfs) and SSDs this does NOT securely erase the underlying blocks πŸ’€ true physical erasure relies on device-level trim/discard or full-disk encryption at rest, which this tool cannot provide, and prior exposure cannot be undone. so like... plan accordingly.

  • --encrypt METHOD β€” the encryption method to apply: gpg or openssl_enc. Required.
  • --gpg-recipient KEYID β€” GPG recipient key. 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 (plain unlink).
  • --yes β€” do not prompt before removing plaintext. for scripts; used with --shred.
  • --dry-run β€” show which plaintext streams would be encrypted, without changes.
  • --json β€” output per-stream results in JSON format.
  • --gpg-keyring FILE β€” use a non-default GPG keyring for encrypt and the decrypt proof.

πŸ”₯ EXIT STATUS πŸ”₯

the numbers actually mean stuff, bestie, don't sleep on them:

  • 0 β€” success. for list, this includes an empty or nonexistent target (a warning is printed for a missing local path). for verify, all backups are ok or unverifiable. for backfill-metadata, all candidate sidecars were written (or none were needed). πŸŽ‰
  • 1 β€” for list, the target could not be listed. for verify, at least one backup is corrupt or error. for backfill-metadata, at least one sidecar could not be written. for encrypt, a stream could not be encrypted, or --shred was requested but a plaintext could not be safely removed (proof failed or unlink error). 😬
  • 2 β€” invalid target (for example a non-raw URL scheme), or a --snapshot name that is not present. that one's just not in the building 🚫

🌈 EXAMPLES 🌈

list a local raw target:

btrfs-backup-ng raw list raw:///mnt/usb/backups

list a plain path (treated as raw://):

btrfs-backup-ng raw list /mnt/usb/backups

list a remote raw+ssh target with sudo:

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

machine-readable output:

btrfs-backup-ng raw list raw:///mnt/usb/backups --json

verify every backup a target holds:

btrfs-backup-ng raw verify raw:///mnt/usb/backups

verify a single backup on a remote target:

btrfs-backup-ng raw verify raw+ssh://backup@nas/backups --snapshot root.20260101T120000

preview backfilling sidecars for legacy streams:

btrfs-backup-ng raw backfill-metadata raw:///mnt/usb/backups --dry-run

backfill sidecars for legacy streams:

btrfs-backup-ng raw backfill-metadata raw:///mnt/usb/backups

encrypt plaintext backups with openssl (keeping the plaintext):

btrfs-backup-ng raw encrypt raw:///mnt/usb/backups --encrypt openssl_enc

encrypt and then remove the plaintext after a verified proof:

btrfs-backup-ng raw encrypt raw:///mnt/usb/backups --encrypt openssl_enc --shred

πŸ’œ SEE ALSO πŸ’œ

btrfs-backup-ng(1), btrfs-backup-ng-restore(1), btrfs-send(8), btrfs-receive(8) β€” go read those too, they're the friend group 🫢


⭐ NOTES ⭐

a raw+ssh:// target is a POSIX-shell-remote model: btrfs-backup-ng runs sh, cat, mv, and stat over SSH on the remote host. for a non-POSIX or SMB/NFS/cloud destination, mount it locally and use a raw:// path instead. lowkey the simplest fix, iykyk ✨


and that's the whole manual, bestie πŸŽ€ this was btrfs-backup-ng-raw from btrfs-backup-ng, dated July 2026 β€” the manual page BTRFS-BACKUP-NG-RAW(1), signing off. go forth and verify your streams πŸ’–πŸŒˆβœ¨

πŸ“¦ where this page comes from