augeas / augtool: Editing System Config Files Without sed Voodoo

2026-06-15

Every sysadmin has written the same line a hundred times:

sed -i 's/^#*PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config

And every sysadmin has, at some point, watched it fail in interesting ways: the key was commented twice, it lived inside a Match block, it had a trailing comment, the file had no newline, the regex matched in AllowedPermitRootLogin, or the change duplicated because the script ran twice. Configuration files are structured data we keep pretending are text.

Augeas (and its CLI augtool) is the cure. It parses common config formats through "lenses" into a tree, lets you address nodes by path, and writes the file back preserving comments, ordering, and whitespace. puppet uses it under the hood; you can use it directly.

Inspect what Augeas sees:

$ augtool print /files/etc/ssh/sshd_config | head
/files/etc/ssh/sshd_config/Port = "22"
/files/etc/ssh/sshd_config/PermitRootLogin = "yes"
/files/etc/ssh/sshd_config/PasswordAuthentication = "no"
/files/etc/ssh/sshd_config/Match[1]/Condition/User = "git"
/files/etc/ssh/sshd_config/Match[1]/Settings/ForceCommand = "/usr/bin/git-shell"

Now flip a setting idempotently — works whether the key is present, commented, or missing:

$ augtool -s set /files/etc/ssh/sshd_config/PermitRootLogin no
Saved 1 file(s)

The -s means save. Run it twice — file is byte-identical the second time. Try that with sed -i.

Modify a value only inside a specific Match block — try writing this with awk:

$ augtool -s <<EOF
set /files/etc/ssh/sshd_config/Match[Condition/User='git']/Settings/X11Forwarding no
EOF

Add a host to /etc/hosts if it isn't there:

$ augtool -s <<EOF
set /files/etc/hosts/01/ipaddr 10.0.0.5
set /files/etc/hosts/01/canonical buildbox
EOF

Read every fstab mount that uses nfs:

$ augtool match "/files/etc/fstab/*[vfstype='nfs']/file"
/files/etc/fstab/3/file = /mnt/shared
/files/etc/fstab/7/file = /mnt/backups

Add a sudoers rule without losing your job to a syntax error:

$ augtool -s <<EOF
set /files/etc/sudoers/spec[last()+1]/user deploy
set /files/etc/sudoers/spec[last()]/host_group/host ALL
set /files/etc/sudoers/spec[last()]/host_group/command "/usr/bin/systemctl restart app"
set /files/etc/sudoers/spec[last()]/host_group/command/runas_user root
set /files/etc/sudoers/spec[last()]/host_group/command/tag NOPASSWD
EOF

Augeas validates as it writes. Break the syntax in your script and the file is left untouched, original kept as .augsave.

Built-in lenses cover the usual suspects: sshd, sudoers, fstab, hosts, resolv.conf, nsswitch, pam, postfix, dnsmasq, logrotate, grub, krb5, chrony, php.ini, nginx, samba, and a hundred more. List them:

$ augtool ls /augeas/load

For a format Augeas doesn't ship with, you can write your own lens — it's a small composable parser DSL based on Boomerang. But you rarely need to; the included set covers nearly every /etc file you'll touch in anger.

Why this beats sed-and-pray: idempotency, structural awareness, and atomic writes. Your provisioning script becomes describable as "this config tree should contain these nodes," not "run this regex and hope." It's the difference between editing JSON with jq versus sed — except augeas works on every weird inherited /etc/ format invented before JSON existed.

Key Takeaway: When a config file deserves to be treated as structured data, augtool gives you tree-addressed, idempotent, format-aware edits that survive comments, sections, and re-runs — without the sed regex roulette.

All newsletters