mert574 ~ %

$ cat hosting-this-blog-on-my-homelab.md

Hosting this blog on my homelab

How this site gets built and served from my own cluster, as a static Astro build in object storage.

#infra#astro#homelab

A terminal window showing a git push flowing through the pipeline: the k3s runner syncs to the Garage bucket, which reaches the internet through a Cloudflare tunnel

This site is a set of static files in an object storage bucket on a machine at home, served through a Cloudflare tunnel. Here’s how it fits together and why I set it up this way.

The pipeline

I push to main. A GitHub Actions runner on my k3s cluster picks up the job, builds the site with Astro, and syncs the output to a Garage bucket. Garage serves that bucket as a website, and the tunnel points mert574.dev at it.

Terminal window
aws s3 sync dist/ s3://mert574.dev/ \
--endpoint-url http://garage.internal:3900 \
--delete

The bucket is both the deploy target and the web server. A deploy like this isn’t atomic: sync uploads one file at a time, so someone loading the site mid-deploy could get HTML that points at an asset that isn’t up yet. But the whole upload takes a couple of seconds and Astro hashes the asset filenames anyway, so I’ve decided not to care. If it ever matters, the fix is to upload each build under its own prefix and switch over at the end.

Why the runner runs at home

Garage runs in its own container on the Proxmox host, outside k3s, and is only reachable from inside the network, so the runner has to be inside too if it wants to push the build. I could route the S3 endpoint through the tunnel, but then a whole storage API sits on the internet so that one deploy job can upload files. I’d rather not. A runner that’s already on the LAN gets me the same thing with nothing exposed. (It also sidesteps Cloudflare’s upload body-size cap on the free plan.)

Why object storage

I didn’t want a server to babysit. A static build in a bucket is cheap to serve and easy to reason about, and the backup is just the git repo plus whatever’s in the bucket. Garage gives me S3-compatible storage and static website hosting in the same box, which is the whole job.

What’s still rough

The Kubernetes side of the cluster is already GitOps through Argo CD, but the NixOS hosts still need a manual apply when I change their config. Closing that gap is the next thing I want to sort out.

← all writing