Why Server Configs Belong in Git, Not in Your Head
Almost every server I take over has the same quiet problem: the configs work, but nobody can tell you why they look the way they do. An Nginx vhost has an extra proxy_read_timeout bumped to 300 seconds. A fail2ban jail has a whitelisted IP range. A sysctl tweak doubles a connection limit. Each change was probably the right call at the time, made live, under some pressure, and then never written down anywhere except in that file itself. Six months later, nobody remembers which changes are load-bearing and which were leftover experiments, and nobody wants to be the one who removes the wrong line.
The lightest possible fix: etckeeper
For a single server, the smallest useful step is etckeeper, which puts /etc under git and commits automatically whenever a package manager touches a config file:
apt install etckeeper
cd /etc
git log --oneline -10
From that point on, every manual edit you make gets picked up the next time etckeeper commits (or you can git add -A && git commit yourself right after editing). The payoff shows up the first time something breaks after a config change and you can run:
git log -p -- nginx/sites-enabled/example.com
and see exactly what changed, when, and, if you wrote a commit message, why. That last part matters: git commit -m "bump proxy_read_timeout for slow report export endpoint" is the difference between future-you understanding a config and future-you being afraid to touch it.
For multiple servers: a configs repo per client
etckeeper is local to one machine, which is fine for a single VPS but doesn't help when a client has three or four servers with related configs. For those, I keep a separate repo structured by host:
client-configs/
├── web-01/
│ ├── nginx/sites-available/example.com
│ └── fail2ban/jail.local
├── db-01/
│ ├── postgresql/postgresql.conf
│ └── pgbackrest/pgbackrest.conf
└── deploy.sh
Changes happen locally, get reviewed in a diff before they go anywhere, and a small deploy.sh pushes the relevant files to the right host:
#!/bin/bash
HOST=$1
rsync -av "$HOST/" "deploy@$HOST.internal:/" --dry-run
# remove --dry-run once the diff looks right
The --dry-run default is deliberate. Config deploys are exactly the kind of thing where a misplaced trailing slash in rsync can overwrite more than intended, so the default behavior shows you what would change before it actually does.
What this buys you that documentation doesn't
The usual alternative is a wiki page or a README describing "important config changes." These rot almost immediately, because updating the wiki is a separate step from making the change, and separate steps get skipped under time pressure. Git history doesn't have that problem, the record is a byproduct of doing the work, not an extra task layered on top of it.
It also changes how changes get made. When a config lives in git, the natural move is to edit it locally, commit with a message explaining the reasoning, and then deploy, rather than SSHing in and editing the live file directly. That small shift in workflow is where most of the value comes from: not the backup, but the habit of writing down why a moment before you do the what. Three months later, when someone (often me) is staring at a config wondering if a particular line can be safely removed, that one sentence in a commit message saves an hour of cautious archaeology.