LOG-003 isn’t a project. It’s the thing that makes the rest of this log possible to write without worrying whether the site will still be here tomorrow — the deploy path and the backup pipeline behind rootlabs.us itself.
What we almost shipped
Before any of this existed, the local checkout and GitHub’s copy of this repo had drifted — three commits ahead, seven behind. Reconciling them meant actually reading what was on the other side, not just merging blind. What we found on main was 51 files from a pre-WordPress version of this site: a hand-rolled blog engine from before the WordPress migration, including its own blog/index.php. Deploying that as-is would have overwritten WordPress’s own blog/index.php and taken both the blog and the forums offline, silently, on the next push.
Nothing was live yet, so nothing broke. But it’s exactly the kind of mistake that only announces itself after it’s already happened — and the honest fix wasn’t “be more careful next time,” it was “stop deploying in a way where this is possible.”
The deploy path
deploy-rootlabs.sh runs on the server and does three things in order: backs up first, refuses to touch anything if the live tree has uncommitted changes, then git pull --ff-only. A fast-forward-only pull can’t silently merge or rewrite history — if the server’s copy has diverged from what’s on GitHub, it stops and says so instead of guessing. After the pull, it smoke-tests three URLs and reports pass or fail. If any of that fails, nothing about the live site has changed yet.
Backups that survive losing the server
A nightly job on the server takes the site files and a database dump at 3:15am, keeping 14 days locally, with credentials read at runtime from a file outside the web root rather than baked into the script. That protects against a bad deploy. It does nothing for losing the server itself.
The second half runs the other direction: a script on the homelab side pulls those backups down to a NAS share, rather than the server pushing them out. That’s deliberate. The server holds no credentials for the homelab NAS — it couldn’t reach in and delete backups even if something on it were compromised, and it’s sitting in AWS with no route to the home LAN regardless. Pulling from the side that’s allowed to fail is worth the extra script.
That script had its own small war: pscp timed out against this host for reasons that were never worth chasing down, so file transfer goes through plink piping a remote cat instead — verified byte-exact with gzip -t against every file it pulls. And the transfer loop reads its file list from a here-string, which meant plink was quietly swallowing every line after the first until stdin got explicitly redirected from /dev/null per call. Small thing. Would have meant only the newest backup ever actually made it to the NAS, forever, without a single error to say so.
Why this is LOG-003
Everything else in this log is a thing we built. This is the reason a bad Tuesday doesn’t erase any of it.