
Why I Rolled My Own Blog (and Shipped It in a Weekend)
I wanted a blog on my site, not bolted on the side. I didn’t want another dashboard to babysit or a plugin carnival. I wanted something boring, reliable, and mine.
Hosted options I tried (and skipped)
Blogger.com — I’ve used it. It works. But it feels like borrowing a minivan to run laps at a track day. Not my speed for a dev-centric site.
WordPress — No thanks. Lots of reasons; none I care to re-litigate here. If you love it, I’m happy for you.
Ghost — Clean and fast. I liked 95%. The 5% I didn’t like was loud: their branding popping up in verification emails. Yes, you can patch it… until the next update un-patches it. I want my brand to be the default, not a weekend chore.
So: close, but not what I wanted. I rolled my own.
Pre-AI me probably wouldn’t have finished
Before AI got useful, I’d have quit halfway. Authorization has historically punched me in the face and then charged rent. Motivation dies right there.
What changed: AI + a clear plan
I asked ChatGPT to poke holes in “build a simple blog inside my site”:
Verdict: not that hard if I keep it boring and straightforward.
Clerk for auth — I already used it in two apps. Fewer surprises.
It helped me write prompt files for GitHub Copilot, so Copilot generated code that fits my stack instead of wandering off into React-land.
That combo gave me momentum. No framework war. Just a checklist and shove.
The build (one weekend, minimal drama)
I kept the stack simple and let the site’s existing bones do the work:
Nice UI, my way. I built a clean admin UI that fits my site’s look. It has tags, drafts, and scheduled posts out of the box. (I’m going to use a screenshot of that UI—this article selected—as the header image for this post. Because yes, I built the thing and I’m going to show it.)
Markdown workflow, database-backed. I write in Markdown and paste it into my UI. The system stores the Markdown in the database and pre-renders it to HTML and stores that too. Why both? Because I like diff-friendly Markdown for edits, and I like fast, no-thinking HTML for serving. Zero plugin roulette.
Vanilla HTML/CSS/JS for the frontend. No React/Vite here. The blog pages drop into my existing template and ship fast.
.NET backend with minimal endpoints: list posts, fetch one, draft/publish/schedule, and protect the admin bits behind Clerk.
Looks like it belongs. Same layout, same typography, same deploy. It’s part of the site, not a stranger with a subdomain.
Where I face-planted (so you don’t have to)
Auth mismatch
I prompted like I was building another React/Vite app. I wasn’t. I shipped vanilla HTML/JS. Some Clerk wiring assumed SPA patterns, and I had to unwind it. Fixable, but annoying.Program.cs
got thicc
I let the main file balloon. I tossed routes and wiring into one place “just to get it working.” It worked… and now it’s a casserole. Future me will be refactoring with a stern look.I broke my own patterns
I didn’t follow the structure the rest of the site uses. I noticed after going live (of course). Nothing exploded, but it made refactors spicier than they needed to be.
Why not keep Ghost and patch it?
Because I don’t want to keep re-asserting my brand after every update, and I don’t want to wake up to “surprise, your email template changed.” With my own setup, when something goes sideways, it’s my code and my fix. Fewer variables. Less anxiety.
What this gives me
Ownership — Lives in my repo, my deploy, my domain. No plugin zoo.
Speed — Write Markdown → paste → save. HTML is already rendered and ready to serve.
Focus — No theme rabbit holes. I write and hit publish.
Brand control — From emails to meta tags, my name is the loudest one.
Takeaways (notes to future me)
Follow the existing structure. If I’m dropping big functionality into an app, use the app’s patterns. Consistency > clever.
Don’t skip steps. Test the Docker image locally before going live. “It should be fine” is famous last words.
Don’t be in a hurry. Shipping in a weekend is nice. Shipping right in a weekend is better.
Right-size auth to the UI. If it’s not an SPA, don’t prompt for SPA patterns.
Keep
Program.cs
skinny. Split routes, services, and features. My future sanity is worth a few extra files.
What’s next
Short term:
Clean up the backend. Break apart the chunky bits, introduce feature folders, and make the code read like a cookbook instead of a diary.
Subscriber updates. Add a way for people to get the latest posts: simple email notifications (digest or instant), and probably a dead-simple RSS/Atom feed for good measure.
Polish the UI. Tiny QoL—filters on tags, better draft previews, and a friendlier scheduler.
Medium term:
Search and tiny analytics. Find posts quickly and get a pulse on what people read.
Safer previews. Lock down draft previews behind one-time links so I can share WIP without opening the admin.
Final word
This post is running on the system I just described. No rented garden. No mystery updates. It’s my stack, my UI, my rules: .NET backend, vanilla frontend, Markdown in → Markdown & HTML stored in the DB, and a clean admin that doesn’t fight me.
If you keep postponing “just a simple blog,” here’s my advice: build the boring version that lives where your code lives. Ship it, then tidy it. And yes—**test the Docker image before you hit publish.bold