Economy of Effort

Twitter LinkedIn GitHub Mail RSS

Goodbye Wordpress, Hello Octopress

I have been running this blog on Wordpress since 2005. Back then, Wordpress was purely a blogging engine.

In the years since then, Wordpress has grown into something more akin to a CMS built around a blogging engine. At work, we have used it as such for a couple of small storefronts, built around the blog and the Wordpress e-Commerce shopping cart plugin.

Maintaining a full Wordpress installation for my personal blog, however, had become cumbersome. Particularly so since I am not running any other PHP code for personal projects. At Lone Star Ruby Conference, one of the talks finally convinced me that it was time to leave Wordpress behind, and to go with a static site compiled blog engine. I had previous experience with a static site compiler, nanoc, which we use at work for creating static websites. A more blog-aware tool that works similarly held plenty of appeal to me.

I also no longer wished to run this site on hosting that costs me money. I started with shared hosts like Dreamhost, graduated to a Linode VPS (more for experimenting with VPS hosting than for any actual traffic needs), and most recently ditched the VPS and hosted on NearlyFreeSpeech’s low-cost pay-as-you-use hosting. But for how low traffic the site is, paying even what I give to NearlyFreeSpeech seemed unnecessary. Heroku’s free single web dyno was staring me in the face, offering more than enough hosting power for a static version of my site, for $0.

Introducing Jekyll and Octopress

Octopress is a framework built around the Jekyll blogging engine. It provides various plugins and extensions, as well as a nice default theme, to make blogging on Jekyll a nice out-of-the-box experience.

Jekyll allows users to write blog posts in Markdown and compile them into static HTML pages. Instead of writing posts in a web-based panel, posts are created by adding a new Markdown file in the _posts folder, and writing the post in there using the user’s editor of choice. Finally, I am blogging with Vim.

Octopress provides out-of-the-box support for Disqus commenting, recent Twitter tweets in the sidebar, Google Analytics, and a whole host of other added functionality.

Importing Content from Wordpress

My strategy for importing my Wordpress content into a Jekyll blog reolved around Exitwp. Exitwp will parse a Wordpress export file and generate a Jekyll blog with the same content.

The Exitwp Github page has instructions for installing dependencies on Ubuntu, but on Homebrew on OS X, the commands were:

$ brew install python
$ brew install pyyaml html2text beautifulsoup

(Important: make sure /usr/local/share/python is in $PATH.)

Next, I needed to go into my Wordpress admin page and generate a Wordpress export XML file. As of the time of this writing, this is done in Wordpress by logging in to the dashboard as an admin, and going to Tools -> Export.

With the export XML file generated and on my desktop, I set Exitwp to work:

$ python name-of-export-file.xml

One important thing to note: images require some handling. You can make Exitwp download your blog’s images by editing the Exitwp config.yaml file and setting…

download_images: True

… however, this will only download the image files. It will not edit the posts themselves to point to new image locations.

I did not relish the idea of going through all of my old posts and editing each of the image URLs. Instead, what I did was create a wp-content/uploads folder in my Octopress blog’s source/ folder, and copied the contents of wp-content/uploads from my Wordpress blog into there. Since I am hosting the new blog on the same domain, the result is that all of those image files will still be on the same URL. Having a wp-content folder inside my new blog is slightly ugly, but it solves the problem for now, and allows me to gradually move images over and edit image paths on old posts.

Also important to note: comments have to be dealt with separately, too. As a static site has no capacity for comment handling itself, comments on Jekyll/Octopress blogs are handled by Disqus. Fortunately, in my case, I had already moved my Wordpress site to using Disqus commenting. For me, that meant that my comments would carry over to the new site, so long as my post URLs did not change. In my case, this meant making just a small tweak to the config file of the Jekyll blog once it was generated, so that the URL structure would mirror my old Wordpress site’s.

Setting up Octopress

After running Exitwp, I have my old blog exported into a raw Jekyll blog. But now, I have to get that blog into Octopress.

This part confused me for a while. It seems like something everyone else just glossed over.

For starters, I knew I wanted to store this blog in Git. The Octopress instructions would have me clone the Octopress repository, but I don’t want Octopress to be the origin on my blog repo. Instead, I did much like this blog post demonstrates - I made my own blank repository, and I added the Octopress repo as a remote head.

So, now I had Git set up, and I had Octopress checked out locally by virtue of having run git pull octopress master. The part that wasn’t immediately obvious to me was how I was to take my Exitwp-generated Jekyll blog and put that in there.

Exitwp put my generated blog in exitwp/build/jekyll/blog-name. I copied the contents of this folder, and pasted it into octopress/source. Now, my Jekyll content was where it needed to be in Octopress.

To update my remote repo’s copy of the site, I check everything in, and run

$ git push origin master

And whenever I want to pull in the latest updates from Octopress, it’s

$ git pull octopress master

Deploying to Heroku

Adding to the Git setup even more was the fact that I wanted to deploy this to Heroku. For that setup, I basically followed these instructions starting at the “Deploy to Heroku” section. I had never deployed an app to Heroku, but it was very straightforward.

Since Heroku acts as a Git server, I could have skipped the part where I made my own repository host, and just cloned from Heroku whenever I wanted to access the repo on another machine. But I prefer having a copy of the site in my own Git hosting account (on Bitbucket, for the record), and it’s hardly any additional bother. My Bitbucket repo is on “origin”, the Octopress repo is on “octopress”, and Heroku is on “heroku”.

Whenever I want to push updates to deploy to Heroku, I simply do

$ git push heroku master

Finally, I set up Heroku to use my domain name.

What I Think

I haven’t used Octopress for very long yet, but a few thoughts:

  • Writing posts in Vim and in Markdown syntax has made me realize how much of a drag using the Wordpress post editor was on my blog writing. I write code all day in Vim, and writing my blog posts there too is much less of a context switch. Flicking back and forth between Vim buffers is a lot less of a hassle. It makes offline blogging a lot easier, too.
  • I never found a code formatting plugin for Wordpress that I did not hate. Octopress comes with code formatting styling out-of-the-box and it works very well. I’m not a huge fan of the Solarized theme it uses by default, and I may see about changing that in the future. But the important thing is that it works.
  • Not having to worry about Wordpress updates is a big relief. I can’t say that I stayed on top of updates nearly as much as I needed to. And I don’t have to worry about database backups, either. There’s a “weight off my shoulders” feeling with making this move.
  • There’s something comforting about having my entire blog history as a series of Markdown files, instead of posts locked away in a Wordpress database table in MySQL.
  • There are a lot of neat Octopress plugins that I haven’t really delved into yet. But the default out-of-the-box experience is pretty much awesome. Even if, for now, my blog looks just like a bunch of other Octopress blogs.

Tip: Use IFTTT to tweet new posts

One of the plugins I used with Wordpress would add tweets to my Twitter feed, informing followers of new posts to the blog.

Without the server-side component, Octopress lacks this ability. However, thanks to the fact that Octopress generates an RSS feed file, we can use an external service to accomplish the same thing.

IFTTT is a service that allows you to write “triggers” that perform various actions. In this case, I have IFTTT watching my blog’s RSS feed, and whenever it detects a new feed item, it makes a post to my Twitter, as well as one to my Facebook wall.

Tip: Use Pow on OS X for easy testing

By default, users can run rake preview to make Octopress spin up a web server at and listen for changes to files to automatically rebuild the site for easy previewing.

This process can be made a little nicer with Pow, a handy little Rack webserver for OS X.

Just add a symlink your site folder in to ~/.pow/, and your system will run that site, and configure it to be reachable at Then, run rake watch to make Octopress listen for changes and rebuild pages.

Gotcha: Drafts aren’t imported by Exitwp

I had accumulated many half-written posts in my Wordpress install over the years. Posts that I totally intend to finish.

Exitwp did not import these (or, more likely, the Wordpress export functionality did not include them in the export. I’m not sure which it is.)

I ended up fetching these manually.

Gotcha: zsh and square bracket commands

From that point on, I just followed the Octopress documentation to get up and running. I did, however, run into an annoying issue.

Octopress command-line commands often use square brackets, such as:

$ rake new_post[“My new post’s title”]

Run it in zsh, though, and you get:

$ rake new_post[“My new post’s title”]
zsh: no matches found: new_post[My new post’s title]

The problem is that square brackets are a glob operator in zsh. This blog post pointed me in the right direction. The “solution” is to escape the square bracket characters.

$ rake new_post[“My new post’s title”]

Alternately, zsh users can disable zsh’s GLOB option. From the Octopress Github issue on this problem, though, it sounds like some tweaks will be added to Octopress to address the issue.