How this blog is built

 · 1 min read

A few people asked what’s under this site, so here’s the whole stack. The goals were simple: free hosting, custom domain, zero servers, Markdown in / Markdown out, and code blocks that don’t look like 2012.

The stack

  • Astro — the static site generator. Posts are Markdown (or MDX when I need a React-less component). Astro 7’s content layer with a zod schema means a typo’d date fails the build instead of silently breaking the site.
  • Astro’s built-in Shiki — for the code blocks. Same highlighting engine as VS Code, with a light/dark dual theme that swaps with the site toggle:
    import { glob } from 'astro/loaders';
    
    const blog = defineCollection({
      loader: glob({ base: './src/content/blog', pattern: '**/*.{md,mdx}' }),
    });
  • Cloudflare Workers Static Assets — the host. A pure static site needs no Worker script and no SSR adapter; the entire wrangler.jsonc is four lines:
    {
      "name": "personal-blog",
      "compatibility_date": "2026-07-25",
      "assets": { "directory": "./dist/" }
    }
  • GitHub Actions with cloudflare/wrangler-action@v4 — every push to main builds and deploys. Cloudflare serves unlimited static requests on the free tier, so a popular post costs me $0 in bandwidth.

Why this over, say, Ghost or Medium

Because it’s plain text in a git repo. I edit a Markdown file, push, and a minute later it’s live. No database, no CMS login page, no renewing a $200/yr newsletter tool. readers get an RSS feed; I get my words back whenever I want.

There’s a longer note on the drafting workflow — editor, images, diagrams — coming next. For now, this is the “show your work” version of the site itself.