Skip to content

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:

"cursor.cpp.disabledLanguages": [
    "markdown"
]

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!

mkdir tallpauley-blog
cd tallpauley-blog
git init
I won't get into Git usage much, but I'm going to "commit early, commit often," especially with this file tech-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:

uv run python

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:

uv add mkdocs-material

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:

uv run mkdocs new .

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!

uv run mkdocs serve

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:

docs/
  index.md (empty file)
  posts/
     tech-blog.md

Then I added the date metadata to the top of tech-blog.md. See more metadata options here.

---
date: 2025-01-28
---

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):

Draw a mermaid JS diagram that shows how boring blogging about making a blog is

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:

  1. Followed this GitHub guide to verify ownership of my domain
  2. Followed this GitHub guide to set up my custom domain2

I remembered from publishing wordsiv.com that I need a docs/CNAME file:

echo chrispauley.dev > docs/CNAME

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/blog3:

git add origin git@github.com:tallpauley/blog.git
git push --set-upstream origin main

And just like that (hours later from when I started 😂), chrispauley.dev is a go! 🎉


  1. Definitely check out Butterick's Practical Typography! Great resource. 

  2. Actually even easier through Porkbun. I'm not sponsored in any way by them. 

  3. In my case I needed to manually select gh-pages as the pages source branch