sysadmin automation with git hooks

When I first created my VPS, I edited config files and made a note in a file in my home directory and that was it. If I were to ever move to a different VPS, then I would’ve copied over the notes file and all the files it mentions and set things up manually. Pragmatically, this is a perfectly acceptable setup given I have very little on my VPS and so far have only moved once in the 5 or so years I have been administering a server for. However, it has no form of version control and requires doing all the work through an ssh session which isn’t always desirable.

I recently came across this blog post explaining how a post-receive git hook can be used to automatically update a website’s content when the repository is pushed to and decided to make some use of it. Currently, I use a makefile rule like this to send my built site to my VPS independently of pushing:

push: site
    rsync --exclude-from=rsync-excludes -ruzv ./ user@host:/var/www/

This is what I am continuing to use as my website’s repository is not just static files, and it relies on a handful of more obscure and self-made tools. However, in the case of the VPS config files this can work perfectly.

I moved all those config files and things for my VPS into a private git repository with a tree mirroring where they lie in my system. I also put the notes in that repo so I always have them easily accessible, then I just have this post-receive hook (which goes in the hooks directory of the bare git repo on the server that’s being pushed to):

mkdir /tmp/vps
GIT_WORK_TREE=/tmp/vps git checkout -f main
sudo rsync --exclude-from=/tmp/vps/rsync-excludes -rv /tmp/vps/ /
git diff-tree --no-commit-id --name-only -r \
    $(git rev-parse --verify HEAD) > /tmp/vps/modified
grep nginx.conf /tmp/vps/modified && sudo systemctl restart nginx
rm -rf /tmp/vps

What this does is first create a temporary directory the tree can be checked out to. Then it uses rsync to apply the config files to the system. Next it gets a list of all the files that were modified in the last commit and restarts services if files relating to them were affected (in the example I’m just doing this for nginx, but in the real one I use it for several other files too).

Note that there is the caveat that you’ll need to allow the user running the git commands (the one you’re pushing as) to run the systemctl and rsync commands with sudo without a password as they will not run interactively.

The result of this setup is I now have all my server’s config files version controlled, more easily accessible for tweaking, and a handful of menial tasks are automated for me!