Moving to Freedom, .Org(on)

Fly, Pelican, Fly

Pelican (

Welcome to the other side. After nine years and eight months, WordPress is no longer powering this site. I still love WordPress. It’s done me right all these years. It’s gotten steadily better and has a great community using and supporting it.

But it’s more than I need for the blog. There are a number of moving parts with content management systems. Software updates to keep up with, a database, and… mostly those two things. For years I maintained a local dev installation, creating even more work for myself.

It wasn’t that hard to deal with. In earlier years I enjoyed learning PHP along with WordPress, and I didn’t mind the tinkering and maintenance. I enjoyed it. Automattic has made WordPress easy to update, and I’ve not had particular trouble with updates breaking things.

But I’ve come to desire something simpler. I can’t remember what prompted me to start looking at “static site generators,” but the idea quickly took root. (I remember early on being pointed to a good post about Jekyll.)

With these programs, you generate everything locally and upload it to the server. It’s just a collection of files that can be served by your basic web server. Or perhaps with AWS S3 and CloudFront, for a heavy duty hosting option. (A cheap heavy duty hosting option, assuming modest visitor numbers for a site like mine. Which of course doesn’t need heavy duty, but it would still be neat.)

Jekyll is a popular site generator, but I went with Pelican because it’s written in Python and I love Python. And Pelican is super, regardless of the underlying language. It’s popular enough that it has lots of stuff to cater to your deepest needs and longings.

A nice feature of these things is they support formats like markdown, and even better in the case of Pelican, reStructuredText. I like getting away from HTML for the source. (Even while using .. raw:: html liberally as needed.)

Pelican has a WordPress importer to convert your posts into .rst files. It works well and gets you a lot of mileage, but there was still a lot of manual work involved. I got medieval on the task and went through all 500 of my posts, making updates and murdering 130 of them. (All numbers approximate.) The slain include most of my old technical posts which had fallen sadly out of date, and others that begged for a mercy killing.

All along: much joyful tinkering and learning of new software.

Developer Scott loves it because now everything is flat files, easily and fine-grainedly tracked in source control. (Git, of course.) You have the entire array of Unix command line tools for munging it all up. Posts are more easily searched and manipulated as a group. (And individually, with popping something open in a text editor versus searching in a web UI and waiting for things to load.)

And then everything is easily sync’ed to the live site. Local and live sites are essentially the same. It pleases me greatly to have the server side be so simple: a pristine assortment of index.html files.

I’ve decided to jettison comments, but for your own blog if you wanted comments, and are willing to use Disqus, this is well-handled. (I expect I’ll resurrect some comments, someday.)

Let’s Get Technical

Some technical tips and learnings on moving from WordPress to Pelican…


This minimalist theme was a great starting point:

mnmlist, by Matheiu Agopian (based on Leo Babauta’s mnmlist WordPress theme).

Plugins, Etc

I’m using these three plugins initially:


Neighbors hooks up neighboring posts with links.

Pelican YouTube lets you insert responsive YouTube embeds with just the video ID like so:

.. youtube:: 62EpTFlKzNI

(BTW, if you submit to YouTube embeds on your site, you must constantly monitor them, for copyright holders wreak much havoc.)

There is a typogrify Python library that handles curly quotes and other typographical goodness. Once installed, you need only in your conf:


The Feed

The feed generator doesn’t (or I didn’t see how to make it) fix relative links for images and posts, which is a problem for feed presentation and link functioning. I also wanted to style my floating images. (With apologies. We obvs shouldn’t style our feeds. But it bothers me to sink floating images.)

Longer term I may work on a plugin approach, but for now I just use good old sed before I upload:

function run_sed {
    sed -i -r "$1" feed.xml

    if [[ $? -ne 0 ]]; then
        exit 1

run_sed 's|(<img.*?src=")/|\1|g'

run_sed 's|(<a .*?href=")/|\1|g'

run_sed 's/class="[^"]*(imgfloatright|alignright)[^"]*"/style="float: right; margin: 0 0 15px 15px"/g'

run_sed 's/class="[^"]*(imgfloatleft|alignleft)[^"]*"/style="float: left; margin: 0 15px 15px 0"/g'

I later noticed float: none; creeping in from somewhere and overriding the float, so as a quick workaround, I added:

run_sed 's/float: none;//'

This whole script is janky, of course, based on fleeting empirical observations and with assumptions of how I use images and URLs.

CSS Cache Buster

I added this to the conf to create a hash for the stylesheet (8 characters should serve our uniquity needs):

import hashlib

# ...

hasher = hashlib.md5()
with open(THEME + '/static/css/' + CSS_FILE, 'rb') as afile:
    buf =
CSS_HASH = hasher.hexdigest()[0:8]

In my conf, I’ve specified THEME as a path and CSS_FILE = 'style.css' to override the default main.css, so they’re available as variables here, but in other cases you’d have to hardcode the path or do something smarter and more elegant than I’m aware of ATM.

And then in base.html:

    href="{{ SITEURL }}/theme/css/{{ CSS_FILE }}?{{ CSS_HASH }}"


Kudos to Justin Mayer for stewarding this thing.


Would do business with again!

Previously More Still...