DEV Community

Cover image for Track Linux Config Drift with etckeeper + systemd.path: Immediate /etc History Without Guesswork
Lyra
Lyra

Posted on

Track Linux Config Drift with etckeeper + systemd.path: Immediate /etc History Without Guesswork

Track Linux Config Drift with etckeeper + systemd.path: Immediate /etc History Without Guesswork

Linux configuration drift is rarely dramatic. It is usually one line in sshd_config, a package upgrade that rewrites a default, or a hurried edit at 2 AM that nobody remembers the next morning.

If you already use Git for code, the obvious question is: why not keep /etc under version control too?

That is exactly what etckeeper is for. It puts /etc in a Git repository, tracks metadata that Git normally misses, and can automatically commit changes made during package operations. With one extra systemd.path unit, you can also record manual edits to /etc much closer to real time.

This guide shows a practical setup that is:

  • easy to audit
  • safe enough for real Linux systems
  • focused on complete, working examples
  • explicit about the security trade-offs

Why this is different from file integrity monitoring

I already covered full file integrity monitoring with AIDE. That solves a different problem: detecting whether protected files changed at all.

This setup is about operational history for /etc:

  • what changed
  • when it changed
  • which package operation triggered it
  • what the previous content looked like
  • how to roll back a bad edit quickly

In other words: this is configuration history, not tamper detection.

What etckeeper actually does

According to the upstream README, etckeeper stores /etc in a VCS repository, hooks into package managers such as apt to automatically commit configuration changes made during package upgrades, and stores metadata Git does not normally track, including permissions that matter for files like /etc/shadow.

That last part matters. Plain Git is not enough for /etc because permissions and ownership are part of the configuration story.

The security warning you should not skip

Before we touch commands, the biggest caveat:

/etc can contain sensitive material.

The upstream etckeeper documentation explicitly warns that versioning /etc creates another copy of secret files such as /etc/shadow, and that the repository contents must remain private. If you push it anywhere, that remote must be trusted and locked down.

So the rule is simple:

  • keep /etc/.git readable by root only
  • do not push to a public remote
  • treat any backup of the repository as sensitive data

If your system stores private keys or secrets under /etc, assume the Git history is sensitive too.

Step 1: Install etckeeper

Debian / Ubuntu

sudo apt update
sudo apt install -y etckeeper git
Enter fullscreen mode Exit fullscreen mode

Fedora

sudo dnf install -y etckeeper git
Enter fullscreen mode Exit fullscreen mode

Arch Linux

sudo pacman -S etckeeper git
Enter fullscreen mode Exit fullscreen mode

On many distributions, package installation initializes the integration hooks automatically, but I still recommend verifying the config and making the first commit yourself.

Step 2: Check the main config

Open:

sudo editor /etc/etckeeper/etckeeper.conf
Enter fullscreen mode Exit fullscreen mode

Things to verify:

  • VCS="git"
  • your package-manager settings are correct for the distro
  • automatic commits are enabled the way you want

For a typical Git-based setup, the defaults are usually fine.

Step 3: Initialize /etc and create the first commit

sudo etckeeper init
sudo git -C /etc status
sudo git -C /etc commit -m "Initial /etc check-in"
Enter fullscreen mode Exit fullscreen mode

Why use git -C /etc here instead of cd /etc? It is easier to script and makes it obvious which repository is being modified.

Then pack the repository to save space:

sudo git -C /etc gc
Enter fullscreen mode Exit fullscreen mode

The upstream README recommends git gc after the initial commit because it can reduce the repository footprint significantly.

Step 4: Verify package-manager auto-commits

One of the most useful etckeeper features is automatic history around package changes.

The upstream README documents that before package installation, etckeeper checks whether /etc has uncommitted changes, and after package installation it adds interesting files and commits the result.

A quick verification workflow on Debian or Ubuntu:

sudo apt install -y tree
sudo git -C /etc log --oneline -n 5
Enter fullscreen mode Exit fullscreen mode

You should see commit messages created around the package operation.

On Arch Linux, the ArchWiki documents that package hooks automatically run before and after package installation, update, and removal in current etckeeper packaging.

Step 5: Add near-real-time commits for manual /etc edits

Package hooks are great, but they do not capture every hand edit immediately.

This is where systemd.path helps.

From the systemd.path(5) manual:

  • PathChanged= activates a service when a watched file or directory changes and the open file gets closed
  • path units use inotify(7) under the hood
  • they are not appropriate for remote NFS changes because they inherit inotify limitations

That makes PathChanged=/etc a practical way to notice local edits and trigger a small commit service.

Create the commit helper script

Create /usr/local/sbin/etckeeper-path-commit.sh:

sudo install -d -m 0755 /usr/local/sbin
sudo tee /usr/local/sbin/etckeeper-path-commit.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

# Avoid empty commits.
if etckeeper vcs status --porcelain | grep -q .; then
  etckeeper commit "auto: capture manual /etc changes"
fi
EOF

sudo chmod 0755 /usr/local/sbin/etckeeper-path-commit.sh
Enter fullscreen mode Exit fullscreen mode

Create the service unit

sudo tee /etc/systemd/system/etckeeper-path-commit.service >/dev/null <<'EOF'
[Unit]
Description=Commit manual /etc changes with etckeeper
Documentation=https://etckeeper.branchable.com/README/

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/etckeeper-path-commit.sh
Nice=10
IOSchedulingClass=idle
EOF
Enter fullscreen mode Exit fullscreen mode

Create the path unit

sudo tee /etc/systemd/system/etckeeper-path-commit.path >/dev/null <<'EOF'
[Unit]
Description=Watch /etc for manual configuration changes
Documentation=man:systemd.path(5)

[Path]
PathChanged=/etc
Unit=etckeeper-path-commit.service

[Install]
WantedBy=multi-user.target
EOF
Enter fullscreen mode Exit fullscreen mode

Enable it

sudo systemctl daemon-reload
sudo systemctl enable --now etckeeper-path-commit.path
Enter fullscreen mode Exit fullscreen mode

Test it

echo '# drift test' | sudo tee -a /etc/motd >/dev/null
sleep 2
sudo git -C /etc log --oneline -n 3
sudo git -C /etc diff HEAD~1 HEAD -- motd
Enter fullscreen mode Exit fullscreen mode

If the path unit triggered correctly, you should see a fresh commit containing the motd change.

Why I use PathChanged= instead of PathModified=

The systemd.path(5) manual distinguishes the two:

  • PathChanged= triggers when the watched file is changed and then closed
  • PathModified= also triggers on simple writes

For /etc, PathChanged= is usually the calmer choice because it reduces noisy mid-write activations. You want a commit after an edit is finished, not during every write burst from an editor.

Step 6: Add a private backup remote

A local /etc/.git history is already useful, but a second private copy makes recovery much easier.

The upstream etckeeper README gives a safe pattern: create a root-only bare repository on a trusted host, then push over SSH.

On the backup host:

ssh backup-host 'mkdir -p /srv/etckeeper/server1 && chmod 700 /srv/etckeeper/server1 && git init --bare /srv/etckeeper/server1'
Enter fullscreen mode Exit fullscreen mode

On the machine you are protecting:

sudo git -C /etc remote add backup ssh://backup-host/srv/etckeeper/server1
sudo git -C /etc push backup --all
Enter fullscreen mode Exit fullscreen mode

If you want to push after every commit, etckeeper supports a PUSH_REMOTE setting or custom commit hooks, but I would start with manual pushes until you are sure the security model is right.

Useful day-2 commands

See what changed recently

sudo git -C /etc log --stat --oneline -n 10
Enter fullscreen mode Exit fullscreen mode

See exactly what changed in one file

sudo git -C /etc log -p -- ssh/sshd_config
Enter fullscreen mode Exit fullscreen mode

Compare current config with yesterday's state

sudo git -C /etc diff HEAD~1 HEAD
Enter fullscreen mode Exit fullscreen mode

Roll back one file carefully

sudo git -C /etc restore --source=HEAD~1 -- ssh/sshd_config
sudo etckeeper commit "restore sshd_config from previous revision"
Enter fullscreen mode Exit fullscreen mode

Do not blindly roll back all of /etc on a live box unless you fully understand the consequences. Restoring a single file is usually safer.

Operational caveats

A few things are worth saying plainly:

1) /etc is not a normal application repo

The etckeeper README warns that checking out older branches or revisions directly into /etc can be risky, because Git operates on the live system. If you do a branch checkout or restore older states broadly, rerun etckeeper init so metadata handling is refreshed.

2) Path units are not magic

systemd.path uses inotify. That means:

  • it is good for local filesystem changes
  • it is not reliable for remote NFS-originated changes
  • very noisy writers can trigger more often than you expect

If you have software rewriting /etc constantly, add filtering or skip the path unit and rely on daily/package-manager commits.

3) Secrets still need separate handling

This article is about change history, not secret management. If you are trying to remove secrets from .env files or service definitions, use a dedicated mechanism such as systemd credentials or an external secret manager.

A sane minimal setup

If you want the shortest path to value, do just this:

  1. install etckeeper
  2. initialize /etc
  3. make the first commit
  4. verify package-manager auto-commits
  5. optionally enable the path unit if manual edits are common
  6. push to a private bare Git remote over SSH

That gets you a useful audit trail with very little operational overhead.

Final thought

When Linux configuration breaks, the hardest part is often not fixing it. It is reconstructing the story.

etckeeper gives /etc a memory. Adding systemd.path makes that memory more immediate.

And when the next "nothing changed" incident lands on your desk, having a real commit history beats guesswork every time.

References

Top comments (0)