VPS Snaps

How to track /etc changes with etckeeper

etckeeper turns /etc into a git repository. On Debian and Ubuntu, sudo apt install etckeeper creates /etc/.git and the first commit; after that, every apt run and a daily job commit changes on their own, sudo etckeeper commit "message" records your edits, and sudo git -C /etc log shows what changed, when and by whom. It is a change history, not a backup: the repository lives on the same disk as /etc.

8 min readUpdated Checked against official documentation

Install etckeeper

Terminal
sudo apt install etckeeper

The package pulls in git, then runs etckeeper init and etckeeper commit "Initial commit" itself. Ubuntu 24.04 and Debian 12 ship etckeeper 1.18.20, Ubuntu 22.04 has 1.18.16 and Debian 13 has 1.18.22. Check that the first commit exists:

Terminal
sudo git -C /etc log --oneline

If the list is empty (another distribution, or git was missing), set it up by hand. init creates /etc/.git with mode 700, a .gitignore and a pre-commit hook, and stages the files without committing:

Terminal
sudo etckeeper init
Terminal
sudo etckeeper commit "Initial commit"

The .gitignore already skips files that change by themselves, such as ld.so.cache, adjtime, mtab and dpkg's *.dpkg-* copies. Add your own patterns outside its "managed by etckeeper" block, which upgrades rewrite.

What gets committed automatically

WhenCommit messageRun by
InstallInitial commitThe package's install script
Before apt changes packages, if /etc has uncommitted editssaving uncommitted changes in /etc prior to apt runetckeeper pre-install, from DPkg::Pre-Invoke in /etc/apt/apt.conf.d/05etckeeper
After aptcommitting changes in /etc made by "apt install nginx", then the packages that changedetckeeper post-install, from DPkg::Post-Invoke
Once a day, if anything changeddaily autocommit/etc/cron.daily/etckeeper and etckeeper.timer

Both daily triggers run /etc/etckeeper/daily, which commits only when /etc has changes, so running both does no harm. The timer fires 15 minutes after boot and then every 24 hours. Upstream calls it opt-in, but Debian's package enables it on install (Debian bug #1149806, reported against 1.18.22). Check yours:

Terminal
systemctl is-enabled etckeeper.timer

AVOID_DAILY_AUTOCOMMITS=1 in /etc/etckeeper/etckeeper.conf stops only the cron job, because the daily script doesn't read that file. To end daily commits, set it and also run sudo systemctl disable --now etckeeper.timer. Teams that want a reason on every change add AVOID_COMMIT_BEFORE_INSTALL=1, which makes apt stop when /etc has uncommitted edits instead of committing them for you.

Commit your own changes

After editing a file, commit with a message that says why:

Terminal
sudo etckeeper commit "Disable SSH password logins"

Run through sudo, etckeeper records the user who ran sudo as the author, with the name and email from their own ~/.gitconfig if they have one, so the log shows who made each change. etckeeper commit stages everything first (git add --all), new and deleted files included; plain sudo git -C /etc commit -a runs the same pre-commit hook but skips new files. In a script, sudo etckeeper unclean succeeds when there are uncommitted changes.

Read the history

These outputs come from a test repository built with etckeeper's commit messages. Paths are relative to /etc. Without sudo, git can't read /etc/.git (mode 700) and stops with fatal: not a git repository (or any of the parent directories): .git.

Terminal
sudo git -C /etc log --oneline
Output
81e4be6 daily autocommit
acab37a committing changes in /etc made by "apt install nginx"
e208226 Initial commit

Only the commits that touched one file:

Terminal
sudo git -C /etc log --oneline -- ssh/sshd_config
Output
81e4be6 daily autocommit
e208226 Initial commit

The newest change to that file, as a diff:

Terminal
sudo git -C /etc log -p -1 -- ssh/sshd_config
Output
commit 81e4be601b2ed5cd445e6a266288b7f2fe2bcd02
Author: root <root@web1>
Date:   Sat Oct 3 06:25:00 2026 +0000

    daily autocommit

diff --git a/ssh/sshd_config b/ssh/sshd_config
index afcb082..f742952 100644
--- a/ssh/sshd_config
+++ b/ssh/sshd_config
@@ -1,2 +1,2 @@
 PermitRootLogin prohibit-password
-PasswordAuthentication yes
+PasswordAuthentication no

Someone edited sshd_config by hand without committing, so the daily job recorded it as root. Committing your own changes keeps the author and the reason. More commands:

CommandShows
sudo git -C /etc status --shortFiles changed since the last commit
sudo git -C /etc diffThose changes, line by line
sudo git -C /etc log --since=2026-10-01 --statCommits since a date, with the files each one changed
sudo git -C /etc blame ssh/sshd_configThe commit, author and date behind each line
sudo etckeeper vcs logThe same as git log, run in /etc

Restore a file from history

Look at the old version first:

Terminal
sudo git -C /etc show e208226:ssh/sshd_config
Output
PermitRootLogin prohibit-password
PasswordAuthentication yes

Write it over the current file. tee rewrites the existing file in place, so its owner and mode stay as they are; in our test a mode 600 file stayed 600:

Terminal
sudo git -C /etc show e208226:ssh/sshd_config | sudo tee /etc/ssh/sshd_config > /dev/null

Check the service's config before reloading it (sudo sshd -t, sudo nginx -t), then commit the restore:

Terminal
sudo etckeeper commit "Restore sshd_config from e208226"

git checkout <commit> -- <file> and git restore write a new file using your umask. In our test a mode 600 file came back 644, and a deleted file that had been 640, like /etc/shadow, came back readable by everyone. After a checkout, run sudo etckeeper init to reapply the recorded owners and modes. It also stages all changes, so commit afterwards.

The recorded settings live in /etc/.etckeeper: a line such as maybe chmod 0640 './shadow' for every file, plus maybe chgrp 'shadow' './shadow' when the group isn't root. To see one file's entries:

Terminal
sudo grep "'./shadow'" /etc/.etckeeper

What etckeeper records

ItemRecorded?
File contents and symlinksYes, by git
Executable bitYes, by git
Every file's mode, and owners and groups other than rootYes, in /etc/.etckeeper, rewritten by the pre-commit hook on each commit
Empty directoriesYes, as mkdir -p lines in /etc/.etckeeper
ACLs and extended attributesNo; the metadata script skips them
Sockets, pipes, device files, and which files are hard linksNo; etckeeper warns about them
Ignored files, and anything outside /etcNo

Secrets: keep the repository private

/etc holds secrets, and git keeps every version of them: password hashes in /etc/shadow, SSH host private keys in /etc/ssh/, TLS keys under /etc/ssl/private and /etc/letsencrypt, VPN keys and passwords in application configs. etckeeper makes /etc/.git and /etc/.etckeeper readable by root only. Anyone who gets a copy of .git gets every secret it ever held.

Never push it to a public repository, and treat any hosted copy as you would /etc/shadow. To keep a copy elsewhere, the README pushes over SSH to a bare repository on a server you control, created with mode 700. root's SSH key must reach that server:

Terminal
ssh backup1 'mkdir /srv/etc-web1 && chmod 700 /srv/etc-web1 && git init --bare /srv/etc-web1'
Terminal
sudo git -C /etc remote add backup ssh://backup1/srv/etc-web1
Terminal
sudo git -C /etc push backup --all
  • PUSH_REMOTE="backup" in /etc/etckeeper/etckeeper.conf pushes after every commit. A failed push doesn't fail the commit, so check the remote now and then.
  • Encrypt off-site copies of the repository, for example inside an encrypted backup.
  • etckeeper init runs code stored in the repository, so only run it on clones you trust.
  • If a repository holding secrets leaks, rotate those secrets. Rewriting history doesn't recall a copy.

Why etckeeper is not a backup

The repository sits in /etc/.git, on the same disk as /etc: a dead disk, a deleted server or an attacker with root takes both. It covers only /etc, not your sites, databases or /home. Use it as the record of configuration changes and keep a real backup beside it: a tar archive of /etc and your data, copied off the server, as in the server backup checklist. A tar of /etc includes /etc/.git, so the history leaves the server too. Commit before a release upgrade and you can diff every config file the upgrade touched.

Common errors

MessageCauseFix
Auto packing the repository in background for optimum performance. (a daily cron mail)git's automatic housekeeping, which etckeeper runs ten times more often than git's defaultsudo git -C /etc config gc.auto 0, then run sudo git -C /etc gc --quiet now and then. In our test this stopped the message.
** etckeeper detected uncommitted changes in /etc prior to apt runAVOID_COMMIT_BEFORE_INSTALL=1 and uncommitted editsCommit them with sudo etckeeper commit, then rerun apt.
etckeeper warning: special files could cause problems with git:A socket, pipe or device file in /etcMove it or ignore it; AVOID_SPECIAL_FILE_WARNING=1 silences it.
etckeeper warning: hardlinked files could cause problems with git:Two names for one file; git stores two copiesUsually harmless; a symlink avoids it.
warning: etckeeper failed to commit changes in /etc using gitThe commit after apt failedRun sudo etckeeper commit "test" to see git's error.
etckeeper warning: etckeeper is not yet enabled for /etcNo /etc/.gitsudo etckeeper init, then commit.
Please configure a VCS in /etc/etckeeper/etckeeper.confNo VCS= line and no repositorySet VCS="git".
fatal: not a git repository (or any of the parent directories): .gitgit run in /etc without sudoAdd sudo.

Frequently asked questions

Does etckeeper back up /etc?
It keeps a history of /etc in /etc/.git on the same disk, which helps with mistakes but not with losing the server. Copy /etc, including .git, off the server with a real backup.
How do I see what changed in /etc yesterday?
sudo git -C /etc log --since=yesterday -p lists each commit with its diff. sudo git -C /etc diff shows changes not committed yet.
Can I push etckeeper to GitHub?
Only to a private repository, and only if you accept a third party holding your password hashes and private keys. A bare repository on a server you control, mode 700, is safer.
How do I stop etckeeper's daily commits?
Set AVOID_DAILY_AUTOCOMMITS=1 in /etc/etckeeper/etckeeper.conf for the cron job, and run sudo systemctl disable --now etckeeper.timer for the timer.

How this was checked

Commands, limits and prices were checked against these official pages, on October 4, 2026: