mtree: The BSD Manifest Tool That Notices When Your /etc Grew a New File at 3 AM

2026-09-14

Every few years someone reinvents this: crawl a directory, hash every file, save the list, diff it later. The reinventions are usually a shell script wrapped around sha256sum and find, and they always miss something — mode bits, symlink targets, ownership, xattrs, or the fact that a file being missing matters as much as one being modified.

BSD solved this in 1989. It's called mtree, and on Linux you install it as mtree-netbsd (Debian/Ubuntu) or bsdmtree (via libarchive on Fedora/Arch). It writes a plain-text specification of a directory tree — one entry per file, with whichever keywords you asked for — and later verifies any tree against that spec.

The spec looks like this:

/set type=file uid=0 gid=0 mode=0644
.               type=dir
    hosts       size=412 sha256digest=e3b0c44298fc1c14...
    passwd      size=1834 mode=0644 sha256digest=af5570f5a1810b7a...
    shadow      mode=0640 gid=42 sha256digest=6b3a55e0261b0304...

/set lines set defaults that following entries inherit — so the file is dense, not repetitive. Snapshot /etc:

mtree -c -K sha256digest,size,mode,uid,gid,link -p /etc > /var/backups/etc.mtree

Then, later — cron, post-deploy, after an intrusion scare — verify:

mtree -f /var/backups/etc.mtree -p /etc

Output tells you exactly what drifted:

ssh/sshd_config: sha256digest expected af5570f5... got 6b3a55e0...
ssh/sshd_config: modification time expected Sun Sep 14 03:12:04 2026 got Mon Sep 14 03:47:19 2026
./cron.d extra: mystery_job
./motd missing

Three categories in one pass: modified, extra, missing. That last one is where the homegrown find | sha256sum pipelines always fall over — they hash what's there, not what should be there.

Where it beats the alternatives:

The tricks nobody tells you about:

Compare two live directories without ever writing a spec to disk:

mtree -c -K sha256digest -p /srv/app-v1 | mtree -p /srv/app-v2

Reset a tree's permissions from a known-good spec (this is how FreeBSD lays down / on install):

mtree -U -f /etc/mtree/BSD.root.dist -p /

The -U flag doesn't just report drift — it repairs it, chmod'ing and chown'ing to match. Content it won't touch; ownership and modes it will.

Exclude noisy paths with a plain-file list (one glob per line):

mtree -c -X /root/exclude.list -p /var > /var/backups/var.mtree

Filesystem manifests as build artifacts: generate the spec at package time, ship it with the package, and let the installer verify the extraction. Debian's debsums is a weaker version of this idea; rpm -V is a stronger version welded into RPM. mtree does it as a standalone file, portable across any Unix.

The whole tool is one C binary, no dependencies, no state directory, output is human-readable, exit code is zero when clean and non-zero when drifted. That's about as Unixy as intrusion detection gets.

Key Takeaway: mtree gives you tamper-evident directory manifests in a plain-text format — with no daemon, no database, and honest handling of missing files — replacing whatever find | sha256sum script you were about to write.

All newsletters