
This site: Astro on a static host
A static blog built with Astro, deployed straight from Git on every push, with no database and no server to maintain.
This site is a project too, and the smallest of them all: a handful of Markdown files, a static site generator, and a deploy step that nobody has to think about.
The constraints
- No server to maintain. There’s enough infrastructure in my life already.
- Write in Markdown, in a text editor, versioned alongside everything else.
- Zero cost, hosting included.
- Fast by default: no client framework, no unnecessary JavaScript.
A static site behind a CDN ticks all four boxes. The repository already exists, the build is one command, and the host takes it from there.
How it’s built
Astro generates static pages and only ships JavaScript to the browser when you explicitly ask for it. Here that’s a single script for the light/dark toggle. The rest is HTML and CSS.
Content goes through content collections: every post is a Markdown file whose frontmatter is validated by a Zod schema at build time.
const baseSchema = z.strictObject({
title: z.string().min(1).max(120),
description: z.string().min(1).max(300),
pubDate: z.coerce.date(),
updatedDate: z.coerce.date().optional(),
tags: z.array(z.string().min(1)).optional(),
draft: z.boolean().default(false),
});
strictObject is the detail that makes the whole thing reliable: a misspelled
frontmatter key fails the build instead of being silently ignored. Typos get
caught at build time, not while re-reading the site three weeks later.
Deployment
There is no pipeline to maintain. The host watches the repository, runs
npm ci && npm run build, and serves dist/ from its CDN:
npm ci
npm run build # astro check, then astro build
# dist/ is the whole site
A git push to main is enough, and the site is live a minute later. Pull
requests get their own preview URL, which turns “does this render correctly?”
into something you can answer before merging rather than after.
What I might change
Client-side search, if the number of posts ever justifies it. And per-post Open Graph images generated at build time. For now a static image does the job, and a feature you don’t write is a feature you don’t maintain.