How Ghost helped me build a self-hosted blog

How Ghost helped me build a self-hosted blog
This is the final part of a trilogy. In the previous parts, I talked about why I chose self-hosting and what I am using to host this blog.

This post was written to the sound of house music. Here’s the playlist:

When you want to self-host a blog, there are many different options available. You could build something from scratch with basic HTML or a JavaScript framework, use a traditional CMS like WordPress, or go with a static site generator like Hugo, which several of my acquaintances have chosen. I enjoy self-hosting and configuring things myself, but building from scratch isn't for me. Besides, why reinvent the wheel when solid solutions already exist? This is the approach I bring to my day job as well, but more on that in a future post.

What my friends think I do as a web developer

I wanted to build a personal blog for a while. I made several attempts, but none lasted beyond the initial "Hi, this is my blog" post. For current iteration, I considered WordPress since I used it before and Namecheap offers basic hosting for it. But around that time, I discovered Ghost - an open-source CMS that balances configurability with out-of-the-box usability. The built-in newsletter functionality was particularly appealing since it meant keeping everything in one place instead of relying on an external email solution. Ghost also offers built-in themes, community themes, and the option to build custom ones. Maybe someday I'll try creating my own custom theme and document the experience.

Hackerman meme about building a custom theme

Ghost runs on my Mini PC as a simple Kubernetes pod with a database for storing content. At this point, you might be wondering about security:

How safe is it to host a publicly accessible blog at home?

I had the same concern while planning the setup. I didn't want to open ports on my home network since that creates potential vulnerabilities. That's when I discovered Cloudflare Tunnels - a reverse proxy that exposes specific Kubernetes service endpoints through Cloudflare without opening any ports. This was exactly what I needed: precise, controlled exposure of a single app. Since Cloudflare also handles domain registration, I could keep all website and access configurations in one place. They offer additional security and authentication options too, which is useful for admin tools that need controlled access.

Homer Simpson sleeping peacefully with caption of "How I sleep after obsessing over security details"

This is how I deploy and serve my blog - a local Ghost deployment proxied through Cloudflare. I'm curious to see how far this setup will scale and how it performs under real-world conditions, but that's something I'll be observing and sharing over time as the blog grows.

For now, I'm happy with what I've built. The setup gives me the functionality I need and the flexibility to experiment, which is exactly what I was after. I'll continue developing and refining it, adding new apps and tools as I go, and you can expect future posts documenting those additions and the lessons learned along the way.

Thank you for reading this initial trilogy. I hope you've found it helpful or at least interesting to follow along with my process. There's plenty more content coming - from deep dives into specific technical challenges to reflections on what's working (and what isn't).

If you missed the previous parts, you can read them here:
➡️ Why I decided to make a self-hosted blog
➡️ What I am using to host this blog

Subscribe to the newsletter if you want to catch future posts, and feel free to drop any questions or topic requests in the comments. Your suggestions might just inspire a future article.