8 min read

I Took Self-Hosting Too Far

Table of Contents

A little over a week ago I wrote Self-Hosting My Repositories and Sites. The conclusion was straightforward: if I was going to self-host the repositories, I should self-host the sites they produce as well.

I have been working on that setup since, and I think I took the argument too far — though not the repository part. I still want the working repositories, CI, releases, and the rest of the development workflow under infrastructure I control, and I still think owning the URL is one of the most important parts of making any of this portable.

The part I got wrong was treating the sites themselves as if they needed the same level of sovereignty. They do not.

I Took Self-Hosting Too Far

What Has Not Changed

The original decision started with the repositories, and nothing I have learned since changes that reasoning.

I want the canonical repository to live at an address I control, the CI pipeline attached to infrastructure I control, and releases produced without depending on GitHub, GitLab, Cloudflare, or another platform also being the place where the source repository has to live. That separation still matters: it means I can change the infrastructure behind the URL without changing the identity of the repository, and that the pipeline belongs to the project rather than to whichever SaaS platform happens to run it today.

The same principle applies to the public sites — each domain is mine, and where the files behind that address happen to be served from is implementation detail.

That distinction is where my earlier reasoning went wrong.

Where I Went Too Far

In the previous post I described self-hosting the sites as finishing the sovereignty argument: the repository would run on infrastructure I control, the pipeline would run there, and the resulting static sites would be deployed to a static server I operate, with a CDN in front of it.

It is coherent, but it is also unnecessary.

A static site is an artifact. Astro builds a directory of files and something needs to serve them over HTTP. I can operate that something myself, but doing so means maintaining another piece of infrastructure — deployment, availability, TLS, caching, monitoring, updates, backups where necessary, and whatever else accumulates around keeping a public endpoint healthy — and Cloudflare already does all of that extremely well.

More importantly, using Cloudflare to serve the result does not require giving Cloudflare control over the process that produces it. That is the part I underestimated.

Wrangler Was Never the Missing Piece

The previous post already knew that Wrangler decouples the repository from Cloudflare — that only the deploy target needs the API token, not the source. That was part of the case for Cloudflare Pages back then, and I chose self-hosting anyway, because finishing the sovereignty argument outweighed it at the time.

What changed is not the fact about Wrangler, but how I weighed it against the cost of running a static file server myself.

If deploying to Cloudflare meant putting the repository on Cloudflare, connecting their build system to it, and making their platform responsible for turning a commit into a website, I would still be much less comfortable with the dependency — but that is not how I need to use it.

Wrangler gives the pipeline a deployment target: the repository can remain on my Forgejo instance, CI can run on infrastructure I control, the build can happen there, and when it succeeds, Wrangler takes the finished output and publishes it to Cloudflare.

That makes Cloudflare the serving platform, not the development platform. The flow becomes:

Repository → my CI → build → Wrangler → Cloudflare

Cloudflare does not need to own the repository or orchestrate the pipeline — it receives the artifact at the end, which is a much narrower dependency, and one I am comfortable making.

Control Where It Matters

I think the mistake was treating self-hosting as a goal rather than as a tool.

There are parts of the stack where control has architectural consequences. The domain is one: if I own the domain, I control where people find the project. The repository is another — it contains the history, issues, releases, and increasingly the workflow around producing the software. The pipeline matters for similar reasons: I do not want the ability to build and release my work tied unnecessarily to the repository hosting model of a third party.

For the server returning a static HTML file, though, I am struggling to find the equivalent argument. If Cloudflare disappeared tomorrow, the repository would still exist, the pipeline would still work, and the site would still build — I would just need to change the final deployment step and point DNS somewhere else. That is exactly the kind of dependency I want: replaceable. The important thing is not that I operate every layer, but that I retain the ability to replace the layers I do not operate.

The Operational Cost Matters Too

There is also a more mundane reason for changing course: time.

We are a few people, and every service we operate is something we need to understand, configure, monitor, update, troubleshoot, and eventually migrate or replace.

Sometimes that cost is justified — self-hosting Forgejo gives me something I actually want: control over the canonical repository and the workflow around it — but self-hosting static files mostly gives me another server to maintain.

Cloudflare gives me a global edge network, caching, TLS, DDoS protection, and a deployment target that already fits neatly into the pipeline I am building.

Trying to reproduce enough of that infrastructure myself so that I can say the site is self-hosted is optimizing for the wrong thing.

The goal is not to own infrastructure — it is to own the things that would be painful to lose.

A Better Boundary

So the architecture changes: the Forgejo instances, the repositories, and the CI and release pipelines stay under my control, and the domains stay independent. The static sites go to Cloudflare, with Wrangler as the boundary between the two.

That actually gives me a cleaner division than the one I described in the previous post. Everything involved in creating the artifact remains under my control, while the commodity infrastructure involved in distributing that artifact is delegated to a provider that is very good at it.

And because the deployment target sits behind a domain I own and at the end of a pipeline I control, replacing Cloudflare later remains straightforward — I do not need to prepare for that replacement by operating the alternative today.

Changing My Mind Is Part of the Process

One reason I write these posts while I am making decisions rather than after everything is finished is that I want them to capture the reasoning as it actually happens.

Sometimes that means the next post says the previous one was wrong, and this is one of those times.

I still believe in the sovereignty argument behind self-hosting my repositories, that the canonical URLs should belong to me, and that pipelines should not depend unnecessarily on the repository hosting or deployment model of a SaaS provider. I just no longer think those principles imply that I should serve every static file myself — the pendulum swung too far.

The better principle is simpler:

Own what needs to be durable. Control what needs to be portable. Rent what is genuinely replaceable.

For my sites, Cloudflare falls firmly into that last category.

So I am keeping the repositories and pipelines at home, while the sites go back to the edge.


💬

Drawn your own sovereignty line somewhere between self-hosting everything and renting everything? Leave a comment below, or reach out through another channel if you would rather keep the conversation private. Follow the RSS feed for more.