For quite a while my blog has run on top of Ghost. Ghost is a blog hosting system built on top of nodejs. It provides a nice admin backend for editing your posts and it comes with a default theme that's good enough to not care about theming.

My blog has been through some rewrites, from Drupal to Jekyll to various PHP based CMS platforms and finally it ended up on Ghost. I like Ghost quite a lot but it has a few flaws that made me decide to replace it.

  • It's nodejs. I know a lot of people like javascript for some reason but it's quite horrific for system administration. I do not like having to add third party repositories to my system to get a recent enough nodejs build to host a simple website. I'm pretty sure this dependency on the latest and greatest is the main reason why everyone wants to stuff their project in docker and not care about the dependencies.
  • It only officially supports Ubuntu to install on (aside from Docker). It was already annoying to get it installed on Debian but now I've moved over most of my servers to Alpine this has become even more of a pain. There's no reason this has to integrate so deeply with the distribution that it matters what I'm running.
  • It depends on systemd, this is largely part of the Ubuntu dependency above and not needing to implement something else. But from running Ghost in openrc I've noticed that it depends on the automatic restarts done by systemd when the nodejs process crashes.
  • I'm just running a simple blog which is fully static content. this should not require running a permanent application server for availability of the blog and the dependency on Mysql is also very much overkill for something that's so read-heavy.
  • I don't particularly like the templating system for adjusting the theme. I've been running a fork of the casper default theme for a while that just rips out the javascript files. This breaks almost nothing on the default theme except the infinite scroll on the homepage.

Switching to something different

I could try to switch to yet another blogging platform. The issue is that I really really like writing posts in the Ghost admin backend. With a casual look at the browser dev console another solution became obvious...

The whole Ghost admin backend is neatly packaged into four files:

  • ghost.min.js
  • vendor.min.js
  • ghost.min.css
  • vendor.min.css

Since this is a fully standalone client side webapp thing I can just host those four files and then write a Python Flask application that implements the same REST api that the nodejs backend provided.

This is exactly what I did, the whole backend is now a small Flask application that implements the Ghost API in ~500 lines of Python. It's called Spook (since this is dutch for ghost).

This also uses an SQLite database as storage backend for the whole system. This Flask application also does not implement any of the website rendering. It just stores the state of the Ghost backend and does media uploads. To actually get a website from this it implements a hook script that gets called whenever anything on the website changes. With that it's possible to use any static site generator to generate a static website from this dataset.

Reimplementing casper

As static site generator I used a second Flask webapplication. This one uses the Flask-Frozen module to generate a static website, this is why this module has the name frozen_casper. This generator reads the SQLite database from Spook and generates static HTML compatible with the stylesheet from the stock casper theme from Ghost. It also generates an identical RSS feed so the RSS subscribers can be migrated over with identical post unique ids.

I did make some modifications to the generated html like implementing very basic paging on the posts list pages instead of relying on javascript infinite scrolling.

The spook and frozen_casper module together replaces a complete Ubuntu Server install, a mysql instance I had to keep giving RAM and a crashy nodejs webapplication. Since today this new system is hosting this blog :)