Latest Posts
Using a Tailscale exit node on a Docker host
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.
Moving Mastodon to Docker Compose
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 multi-stage Dockerfile for Laravel and FrankenPHP
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.
Setting up a free Oracle VPS as a reverse proxy
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.
Auto-Deploying a static site from Forgejo to CloudFlare Pages
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.
Re-verifying failed backups with Proxmox Backup Server
A simple script to re-verify failed backups in Proxmox Backup Server
FreshRSS mobile improvements
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.
Self-Hosting Mastodon Behind Cloudflare Tunnel
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
Setting Up BIMI for Your Email
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.
On Serving Markdown to AI Agents
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.
Adding Plausible to Uptime Kuma Status Page
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.
Investigating the performance of Laravel's `whereIn` vs `whereIntegerInRaw`
Revisiting a classic Laravel tip: does `whereIntegerInRaw()` outperform `whereIn()` in 2026?
Adding Plausible to Mastodon Using nginx `sub_filter`
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.
Using Pi-hole as a DNS Server for my Tailnet
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.
Configuring a Custom Block Page for Pi-hole
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.
Seeing the Full Conversation: Fetching Replies on Mastodon
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 from PostgreSQL 15 to PostgreSQL 17
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.
Reason 1,000,001 why OpenAI sucks
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.
Using Laravel's `PromptsForMissingInput` interface
Discover how to enhance your Laravel Artisan commands using the PromptsForMissingInput trait. Learn to dynamically prompt for missing arguments, apply real-time validation, and leverage Laravel Prompts for secure, interactive input. Build more robust, user-friendly CLI tools with minimal effort.
Cross-Language Queues: Sending Jobs from Node.js to Laravel
Did you know you can push jobs into a Laravel queue from outside your application? Whether you're offloading tasks from a Node.js service or integrating Laravel with external systems, queues make it easy to handle background processing efficiently. In this post, I'll show you how to send a Laravel job from a Cloudflare Worker using AWS SQS—perfect for reducing load on your main app while keeping everything in sync.
Installing and configuring Plausible Analytics Community Edition on Ubuntu
Running through how to install Plausible Analytics Community Edition with its requirements on a bare Ubuntu server.
How (and why) to change Mastodon's Referrer Policy
Set Mastodon's HTTP Referrer Policy to allow passing on the referrer origin (i.e. your domain name) when users click external links.
Creating a Custom PHP Carbon formatter using macros
Using a Carbon macro to customise formatting of date intervals.
How to generate Mastodon's Annual Reports / 'Wrapstodon'
This post explains how you can run Mastodon's Annual Report on your mastodon instance.
FediFetcher Step by Step guide
A very detailed step-by-step guide to installing FediFetcher on your own server.
Smart Routing with Nginx: Serving Different Sites Based on IP and Cookies
This post shows how you can use nginx to set a different website root based on a combination of the visitor's IP address and cookies. You can use this to allow staff and contractors to test a new site, whilst regular visitors still see the old site.
Using Visual MySQL Explain with Laravel
Using mysqlexplain.com's new Visual Explain mode to analyse and improve query performance in Laravel
Using MySQL Views with Laravel
Using MySQL views with Laravel allows you to encapsulate a lot of logic in a model class, which simplifies code, and can improve performance over eager-loading lots of relationships.
Using session cookies with Node.js and Axios
This post shows how to use `axios-support-wrapper` to add support for `tough-cookies` to the Axios HTTP client in Node.js, so that you can use session cookies in Node.js
I built something
Etsy Merchant Feeds allows you to create a product feed of your Etsy listings to use when tagging posts on Instagram, or creating Google Shopping ads.
Event Listeners in Laravel 11
Simplifying event listener registration in Laravel 11
The Perils of your Domain Name
Choosing domains can have consequences that reach beyond what you might expect.
Backing up Nextcloud to Backblaze
Backing up my Nextcloud data from Hetzner's Storage Share to Backblaze B2 using WebDAV and rclone.
Moving to Nextcloud
How I moved from Google Drive to Nextcloud, in order to remove my dependency on Google.
Clearing up the Mastodon Database using pg_repack
Running pg_repack regularly on your Mastodon instance can stop the database from growing too large.
Splitting out Microservices
I've always valued simplicity in all the code I write, and in every bit of infrastructure I maintain. As such I've always favoured monolithic software architecture. So why and how have I recently split out two microservices from our monolith?
Setting up Elasticsearch for Mastodon 4.2.x
With Mastodon 4.2.0 proper full text search has finally arrived. This post runs through the server requirements and setup of Elasticsearch for mastodon 4.2.x.
Tweaking Postgres for Mastodon
I had to increase Postgres' max_connections settings to resolve issues with my Mastodon instance.
Using CloudFlare with a Raspberry Pi for Dynamic DNS
You can use the CloudFlare API and a simple bash script on your Raspberry Pi, to use CloudFlare as a Dynamic DNS service.
Using a CloudFlare Tunnel to share a local development site over the internet
Sharing a local development site over the internet is quite simple using a CloudFlare Tunnel. The Tunnel can even use your own domain, which looks much nicer and more professional, and makes it suitable even for production use.
Colour Contrast and Accessibility
Applying common sense, when aiming for 100 Lighthouse scores.
Muli user support for FediFetcher
FediFetcher now has multi user support. This is great for admins of instances with a very small number of users, who want to run FediFetcher for several of their users.
FediFetcher can now backfill any user mentioned in your notifications
FediFetcher can now backfill the profiles of anyone who is mentioned in your notifications.
`mastodon_get_replies` is now FediFetcher
mastodon_get_replies has always been a pain to both say and type, and also now only covers about 50% of the functionality of the script, so please meet FediFetcher!
Blogging with weblog.lol and GitHub actions
My blog is run on omg.lol's weblog.lol service. In this blog post I want to show how I'm using a GitHub action and the weblog.lol API to manage my blog in Git, rather than through their web interface.
Pull missing posts from new followers, as well as recently followed accounts into Mastodon
Since v3.0.0 of FediFetcher you can backfill remote accounts, after you have begun following them. This post explains how.
Stop disabling form submit buttons
Disabling form buttons on load, and only enabling them when the form is valid, is poor UX. Validate the input when the user attempts to submit the form, and show error messages where relevant, instead.
Pull missing responses into Mastodon
Mastodon does not always pull responses to posts from other instances through to your own instance. This means that if you read a post from another server, you may not see all replies. This post provides a GitHub Action that can help push remote replies into your home instance.
Testing your email setup
This post explains a few options on testing your email setup, as well as for continuous monitoring, to ensure SPF, DKIM and DMARC are all configured correctly.
Using DNS records to protect your email brand
If you own a successful brand, some miscreants will eventually abuse your domain to send spam pretending to be from you. Make sure you configure SPF, DKIM and DMARC to protect your email brand.
Applying Pull Requests from another fork using git
If you have forked an open source project you sometimes want to apply changes from another fork to your own. This post shows how to merge branches from another fork using either GitHub or the command line.
Full-text search in Mastodon
Full-text search support on Mastodon is severely lacking. But you can either use google or install a patch on your own Mastodon instance to significantly improve your search experience.
Translation options for Mastodon
Mastodon supports DeepL as well as LibreTranslate for translating posts. This post compares LibreTranslate and DeepL, and how they work with Mastodon.
Adding comments to your blog, powered by mastodon
Adding mastodon-powered comments to your blog - how to create a dependency free solution to add comments to your blog using the Mastodon API.
My first composer package: Add AI powered fixes to your Laravel error pages
A composer package to use the OpenAI API to show AI-powered fixes for errors in your Laravel application.
Add AI powered fixes to your Laravel error pages
Use the OpenAI API together with Laravel Ignition's Suggestions to show AI-powered fixes for errors in your Laravel application.
Blocking Hashtags from your Mastodon instance
Removing unwanted content from your mastodon instance, that may have been pushed to your server through federated instances.
How I Deal with Money in PHP
Dealing with money and multi-currencies in Laravel and SQL the easy way.
Read and write URLs in JavaScript
Using the URL API to modify a URL is both easier and more reliable than simple string manipulation.
Running a single-user Mastodon instance
Several optimisations and changes I've made to my single user mastodon instance.