Reducing Docker deploy downtime for Laravel and FrankenPHP
Every docker compose up -d took my site down longer than I liked. Three fixes: bake caches into the image, do the volume setup once, and split the deploy so the old container keeps serving.
Michael's blog
Every docker compose up -d took my site down longer than I liked. Three fixes: bake caches into the image, do the volume setup once, and split the deploy so the old container keeps serving.
Restart policies only cover containers that have already started. When AppArmor blocked dockerd's default profile at boot, thirteen containers stayed down for four hours. Here's my fix.
My FediFetcher install kept tripping my own instance's rate limits. A Rack::Attack initialiser let me safelist its IP, once I'd worked out which IP Rails actually sees.
Setting up Pocket ID as an OIDC provider for a self-hosted Mastodon instance, including the undocumented settings needed to link an existing account without locking yourself out.
Dumped a database, imported it on a new server, and suddenly couldn't select from my view. Turns out views run as whoever created them, and mine no longer existed.
A run-through of everything I self-host for myself and my family - from Headscale and Forgejo to Mastodon and Home Assistant - plus the few things I've decided not to.
Self-hosting a static blog with push-to-deploy: Forgejo Actions ships the site over a Tailscale/Headscale tailnet to a Caddy reverse proxy, with no long-lived SSH keys to manage.
How to stop a Tailscale exit node from breaking Docker container networking, using Linux policy routing and a small systemd unit to make the fix permanent.
I finally moved my Mastodon server to Docker. Here's the full migration: inventorying dependency versions, keeping config in git, and importing the postgres dump and redis snapshot.
A walkthrough of a four-stage Dockerfile that ships a lean Laravel 13 image on FrankenPHP, plus the entrypoint and Compose services to actually run it.
Set up a zero-cost reverse proxy: an Oracle Always Free VPS running Caddy, linked to home origin servers via Tailscale, with automatic HTTPS and one-line site additions.
A step-by-step guide to deploying a static site from a self-hosted Forgejo instance to CloudFlare Pages, using a Forgejo Action that redeploys automatically on every push to main.
A simple script to re-verify failed backups in Proxmox Backup Server
Using FreshRSS's Custom CSS and JS extensions to improve the mobile experience: flex-based multi-line article layout, summary spacing, and scroll-aware footer.
A step-by-step guide to running Mastodon behind a Cloudflare Tunnel - ideal if you're self-hosting on a domestic connection without a static IP
A step-by-step guide to setting up self-asserted BIMI: the DNS standard that puts your brand logo in recipients' inboxes, no certificate required.
Cloudflare and Spatie have both shipped tools to serve Markdown to AI agents. I tried it - and discovered ChatGPT silently rejects text/markdown responses entirely.
Struggling to connect Plausible to Uptime Kuma’s status page? The required Analytics ID and Script URL aren’t obvious - here’s what they actually are.
Revisiting a classic Laravel tip: does `whereIntegerInRaw()` outperform `whereIn()` in 2026?
Injecting Plausible Analytics into Mastodon using nginx sub_filter, CSP header overrides, and inline script hashes - plus why doing this is probably a bad idea.
Use Pi-hole as a DNS server across your entire Tailnet with Tailscale, without forcing all traffic through an exit node. Ad blocking and internal DNS everywhere.
Set up a custom Pi-hole block page so users know when a domain is being filtered. It’s easy to enable and provides helpful context for anyone on your network.
Mastodon doesn't always show replies to posts from other instances. This post explores several ways to improve reply visibility and make conversations feel more complete.
Upgrading Mastodon’s database from PostgreSQL 15 to 17 on Ubuntu 24.04 is fast and easy using `pg_upgradecluster` with link mode. Here’s a quick guide with steps, tips, and cleanup.
After days of debugging unexplained span usage in Sentry - despite an ultra-low sampling rate - I discovered `traceparent` headers in requests traced back to OpenAI. Turns out, it's always AI.