BTRFS-BACKUP-NG-RAW(1)
πΈ 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 beraw://PATH(local),raw+ssh://[USER@]HOST/PATH(remote over SSH), or just a plain path (which gets treated asraw://).--jsonβ outputs the authoritative sidecar documents as JSON, for when you're scripting and need machine-readable everything π₯οΈ--ssh-sudoβ usesudofor the remote commands on araw+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 matchescorruptβ the stored stream differs from what was backed uperrorβ the stream could not be read or hashedunverifiableβ 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β usesudofor the remote commands on araw+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β usesudofor the remote commands on araw+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:gpgoropenssl_enc. Required.--gpg-recipient KEYIDβ GPG recipient key. required with--encrypt gpg.--openssl-cipher CIPHERβ OpenSSL cipher foropenssl_enc(defaultaes-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. forlist, this includes an empty or nonexistent target (a warning is printed for a missing local path). forverify, all backups areokorunverifiable. forbackfill-metadata, all candidate sidecars were written (or none were needed). π1β forlist, the target could not be listed. forverify, at least one backup iscorruptorerror. forbackfill-metadata, at least one sidecar could not be written. forencrypt, a stream could not be encrypted, or--shredwas 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--snapshotname 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
- fedora 43: btrfs-backup-ng 0.9.20-2.fc43 Β· MIT
- fedora 44: btrfs-backup-ng 0.9.20-1.fc44 Β· MIT
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms