Back to Blog

Why I Keep Moving More of My Stack to Self-Hosting

#self-hosting#vps#devops
Why I Keep Moving More of My Stack to Self-Hosting

I already run this site off a plain VPS with Nginx instead of an automatic platform, so I’m clearly not allergic to managing my own infrastructure. But this year I noticed I’d quietly moved a handful of other things off SaaS tools and onto that same VPS too — analytics, uptime monitoring, a form backend, an image processing step — without ever deciding “I’m going to self-host more now” as a single conscious choice. Looking back at why each one happened individually, cost turns out to be the smallest reason, not the biggest one.

Analytics was the first thing to go, and privacy was the actual trigger. I was using a third-party analytics SaaS that was fine, cheap even, but every page load meant a request out to someone else’s server carrying visitor data I didn’t strictly need to hand over. I run a privacy-first app on the side, and it started feeling inconsistent to care that much about user data in one project while shrugging about it in another. I moved to a self-hosted, cookieless analytics tool on the same VPS. It’s not a downgrade — I actually get the numbers I care about (page views, referrers, rough geography) without a cookie banner, and the data never leaves infrastructure I control. The cost savings were real but modest. The actual motivation was that I didn’t want to keep making an exception for my own site.

Uptime monitoring moved because of a very specific failure. I was using a free-tier SaaS uptime checker, and one week it silently stopped sending me alerts — no error, no notification that notifications had stopped, I just happened to check the dashboard and noticed my site had been down for six hours with zero pings sent. That’s the nightmare scenario for a monitoring tool: not that it’s imperfect, but that it fails silently exactly when you need it. I moved to a small self-hosted checker that pings my sites and emails me directly, no third party in the loop that can quietly degrade. It’s less polished — no fancy status page, no historical uptime graphs — but I trust the failure mode more, because when it breaks, it breaks loudly on infrastructure I’m already watching.

The form backend move was about not depending on a company’s roadmap. I was using a hosted form-to-email service for a couple of contact forms across different projects. It worked fine until the company pushed a pricing tier change that moved a feature I relied on behind a higher paid plan, with about two weeks’ notice. Nothing broke, I just had to scramble to adjust before the deadline. That’s a small thing on its own, but it made me notice how much of my “simple” infrastructure was actually a chain of external companies who could each independently decide to change terms on me with short notice. A form handler is maybe forty lines of code with a mail library behind it. Running that myself means the only roadmap I depend on is my own.

Image processing moved because of latency, honestly, more than principle. I had a SaaS image resizing/optimization service in a pipeline for one of the client projects, and it added a genuinely noticeable round-trip on every upload — fine for occasional use, annoying for a workflow that processes images constantly. Running the same processing with a library directly on the VPS cut that round-trip out entirely. This one’s the closest to a pure performance decision rather than a philosophical one, but it fits the same pattern: a task simple enough to run myself was going through a network hop to a third party for no reason except that it was easy to wire up that way originally.

What I’m not doing: treating self-hosting as a universal answer. I still use plenty of SaaS — email, domain registration, the VPS provider itself is technically someone else’s infrastructure, obviously. The pattern isn’t “avoid all third parties,” it’s noticing which dependencies are genuinely load-bearing for something I care about (privacy, reliability, not being at the mercy of a pricing page) versus which ones are just convenient defaults nobody ever revisited. Every one of the four moves above started with a specific, concrete annoyance — not a general ideology about self-hosting being morally better. I don’t think it is, automatically. A SaaS tool run by people who actually specialize in that one thing is often more reliable than my own version of it, and I’d be lying if I said my uptime checker has better uptime than a dedicated monitoring company’s.

The maintenance cost is real and I don’t want to undersell it. Every one of these moves means I’m now the person who patches the server, watches disk usage, and gets paged (by my own monitoring, no less) when something breaks at 2am instead of a SaaS company’s on-call team handling it invisibly. That’s a genuine tradeoff, not a free win. What made it worth it for me specifically is that none of these four services are complex enough to be a real operational burden once they’re set up — they’re small, boring, single-purpose processes running next to a static site that was already there. If I were self-hosting something with real complexity, a database-backed app with actual uptime requirements for other people, I’d think about this completely differently. The math only works because the things I moved were simple enough that “I run this myself” doesn’t cost much more of my time than “I configure someone else’s dashboard” already did.

The actual lesson, if there is one: every time something on this list broke a promise — silent alert failure, a pricing change with short notice, an unnecessary latency hit — that was the moment worth asking “could I just run this myself.” Not before, out of pure preference, and not as a blanket policy. Just when a dependency actually shows you its failure mode, that’s usually the best time to decide whether you’re okay depending on it going forward.