On this page
This site is a static Astro build. The words live in a headless CMS, but readers never touch it: they get plain files from nginx (or from a mirror in object storage when the homelab is down).
The shape of it
Publishing in the CMS triggers a build. The build produces a dist/ folder, uploads it as a numbered release, and moves a pointer. The pods notice the new pointer and swap their content without a rollout.
Diagram source
direction: right
author: You { shape: person }
cms: Payload CMS
ci: GitHub Actions
r2: Object storage { shape: cylinder }
pods: nginx pods
author -> cms: publish
cms -> ci: dispatch
ci -> r2: upload release
pods -> r2: poll LATESTHow releases are laid out:
- Static output, one folder per release
- Pointer file names the live release
- Rollback rewrites the pointer
- Content-sync sidecar pulls the new release
Where the site build stands:
- Done:Type check clean
- Done:Pagination tried with a small page size
- To do:First real post written
Why not just render pages from the CMS?
- Availability: a static file keeps working when the database does not.
- Cost: no application runtime on the read path.
- Security: the CMS is only reachable behind an access gate.
Note
A static site on a single-house homelab is strictly less available than a hosted static platform. The point of running it here is to learn, which is why a failover mirror exists.
Typed content, end to end
The schema is code. Types generated from it are imported by the site, so a schema change that breaks a page fails the type check before it fails a reader.
import type { Post } from '~/lib/types';
export function hasPage(post: Post, locale: Locale): boolean { return post._status === 'published' && post.publishedIn[locale] === true;}Building it
A local build is two commands:
pnpm install --frozen-lockfilepnpm buildi18n: 96 keys present in all locales [build] 24 page(s) built in 3.1s [build] Complete!
Published URLs are permanent. Changing a slug without a redirect is a bug.
More in the next part, where the CMS goes into the cluster. See the homelab write-up for the platform.
An interactive demo
Posts can include small interactive pieces written as plain HTML and JavaScript. They run in a sandboxed frame, so they cannot touch the site around them or send anything anywhere.
