Notes from the terminal.
Writing on Laravel, Vue.js, self-hosted infrastructure, and the everyday problems of shipping real systems. Debugging war stories, architecture decisions, and the occasional strong opinion.
Why I put declare(strict_types=1) at the top of every PHP file
It's one line at the top of the file. It costs nothing, catches a category of bugs Laravel happily ignores, and has exactly three gotchas worth knowing about.
Stop passing arrays around your Laravel app
Array shapes are the silent tax on every Laravel codebase. DTOs cost ten minutes to introduce and pay you back forever. Here's the hand-rolled version, then the spatie upgrade.
The silent Horizon queue failure that cost us a full day of jobs
A Laravel Horizon deploy that looks healthy in every dashboard but processes zero real work — and the Redis curly-brace queue naming quirk behind it.
Running Coolify on a Raspberry Pi 5: what I deploy, and what I don't
A year of hosting side-projects and internal tools on a Pi 5 with Coolify and Cloudflare Tunnels — the wins, the scars, and the workloads I still send to AWS.
Multi-tenant domain resolution middleware for franchise platforms
Resolving the current tenant from the incoming domain in Laravel — without patching the router, breaking caching, or losing custom franchise domains.
Laravel Passport v13: the plain-secret storage change that broke my OAuth flow
Passport v13 changed how client secrets are stored. If you upgrade without reading the changelog, existing OAuth consumers will quietly stop working.
A stack-agnostic Docker CI/CD pipeline with GitHub Container Registry
The reusable GitHub Actions workflow I drop into every Laravel and Next.js project. One per-project Dockerfile, environment-differentiated deploys, no vendor lock-in.
onUnmounted isn't firing in my Inertia.js app — here's why
Inertia reuses component instances across page visits more aggressively than you'd expect. If your Vue cleanup code isn't running, this is probably the reason.
Post-mortem: crypto mining malware across an end-of-life Laravel install
How an unpatched Laravel 9 app became the vector for a multi-site compromise, what the attacker did once they were in, and the hardening I now apply to every production server.