cleinad. a blog.

Emacs & Blogging

The inevitable blog post on blogging.

Last Updated: 2026-03-14 Sat 14:41

Table of Contents

I use emacs, which might be thought of as a thermonuclear word processor.

– In the Beginning was the Command Line by Neal Stephenson

There must be some rite of passage (or compulsion?) for developers starting a blog to write a blog post about their elaborate blog setup.

Starting a blog though was always hard for me. Too many choices and frameworks. Too much abstraction for me. I'm lazy and would rather not learn another framework if I don't need to!

I ended up on this slightly custom blog setup with emacs & org mode. It mostly leverages org-publish. I wrote this blog post to document my thought process when making this – to elaborate on why I chose certain things. In my personal experience, a lot of blog posts will just tell you what they did. But I'm more curious on the why!

As a disclaimer, I do assume that the reader has a passing familiarity with emacs & org mode.

Why emacs?

I think most blogs will end up using some kind of static site generator. It's a lot easier to maintain than some kind of dynamic, React website with 60,000 dependencies and 376 vulnerability warnings that you ignore.

Static side generators take plain text content and transform them into nice, static HTML files that look pretty on your browser. It's great.

My issue with all these SSGs were the mountains of code that came with it. I do believe that with any SSG, your use cases will inevitably stray from the standard usage. Then, you have to start debugging whatever framework you decided to choose!

Emacs was something that I knew somewhat. Org mode was something that I wanted to learn. Org mode also had a lot of nifty features to transform *.org files into HTML files. So, I decided to setup my blogging workflow with emacs & org mode.

Generating HTML with Org

I kept one goal in mind when making this – to keep it as simple as possible. The less code that I had to maintain, the better.

To that end, this is what the blog repo looks like.

.
├── README.org
├── about.org
├── blog.el
├── html
├── index.org
├── posts
└── static

I hope it's pretty self explanatory. The posts folder contains all our blog posts, static contains our static assets, html contains our generated static HTML website.

Another thing to note. This blog is completely self contained. There are zero dependencies except for the emacs code that was used to generate the blog which is all in a single blog.el elisp file. Any assets I use are stored in the static folder which I serve myself.

The blog.el file is also quite simple. It's mostly defining the variable org-publish-project-alist to control how the org files are grouped together and exported. It looks something like this:

(setq org-publish-project-alist
      `(("pages"
         :base-directory "~/code/cleinad.com"
         :base-extension "org"
         ;; More options ...
         :html-head ,cleinad/css-head
         :publishing-directory "~/code/cleinad.com/html"
         :publishing-function org-html-publish-to-html)

        ("posts"
         :base-directory "~/code/cleinad.com/posts"
         :base-extension "org"
         ;; Even more options ...
         :auto-sitemap t
         :publishing-directory "~/code/cleinad.com/html/posts"
         :publishing-function org-html-publish-to-html)

        ("static"
         :base-directory "~/code/cleinad.com/static"
         :recursive t
         :base-extension "css\\|txt\\|jpg\\|gif\\|png\\|ttf\\|otf"
         :publishing-directory "~/code/cleinad.com/html/static"
         :publishing-function org-publish-attachment)

        ("cleinad.com" :components ("pages" "posts" "static"))))

Notice that it's really just telling org-publish which directories it should look into and what options it should export it with.

Once we have this, the core publishing logic is done. Running org-publish spits out the website in the html folder. The rest of the setup is just tweaking it to make the blog look slightly less ugly!

Making the blog look less ugly

The default style that org exports with is pretty ugly and makes for an unpleasant reading experience. We can use our own styling with these two options:

:html-head-include-default-style nil :html-head ,cleinad/css-head

Which just tells org to append all HTML files with the below.

<link rel="stylesheet" href="/static/style.css" type="text/css">

Automatically updating the homepage

Obviously, nobody wants to manually update their home page whenever they write a new blog post. That sounds silly.

Fortunately, there is also another publishing option that does this for us. Setting the :auto-sitemap t publishing option tells org to create a sitemap of all org files in that directory. This means we can just create a folder containing all the blog posts, have org generate a sitemap.org file with links to all posts, then include part of that sitemap file in our homepage.

The sitemap.org file that org automatically updates looks like this:

#+TITLE: Sitemap for project posts

- Emacs & Blogging {{{timestamp(2025-08-09)}}}

The homepage is an just index.org file which includes the sitemap like so:

#+INCLUDE: posts/sitemap.org :lines "3-"

Which is the homepage you see now.

Conclusion

I am pretty satisfied with what I have now. I think there's a few more things I would like to add such as tagging blog posts, creating an elisp macro to create a blogging template, etc…

Maybe I'll update with another blog post if I add those features.

Thanks for reading! You can find all the source code here.