Building a Long-Term Personal Site Using Obsidian and MkDocs
In this millennium, I've probably built around a thousand versions of my personal website. That sounds ridiculous, but the pattern was very real. New stack, new architecture, new excitement, followed by the exact same result. I would launch, maintain the site for a while, get completely exhausted by the maintenance burden and... shut it down.
I have been coding for about eight years and every single rebuild reflected the shiny new tech I had just learned. That was part of the problem. My codebase lacked a stable center. As soon as I understood a "better" way to do something, I felt compelled to rewrite everything. I kept chasing technical perfection while the actual content, my writing, stayed underdeveloped.
Each version looked highly impressive in development but did almost nothing useful in production. Bills kept coming anyway. Sometimes, I even built custom CMS systems to fix the problems of my previous custom CMS, only to run into fresh complexity immediately. I simply had no clear definition of "done."
Why the Software Product Mindset Kept Failing
I was treating a personal writing website like a SaaS product that required continuous architectural evolution. What I actually needed was vastly simpler. I wanted a space where I could publish what I was working on, a few paragraphs at a time, without turning every single update into a massive platform decision.
The mismatch between my actions and my goals became obvious once I finally admitted it to myself. My core requirements were straightforward:
- Write quickly and frictionlessly
- Maintain absolute ownership of my content
- Publish with incredibly low overhead
- Avoid endless maintenance loops
This realization pointed directly toward one obvious solution: a file-first approach.
The Tooling Diversion
I tried countless apps and even forked open-source tools to build my own writing system. Full control sounded great on paper. In practice, it just became another unpaid maintenance job.
My custom setup was fragmented and inconsistent across Linux, macOS, Windows and Android. Syncing was fragile. As time passed, I found myself investing all my creative energy into infrastructure rather than actually writing. Part of this stemmed from trust issues around third-party platforms and data ownership. I wanted full control over my files and version history. That instinct was perfectly right, but my implementation was far too heavy.
Obsidian Became the Base
I eventually landed on Obsidian. Not because it's trendy in the productivity space, but because it provided exactly what I needed: local files, a highly flexible plugin model and speed.
I built a custom routine around Obsidian templates and shortcuts so I could spin up new drafts fast and keep them organized automatically. That seemingly small change altered my daily behavior. Writing felt easy again.
Then, one simple "aha!" moment connected the whole system:
If my notes are already local Markdown files, those exact files can become the website source.
Finding MkDocs Through Client Work
I actually stumbled upon MkDocs while working on client documentation. The requirement for the client was crystal clear: managers wanted basic text files that could be edited rapidly and published without a heavy, bloated platform.
That was exactly what I needed for my personal site.
At first, I didn't like MkDocs because I didn't fully grasp its paradigm. But after spending some time with it the mental model clicked perfectly:
- One project folder
- Markdown files as the single source of truth
- Static HTML as the final output
- A highly predictable build process
Zero client-side app layer was required just to read basic text content.
Building Quartz: A Theme for Writers
The default MkDocs themes didn't match my aesthetic and most community themes looked exactly like what they were: API documentation portals. So, I built my own theme and named it Quartz.
Quartz gave me a visual structure that felt like a cozy, personal writing space rather than a sterile docs site. I redesigned the navigation, kept the layout strictly centered and highly readable, and focused heavily on a mobile-first experience.
Later, I extracted the theme into its own Git repository and imported it as a submodule. This setup allows me to evolve UI components in one isolated place and reuse them across different projects if needed.
Custom Blocks Without Framework Debt
Despite embracing simplicity, I still wanted rich presentation. I work a lot with photography and music, so plain text alone isn't enough for every post. I needed reusable blocks for image galleries, grouped media and basic interactive elements.
What I didn't want was to return to a massive React pipeline just to render a gallery. I also refused to pollute my clean Markdown files with inline raw HTML.
Then I run into the, must say, the most elegant solution. Using markdown-custom-blocks in tandem with MkDocs.
I can define custom block syntax, map it to specific templates, and generate clean HTML during the build process. In my setup, blocks use the ::: syntax and automatically inject the HTML, CSS and vanilla JavaScript they require. This establishes a brilliant boundary:
- Markdown stays incredibly clean.
- UI Components remain highly reusable.
- JavaScript is restricted to targeted interactivity, rather than acting as the core rendering engine for the entire website.
Why Build-Time Output Matters
The biggest practical victory of this entire architecture is build-time rendering. Pages ship over the wire as actual content, not as empty shell placeholders waiting for a massive JavaScript bundle to hydrate.
This shift changed both how the site feels and how it operates:
- On the reader side: Content appears instantly. There is no waiting for a frontend runtime to piece together basic article text. The layout is remarkably stable because the page structure is fully resolved before it ever reaches the browser.
- On the developer side: There are virtually no production edge cases tied to runtime rendering. Debugging is drastically simpler because the generated HTML is explicit and static. There's absolutely zero pressure to audit and maintain a massive client-side dependency graph just to serve a text-driven blog.
I still use JavaScript where it matters. The crucial difference is the scope. JS now handles interaction, not page existence.
Decoupling and True Ownership
The deeper philosophy behind this setup is portability.
My content consists of plain text files tracked under Git. I keep the entire history. I keep the absolute source. I can swap out the Quartz theme, change the static site generator or move my hosting infrastructure entirely without ever needing to rewrite the content itself.
This setup removes platform lock-in and reduces migration risk in concrete, measurable ways:
- If my Quartz theme evolves, the Markdown corpus stays untouched.
- If MkDocs stops serving my needs in five years, I still possess clean source material with full version history.
- If I want to publish selected articles to another platform, I can do so directly from local files I own, rather than fighting a proprietary CMS export tool.
Decoupling isn't just an abstract architectural principle here. It is a practical, indisputable guarantee that my writing will outlive the current technology stack.
My 2026 Goal: Stop Overcomplicating
One of my explicit goals for 2026 was to fundamentally stop over-engineering my personal publishing process.
My workflow now features a clean, sharp divide: Obsidian is where I think, draft and edit. MkDocs is the engine that turns those drafts into public web pages.
In practice, this makes my writing loop "boring" in the best possible way. I open my Obsidian vault, draft ideas, refine the structure, and push to publish, without context-switching or battling mental models every single hour. I am no longer juggling server infrastructure while trying to string a sentence together.
- The Old Loop: Write a bit -> Notice a tooling limitation -> Start rebuilding the CMS -> Postpone publishing entirely.
- The Current Loop: Write -> Edit -> Publish.
That behavioral change is vastly more important than any framework migration I have ever completed.
What Simplicity Truly Means to Me
Earlier versions of my site were essentially technical showcases. They proved what I had recently learned, but they rarely supported what I was actually trying to accomplish: sharing my thoughts.
This iteration is different. Success is now measured by publishing consistency, not by implementation novelty. I have stopped treating the website as the final product, because the writing is the real product.
The system exists only to support the work. It should make writing easy, keep quality high, let you fully own your content, and stay simple to maintain.
If I can keep publishing useful posts without getting pulled into endless rebuild cycles, then this architecture is doing exactly what it was built to do.
It’s on me to stick with it and on you to wish me luck. hehe