Systems Engineering
Building a Git-First Developer Blog on Cloudflare
A practical architecture for publishing verified technical writing with Astro, Git, Cloudflare Pages, and an intentionally small runtime.
- Published
- Reading time
- 2 minutes
- Last verified
A technical blog is easy to launch and surprisingly hard to keep trustworthy. The difficult part is not rendering Markdown. It is preserving a chain of evidence from an edited file to the page a reader sees.
xiaoke.dev uses a deliberately small publishing architecture: Git owns history, Astro turns validated Markdown into static HTML, Cloudflare Pages serves the build, and R2 keeps one current off-site mirror of the source.
The source of truth
Git is the only version history. Every published article belongs to a commit, and every correction or rollback creates another commit. This gives the content the same useful properties as source code:
- changes are reviewable;
- releases are attributable;
- old versions are recoverable;
- a rollback does not rewrite shared history.
R2 has a different job. The private source bucket contains only the latest Markdown mirror. It is recovery insurance, not a second history system.
Validate before rendering
The content pipeline rejects invalid metadata before Astro creates a page. A post needs a unique slug, a useful description, an allowed category, registered tags, and dates that make chronological sense.
title: "Building a Git-First Developer Blog on Cloudflare"
slug: "building-a-git-first-developer-blog"
publishedAt: 2026-08-10
draft: false
category: "systems-engineering"
Cross-document checks matter too. A schema can validate one post, but a release check must also detect duplicate slugs, redirect collisions, missing local images, and internal links that no longer resolve.
Keep the runtime small
Every public page is generated at build time. There is no content database, server-side rendering layer, or public content API. That choice improves more than performance:
- The deployed artifact is inspectable.
- A failed build cannot partially update production.
- Search crawlers receive complete HTML.
- Operational work stays focused on content and release quality.
The browser receives JavaScript only where an interaction needs it. Copying a code block is a good use; rendering article text is not.
Treat deployment as a release
The release script runs validation, builds the static site, mirrors source Markdown to private R2, uploads the prebuilt dist/ directory to Cloudflare Pages, and finally checks production.
pnpm run build
pnpm run sync:source
pnpm exec wrangler pages deploy dist
BASE_URL=https://xiaoke.dev pnpm run verify
The production checks cover the home page, blog index, a real article, RSS, Sitemap, redirects, canonical metadata, and representative asset headers. A deployment is finished only when readers and crawlers can use it.
What this architecture optimizes
This system does not optimize for editing from a phone or publishing hundreds of generated pages. It optimizes for a smaller number of technical articles that remain reproducible, searchable, and maintainable for years.
That is the right trade when the content is meant to establish technical trust rather than fill a feed.