Blog
Notes from running a small self-hosted stack — nginx, fail2ban, Docker, Go, and the things that broke.
fail2ban's client only shows you the present
I wanted to know which addresses had been banned most often. fail2ban-client can't tell you — it reports current state and a couple of counters that reset on restart. The history is in its SQLite database, and by default that database is throwing it away after a day.
FROM scratch Go images have no /tmp
os.CreateTemp failed in production with "no such file or directory" — on the temp directory itself. The tests passed because go test runs on a host that has a /tmp. Nothing in the Go toolchain can see this bug.
Four days of nothing, and every health check stayed green
My log collector went silent for four days at a logrotate boundary. The container was up, the process was running, its own logs kept ticking, and it had ingested exactly zero rows. An open file descriptor follows the inode, not the path.
fail2ban kept banning my own git pushes
Git's smart-HTTP protocol always sends one unauthenticated request first and expects a 401 back. SWAG's bundled nginx-unauthorized jail counts any 401 site-wide — so pushing to my own private repos banned my own IP, twice in two days.