my-old-man: Adventures in using mdoc(7) as the markup for this site

Published: December 30, 2020

Update: As of December 2021, I've moved to a new markup format. For a glimpse at what the format discussed looks like, here's the original post.

Uh oh,
looks like,
I'm seeing more of my old man(1) in me
    ~ Mac DeMarco (added (1) by me)


While Mac wasn't talking about good old roff-style man pages, I felt his words were a fun description of my effort to move from a markdown based templated site to a mdoc(7) based site.

After pointing out that I really did rewrite my site in mdoc(7), you might be wondering why I would do this. This blog post is intended to answer just that.


For the learning experience

The single biggest motivator of the rewrite was that I like to use this site as a playground for learning new (old) technology, and mdoc(7) was on my list of tech to learn.

What better way to learn a new markup language than to port an existing project to use it?

I had a blast transferring my old posts and coming up with a new build system.

For the look

I really enjoy a good man(1) page.

You've probably heard it before, but I'll say it again: one of the best parts of using OpenBSD is its concise and comprehensive man pages in the base system.

There's a certain warm fuzzy feeling when you can learn something both without internet access and with the knowledge that it is the authoritative source.

I want my site to be a homage to good, well thought out, man(1) pages.

Keeping it all in base

As an OpenBSD nerd, I find a bit of joy in having a site which is built, deployed, and served all via the base OpenBSD system.

By using mandoc(1) instead of, I can now build my site without any additional dependencies.

Better yet, mandoc(1) is ported to the various Linux distros I use day to day, and it is fast.


If you read this far, I thought you might be interested to hear how I'm deploying the content.

I'm a big fan of automation, so I've rigged up the site to deploy on a push to the master branch. Doing so involved two steps.

Building the mdoc

I created a small Makefile that builds each HTML file from each man page source.

The relevant bit is the implicit suffix rule to convert each .7 file to .html:

SUFFIXES: .7 .html
	@echo "mandoc $<"
	@mandoc -Thtml -O 'man=%N.html;,style=style.css' $< 
		   | sed 's#</head>#<meta name="viewport" content="width=device-width,initial-scale=1">&# ' 
		   > $@

This looks crazy, but it's not too complex. First, know that $< is the source (the <name>.7 page), and $@ is the target (the <name>.html page). The @ prefix is a bit of magic to suppress printing the command run (so that all the output shown on git-push is just a single "mandoc" line for each file updated).

Moving on to the mandoc command, I use the html output of mandoc via -T, with the -O switch specifying that linked man-page references should look locally first, then to point to This allows me to quickly reference OpenBSD base tools and pages, while also using the terse .Xr macro for linking individual site pages.

Finally, I use a sed(1) oneliner to splice in a <meta> viewport tag for mobile devices.

And that's really it! The rest is just listing the man pages I want built, with a phony default target depending on the html pages so that a make builds them all.

Deploying via git hook

Since I'm self-hosting git on the same server as the website, it's trivial to deploy when it receives a push by leveraging git hooks.

For the unfamiliar, git hooks are simply shell scripts that are triggered by specific git actions. In this case, I used the post-receive hook to publish after the refs were updated via a git push.

More specifically, I added the following to <git-dir>/hooks/post-receive:

echo "Deploying to to /var/www/htdocs... "
git -C ${dir} --work-tree=${WT} checkout -f master
make -C ${WT}
echo "done"

So, on any push, it checks out the entire source tree into the webserver's content area and rebuilds only the necessary HTML files (thanks to make(1)).

If I had files I didn't want served, I would modify it to build elsewhere and copy the contents to /var/www; however, I'm publishing both the source for the site and the git history at, so I don't see any harm to having the accessible from the root.

See Also

Back to blog