Goodbye Wordpress, hi rstblog!

written on Monday, June 11, 2012 by

Since having switched from the PHP world to the Python world about 2 years ago, I thought about relaunching my Blog using Python instead of PHP. At first, I thought about creating an application with Django and PostgreSQL, but never really had the time and motivation to finally implement it. But today I stumbled over rstblog by Armin Ronacher.

Meet rstblog

It is basically a static reStructuredText to HTML converter. You create a year/month/day folder structure, create .rst files in there, add metadata to them in YAML format and run the generator-script.

Because it's static, you don't need any dynamic webserver or interpreter, it's just plain HTML files that you can serve practically everywhere. You can even track them using a source control system. And last but not least – you'll never have to update your Wordpress installation again, yay!

rstblog supports everything a blog needs: detail pages, archive pages, tags, atom feeds and comments (using Disqus). You can create custom stylesheets and templates.

You can also embed syntax highlighted code using pygments:

class Bar(object):
    def foo(bar):
        print 'I don\'t like spam!'

And LaTeX formulas:

$$f(x) = a_0 + \sum_{n=1}^{\infty}\left(a_n \cos \frac{n \pi x}{L} + b_n \sin \frac{n \pi x}{L}\right)$$

There's even more to like. If you want to know more, take a look at the repository and the sourcecode. That's also one of the downsides – there's no official documentation and the project seems pretty inactive, pull requests aren't being merged.

To get started, here are some related blogposts:

Publishing Content

To easily publish content, I use Fabric and rsync. Some easy fab tasks simplify the workflow:

import time
from fabric.api import local, env
from fabric.contrib.project import rsync_project

env.hosts = ['']
env.path = '/var/www/dbrgn/blog/'

def push():
    local('git push')

def serve():
    local('run-rstblog serve')

def build():
    # Build HTML
    local('rm -rf _build/ && run-rstblog build')

    # Generate sitemaps
    local('python > _build/sitemap.xml')

    # Minify CSS
    local('cssmin < _build/static/style.css > _build/static/style.min.css')
    local('mv _build/static/style.min.css _build/static/style.css')
    local('cssmin < _build/static/_pygments.css > _build/static/_pygments.min.css')
    local('mv _build/static/_pygments.min.css _build/static/_pygments.css')

    # Add timestamp to css files
    local('find _build -type f -exec sed -i "s/\(link.*\)' + \
          'style.css/\\1style.css?%s/g" {} \;' % int(time.time()))
    local('find _build -type f -exec sed -i "s/\(link.*\)' + \
          '_pygments.css/\\1_pygments.css?%s/g" {} \;' % int(time.time()))

def sync():
                  exclude=['*.py', '*.pyc', 'requirements.txt'])

def deploy():

def publish():

You can see the entire fabfile on Github. The sitemaps are generated by

By issueing a simple fab publish in the root directory, the following steps are taken:

  • The _build directory is (re)built
  • Sitemaps are generated for each .html file
  • CSS files are minified in-place
  • A timestamp is appended to all CSS file names to make browsers reload cached content
  • The _build directory is deployed to the server
  • The git repository is pushed to Github

This Blog

Besides the blog-related technical details, this blog will from now on focus mostly on technical content, usually written in English, mostly about Python / Django / Linux / Programming related things.

The blog repository is published on Github. Content is under a CC by-sa 3.0 license.

This entry was tagged blog, python, rst and rstblog