What This Project Is
I set up a Git repository /opt/homelab-git on .223, bringing the things scattered across three machines and the NAS under version control: programs and configs on the .164 NAS, service configs on the .223 host itself, documents in the NAS workspace, and all DSH conversation records (including the old conversation from the Windows era). The first backup was about 1,400 files and 183M.
Why Do This
The biggest fears in a homelab are "I changed the config and forgot where I changed it" and "the machine blew up and I have to rebuild it from scratch." Previously I relied on scattered .bak files; I could roll back, but I couldn't clearly trace what happened. Now it automatically syncs and commits once a day at 1:00 a.m., so for any change I can find out which day it was made and what was changed.
There is also a side benefit: the backup files left by changes over the years are mostly still on the disk, and I included them as well, which effectively adds the history of older versions to the repository.
How to View It
Web page https://xiejun-server.top/git/; the first card on the homepage also provides an entry; or ssh xiejun-server then cd /opt/homelab-git and run git log directly. The web page is automatically generated and shows the cumulative number of backups, a table of the most recent 40 backups, the size and file count of each part, and a search box for "find changes by file" (pure front-end query, supports direct links like ?q=dashboard/app.js). There is also a "⚡ Sync now" button on the page, so if you want to update during the day you don't have to wait until the early morning.
Current Status
Running; the systemd timer triggers at 01:00:12 every day and has been running stably for a while. The repository only reads and copies the source files; the script never modifies the backed-up content.
Features
- 每天 01:00 自动 rsync + commit,关机错过会在开机后补跑
- 统一收纳 .164 配置、.223 服务配置、NAS 文档与全部 DSH 对话记录
- 自动生成暗色主题进展网页,含最近 40 次备份、体积统计与「按文件查改动」搜索
- 网页内「立即同步」按钮,带防滥用限流(60 秒间隔、每小时 20 次)
- 仓库只读复制源文件,从不改动被备份的内容,可整机还原
Stack: Git · rsync · systemd · Python · Nginx Proxy Manager · Gitea · SSH
Deployment Steps
1. Prepare the repository directory
On the backup machine (in my case, .223), create the directory and run git init:
mkdir -p /opt/homelab-git && cd /opt/homelab-git
git init
git config user.name "backup" && git config user.email "backup@local"
2. Write the sync script
/opt/homelab-git/sync.sh roughly does four things:
- Use
rsyncto pull each source directory intoprojects/164/,projects/223/,projects/nas/, andconversations/; - Run
git add -A && git commit -m "backup $(date +%F)"(skip if there are no changes); - Call
gen-page.pyto generate the progress page andscpit to the Web host; - Redirect logs to a file, and exit with a non-zero code on failure.
Note that the script must explicitly exclude the repository's own directory; otherwise, it will sync itself.
3. Configure a systemd timer
homelab-git-backup.service: Type=oneshot, Nice=10, TimeoutStartSec=3600. homelab-git-backup.timer: OnCalendar=*-*-* 01:00:00, Persistent=true.
systemctl enable --now homelab-git-backup.timer
systemctl list-timers homelab-git-backup.timer # confirm the next trigger time
systemctl start homelab-git-backup.service # run once manually to verify
journalctl -u homelab-git-backup.service -n 50
4. Publish the progress page
Generate a static index.html, scp it to the Web host's directory, and add the following in Nginx:
location ^~ /git/ { alias /data/gitweb/; index index.html; }
If you want to add a "Sync Now" button, run a small Python standard-library HTTP service on the backup machine (listening on an intranet port), reverse-proxy it through Nginx to /git/sync/, and set proxy_read_timeout to 300s. The sync endpoint should have Token validation; the token file should have permissions 600 and must not be committed to git.
5. Rollback
systemctl disable --now homelab-git-backup.timer
rm -rf /opt/homelab-git # delete only the repository; source files are unaffected
General Notes
- Permissions are often a pitfall when reading shared directories across machines. First confirm the read-only mount and ownership; if necessary, grant
a+rXon the source side. - Do not use
cmd | grep -v xxx || warnto determine failure. When grep has no match, its exit code is 1, which causes a false alarm. Instead, output to a temporary file and then check. - Verify configuration paths; do not write them from memory (for example, natfrp's configuration is in
/etc/natfrp, not/opt/natfrp).