Skip to content
Mike Wyatt

Why I Did a 180 on Cloud Providers

· #cloudflare #workers #infrastructure #self-hosting #edge

Three weeks ago I published a post about how I built the infrastructure for Hand of the King. The thesis was simple: I can do the hard part that everyone pays a service to handle badly. Security, CI, hardening, the boring ops work. I said I don’t use Vercel or managed PaaS layers — not because they’re bad, but because I want to own the parts that matter.

That was true. It still is, for some things. But I just moved this site from a Docker container on a $5 VPS to Cloudflare’s edge in under an hour, and I need to talk about what that felt like.


The Self-Hosting Stance

My setup was solid. Nginx in an unprivileged container, read-only root filesystem, non-root user, healthchecks, pinned to a commit SHA on GHCR. The VPS is hardened: SSH off port 22, key-only auth, Tailscale-only access, fail2ban, UFW default deny. I spent real time on this. It works.

But it’s a machine I have to maintain. Security patches, log rotation, disk space, SSL renewal, backup verification. The boring stuff done right is still boring stuff, and it compounds. Every service I add multiplies the surface area.

The mental model was: cloud providers abstract away the wrong things. They give you a dashboard and a bill and hide the complexity you should understand. I still believe that for some layers. But I was wrong about which layers.


The Cloudflare Discovery

It started with Containers. I was reading about Cloudflare’s container runtime — not Docker on their VMs, but actual containers deployed to their edge. That led me to Workers, then Pages, then the broader platform. What I found was different from what I expected.

Cloudflare isn’t abstracting Linux away from you. They’re giving you a different primitive: the edge itself. A V8 isolate that boots in 10ms. A KV store replicated to 300+ cities. A global network that was already handling 20% of the internet’s traffic before you showed up.

The insight I missed: there’s a difference between outsourcing your ops and using infrastructure that’s better than anything you could build. I can harden a VPS. I cannot build a network that routes around outages in 50ms. I can set up fail2ban. I cannot build a DDoS mitigation system that handles 200 Tbps. Those aren’t ops skills I should own. Those are physics and economics at a scale where the provider wins every time.


The Migration: mwyatt.me in 47 Minutes

Here’s what actually happened. I had the site on GitHub — Astro static build, Tailwind, content collections. The goal was: get it running on Cloudflare without the VPS.

Step 1: add the adapter.

pnpm add @astrojs/cloudflare

Step 2: change one line in the config.

// astro.config.mjs
output: "static",
adapter: cloudflare(),

That’s it. Astro builds, prerenders every route, and emits a _worker.js bundle.

Step 3: deploy.

wrangler pages deploy ./dist --project-name mwyatt-me

Upload took 2 seconds. The site was live on mwyatt-me.pages.dev before I could open a browser tab.

Total time: 47 minutes. Most of that was reading Wrangler docs and fixing a version mismatch between the Cloudflare adapter and Astro 5.x. The actual work was maybe 10 minutes.


What I Gave Up vs. What I Gained

Gave up:

Gained:

The tradeoff is real. I don’t have docker exec anymore. But for a static site — content that changes when I push to git, not when a user interacts — I was maintaining a Linux machine for no operational benefit.


The Containers Piece (The Real Hook)

Pages and Workers are the gateway drug. The part that made me do the full 180 is Cloudflare Containers.

Here’s the pitch: write a Dockerfile. Build it locally. Push to Cloudflare’s registry. They run it — not on a VM they provision, but on their edge, with the same network that handles their CDN traffic. Your container boots in a region close to the user, talks to KV or D1 or R2 through internal bindings, and you don’t manage the host.

This changes the calculus. My HOTK stack — Go API, Vue frontend, MCP server, Lethe — runs on Docker Compose on a VPS because I need persistent state (SQLite files), WebSocket connections, and a long-running process model. I’m not moving that tomorrow.

But the pattern is: stateful stuff where I need control, self-hosted. Stateless or edge-friendly stuff, Cloudflare. The blog doesn’t need a server. The auth service (Supabase Cloud) already made that call. The MCP server needs OAuth and persistent connections — that stays. But any new microservice I’m tempted to spin up on the VPS? I’m asking “could this be a Worker?” first.


The Tools Are in My Hands

What sold me wasn’t the dashboard. It was the CLI.

wrangler deploy          # push a Worker
wrangler tail            # stream logs
wrangler kv:key put ...  # write to KV
wrangler d1 execute ...  # run SQL on D1
wrangler r2 object put ... # upload to R2

Everything is version-controlled, scriptable, reviewable in git. No clicking through a web UI to create resources. No Terraform state files to manage. The wrangler.jsonc config lives in the repo next to the code. The deploy is wrangler deploy in CI, or from my terminal, or from an agent with scoped permissions.

This matters because the interface determines the culture. A platform with a great CLI and a config-as-code workflow attracts the same kind of users who self-host: people who want to understand what they’re running, automate it, and roll it back when it breaks. Cloudflare’s tooling culture is closer to my mental model than AWS’s console-first approach.


Why This Isn’t a Contradiction

I’m not abandoning self-hosting. The HOTK VPS isn’t going anywhere. Lethe runs on my Mac mini because it needs local SQLite and I want the data physically close. WAGMIOS manages my homelab because that’s the whole point of a homelab.

But I’m no longer ideologically committed to running every service myself. The principle was never “self-host everything.” The principle was “own the parts that matter, and don’t pay a premium for abstraction you don’t need.” For a static blog, the VPS is abstraction I don’t need — it’s a machine pretending to be a CDN. Cloudflare is a CDN. Using them for edge delivery isn’t outsourcing; it’s using the right tool.

The 180 wasn’t about capability. It was about humility. I can harden a server. I cannot build a better global network than Cloudflare has already built. The question isn’t whether I’m skilled enough to self-host. It’s whether self-hosting adds value for this specific workload. For mwyatt.me, it didn’t.


What’s Next

  1. Custom domain. Point mwyatt.me at the Pages project. SSL is automatic.
  2. Preview deployments. Every PR gets its own URL — already works, I just need to wire the GitHub integration.
  3. KV for dynamic data. Experimenting with Cloudflare KV for a lightweight “now” page or status widget that doesn’t need a full CMS.
  4. HOTK edge experiments. The MCP discovery endpoint (/.well-known/oauth-protected-resource) is stateless and cacheable. That might be a Worker sooner than the rest of the stack.

The Code

If you’re self-hosting everything, ask yourself: are you owning the complexity, or just maintaining it? There’s a difference.


← Back to posts