namei: The Path Walker That Answers "Why Can't I Read That File?"

2026-07-23

You know the drill. A service running as www-data can't open /srv/apps/tenant-42/uploads/inbox/foo.pdf. You start ls-ing each directory: ls -ld /srv, ls -ld /srv/apps, ls -ld /srv/apps/tenant-42... six commands later you spot a stray drwx------ in the middle. Or you don't, because there's a symlink pointing to a bind mount that lives on an NFS export with squashed UIDs, and now you're chasing your tail.

namei is the util-linux tool built for exactly this. One command, one column-per-hop breakdown, symlinks and mountpoints expanded inline. Been shipping in every distro since the '90s.

$ namei -lx /srv/apps/tenant-42/uploads/inbox/foo.pdf
f: /srv/apps/tenant-42/uploads/inbox/foo.pdf
 Dr-xr-xr-x root     root     /
 drwxr-xr-x root     root     srv
 drwxr-xr-x root     root     apps
 drwx------ deployer deployer tenant-42        <-- gotcha
 drwxr-xr-x deployer www-data uploads
 drwxrwx--- deployer www-data inbox
 -rw-r----- deployer www-data foo.pdf

tenant-42 is mode 700 owned by deployer, so www-data can't traverse into it — the leaf's permissions are irrelevant. The chase is over in one screen.

The flags that matter:

The symlink case, where namei earns its keep:

$ namei -l /var/www/html/uploads/report.csv
f: /var/www/html/uploads/report.csv
 dr-xr-xr-x root root /
 drwxr-xr-x root root var
 drwxr-xr-x root root www
 drwxr-xr-x root root html
 lrwxrwxrwx root root uploads -> /mnt/data/uploads
   dr-xr-xr-x root root /
   drwxr-xr-x root root mnt
   drwx------ root root data          <-- symlink target dies here
   drwxrwxr-x root www uploads
   -rw-rw-r-- root www report.csv

Each symlink gets its own indented subwalk. Try producing that with ls and readlink in a loop and see how long it takes you — and how many corner cases (loops, relative link targets, mid-path links) you'd have to handle.

Why not just stat or ls -ld in a loop? You can, and you probably have. But you have to spell out every component, remember to chase the symlink targets manually, and mentally align seven different outputs. namei tokenizes the path itself, walks each component with lstat(2), and hands you one aligned table. It's the diff between "I'll figure this out" and "here's your bug."

The killer combo: sudo -u www-data namei -lx /srv/apps/tenant-42/uploads/inbox/foo.pdf. Now lstat happens as the actual failing user, and any traversal denials surface right where they'd fail at runtime. If your daemon runs as UID 33 and hits an NFS mount that squashes it to nobody, you'll see the exact hop where the perms lie — without having to su into a service account or spelunk through logs.

Key Takeaway: namei -lx collapses "who denied me at what layer?" from a six-command scavenger hunt into a one-command truth table.

All newsletters