Setting Up a Boring Markdown Tech Blog in 2025
I'm sharing how I'm creating my blog while I'm creating my blog.
I'm using MkDocs and Material for MkDocs as my static site builder. I know I'll look like a corporate drone, but I appreciate how easy and full-featured it is for code examples, diagrams, and such. I used it for my Python library WordSiv and it was enjoyable to use.
I'm using uv to isolate
mkdocs-material
dependencies, so we'll dip our toes in managing Python versions
and packages.
Before Getting Started
I want to document some things I do in a project like this, which I'd normally skip over. I hope it'll benefit you!
Making Cursor Less Annoying
I'm editing this markdown file in Cursor, it's great! However, LLM autocomplete is annoying for markdown. How would it know what I want to write before I write it? I think when I'm writing, and the autocomplete clouds my thinking with gobbledygook. So type ⌘ + shift + p, select "Preferences: Open User Settings (JSON)", and add this:
Couldn't I just use VS Code for this? Sure, but it's nice to have an LLM at your disposal for later proofreading, and mindless content rearranging and stuff.
Wrapping Markdown
As a programmer, we're afraid of being judged by our source code, so we'll want our Markdown lines to wrap at a certain line length. Likely you're reading on the rendered HTML version of this blog, but hey, I'll install Rewrap so we can select text and alt + q to wrap lines. I mean, it's kinda nicer to read in the editor too as I type. Why? We aren't used to reading too many words on a line1.
Bored yet? Mission accomplished, this is a boring blog post about a boring tech blog.
Init the Git
You know this!
I won't get into Git usage much, but I'm going to "commit early, commit often," especially with this filetech-blog.md
, so I don't lose any writing! I will
mention, I use oh my zsh for git aliases, because who wants
to type?
Managing CPython and Package Versions
This part historically has been a chore, but I think it's getting better/easier.
My last project I used mise-en-place and
Poetry which I enjoyed, but I just tried
uv and it can replace both of these tools for
Python (mise
's thing is that it works across Python, NodeJS, Ruby, etc.).
Install uv, and do this:
# install latest CPython. This is fast since Astral (creators of uv) build Python
# binaries for your system (you can install PyPy binaries too which is cool!)
uv python install
# init project (creates pyproject.toml, .python-version, hello.py)
uv init
I now have a .python-version
file which specifies this project uses Python
3.13 (also what pyenv does). Let's try Python
3.13:
If we're Pythonistas and "explicit is better than implicit", I think this is
definitely more explicit than mise
's approach of taking over the python
command for the current working directory. And I previously had to install some
plugin to get mise
and poetry
to work together. No more!
Actually Getting Started
Installing MkDocs and Material for MkDocs w/ uv
All we need to do is:
This created a Virtual Environment in .venv
(more editor-friendly than
Poetry's default of installing venvs elsewhere!), and installed
mkdocs-material
, which required mkdocs
, so we're all good to blog!
Bootstrapping the Blog
I'm following the Creating Your Site guide at the MkDocs material site:
Open mkdocs.yml
and udpate it with this:
site_name: Chris Pauley's Technical Blog
site_url: https://chrispauley.dev/blog
theme:
name: material
Then I should be able to serve my blog locally to preview it!
Check it out at http://localhost:8000
!
Configuring/Structuring the Blog
Ok, "it's not really a blog", you say. Yet! We need to to set up mkdocs.yml
for a
blog (as
well as footnotes, code highlighting, code copy, mermaidjs, etc). Here's what I
ended up with:
site_name: Chris Pauley's Technical Blog
site_url: https://chrispauley.dev
theme:
name: material
features:
- content.code.copy
palette:
primary: black
plugins:
- blog:
blog_dir: .
markdown_extensions:
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
- footnotes
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.snippets
- pymdownx.superfences
I put tech-blog.md
in docs/posts/
, so the directory structure of docs/
now
looks like:
Then I added the date metadata to the top of tech-blog.md
. See more metadata
options here.
If you check out the site being served locally, it should look very blogesque now!
Diagrams for Days
People in the corporate world might not take you seriously without diagrams, so I prompted ChatGPT o1 with the following prompt (with some back and forth to fix it):
Here is what it gave me:
flowchart TB
A((Have an Idea)) --> B["Start Setting Up a Blog"]
B --> C["Realize You're Writing About Making a Blog"]
C --> D["Feel It's Getting Repetitive and Boring"]
D --> E["Decide to Write a Post About How Boring It Is"]
E --> F["Readers Notice It's Boring"]
F --> G["Lose Motivation Because It's Actually Boring)"]
G --> H["Ironic Twist: Continue Blogging About Boredom"]
H --> A((Loop Back to Another 'Brilliant' Idea))
We can render this per the mkdocs-material instructions:
flowchart TB
A((Have an Idea)) --> B["Start Setting Up a Blog"]
B --> C["Realize You're Writing About Making a Blog"]
C --> D["Feel It's Getting Repetitive and Boring"]
D --> E["Decide to Write a Post About How Boring It Is"]
E --> F["Readers Notice It's Boring"]
F --> G["Lose Motivation Because It's Actually Boring)"]
G --> H["Ironic Twist: Continue Blogging About Boredom"]
H --> A((Loop Back to Another 'Brilliant' Idea))
Mermaid is set up correctly! Diagram City.
Publish It
Ok, it's getting tiresome blogging about setting up my blog while I'm setting up my blog. Let's publish it!
First I just need to set up my domain. I had tallpauley.com
, but it
transferred to Squarespace when Google Domains wound down, and both updating the
DNS records there and trying to transfer out are nightmarishly
slow. So let's grab chrispauley.dev
at
Porkbun. Then I just:
- Followed this GitHub guide to verify ownership of my domain
- Followed this GitHub guide to set up my custom domain2
I remembered from publishing wordsiv.com that I need a docs/CNAME
file:
Let's use Github Actions to deploy it. I showed you uv
, but I'm kinda over
this blog post so let's just paste this GitHub Actions
Workflow (uses pip
) into .github/workflows/ci.yml
and
push to tallpauley/blog
3:
And just like that (hours later from when I started 😂), chrispauley.dev is a go! 🎉
-
Definitely check out Butterick's Practical Typography! Great resource. ↩
-
Actually even easier through Porkbun. I'm not sponsored in any way by them. ↩
-
In my case I needed to manually select
gh-pages
as the pages source branch ↩