
4 min read
For the last few years, my blog ran on a self-hosted Ghost instance inside my homelab. It worked, it was familiar, and the editor was good. I also liked that it ran entirely on my own infrastructure.
Eventually, the upkeep stopped feeling worthwhile. Ghost needed a MySQL database, a Node.js runtime, backups, security patches, and regular attention when a new release or CVE appeared. The server kept running and drawing power whether anyone was reading the blog or not.
I decided the blog did not need to be another service I had to maintain.
The migration
I moved the site to Astro 7, deployed it on Cloudflare Pages, and put the source and content in a GitHub repository. The change was easier than I expected, and the result fits the way I want to manage a personal blog.
Why Astro
Astro builds the site as static files by default. There is no application server or database behind each page request. The posts are Markdown files with frontmatter, and Astro’s content collections validate that frontmatter against a schema.
The output is small and fast. Astro sends little or no client-side JavaScript unless a component needs it, and it works well with Cloudflare’s deployment tooling.
Why Cloudflare Pages
I already use Cloudflare for DNS, CDN, and WAF services, so Pages was a natural place to host the new site. It gives the blog global CDN delivery, automatic HTTPS, deployment history, and rollbacks without adding another bill for this use case.
Cloudflare Pages also watches the GitHub repository. When a change reaches main, it builds and deploys the site automatically.
Why GitHub
GitHub gives me version history, pull requests, and a record of every change to the site. The posts live beside the code instead of inside a database, so I can review a content change the same way I review any other change.
What changed for the better
The Ghost container and MySQL database are gone, along with their backups and patching work. Readers now get static HTML from Cloudflare’s CDN without waiting for a server to render a page or query a database.
Keeping content in Git also makes changes easy to inspect and reverse. The public blog has no database or Ghost login page, which reduces the amount of software exposed to the internet. I can also use the homelab resources that Ghost consumed for something else, and one less always-on service means a little less power use.
What took some adjustment
At first, moving away from Ghost also meant moving away from its visual editor. I wrote posts in Obsidian, committed the Markdown files to GitHub, and let Cloudflare publish them. That worked, but it put several Git steps between writing and publishing.
I have since added Keystatic Cloud as another editing path. It gives me a browser-based editor while keeping the posts in the GitHub repository and preserving the pull-request workflow. The public site is still static, and Cloudflare Pages still handles the final build and deployment. The Keystatic setup deserves its own follow-up post.
Changes are not instant because the site still has to rebuild. In practice, that wait is about 30 seconds. I also have not settled on a replacement for Ghost’s built-in comments. Most discussion already happens elsewhere, so comments are not a high priority for me right now.
The publishing workflow
The original workflow was:
Write in Obsidian -> Commit to GitHub -> Cloudflare Pages builds -> Site is liveKeystatic now provides a second route for writing and editing. Both routes end in GitHub, where I can review the change before Cloudflare Pages publishes it. A merged change usually reaches the live site in under a minute, without an SSH session or database migration.
Closing thoughts
Self-hosting Ghost taught me a lot, and I still self-host plenty of other services. For this blog, though, the maintenance did not buy me enough. The site mostly needs to serve Markdown content quickly and reliably.
The content is in Git, the pages load faster, and my homelab has one less service to maintain. That is the trade I wanted.
