Creating a static site generator

Creating a static site generator

Towards the end of 2021, I'd decided that 2022 would be the year that I'd write more. I wanted to write articles that were equal parts technical and personal, in contrast to my academic and technical writing up until that point.

The journey had a fine start. I wrote a few posts and enjoyed some engagement from readers. However, I had this feeling that something was missing. Of course, I realised that I'd made the mistake of beginning to write without first creating a static site generator.

Once I'd identified the problem, I stopped all writing and focussed my efforts on creating my own static site generator. This would, once finished, solve all writing-related problems, and put an end to any procrastination 🫣.


I'll be cross-referencing many of my projects at different web addresses through this post. By the time I publish this post, all of these will be running on the Nunlet static site generator.

Nunlet, my personal static multi-site generator

Just over one year later - my static site generator is ready to publish static sites. In the meantime, its various iterations have been used in the wild for testing.

  • The Polyra blog, powered by Ghost CMS, had its content populated entirely by Nunlet. At this point, the static site generator project was codenamed ghostbook.
  • The PlotAPI documentation is generated entirely with Nunlet, with some Django integrations sprinkled in to enhance the (registered) user experience when browsing the docs.

Why is it called Nunlet?

I was looking for domain names that I like, that were actually available, and saw drop. I worked backwards from there. Nunlets are cute-looking birds, and it was shorter than

What was wrong with existing blog solutions?

The available options are great. Some reasons for writing my own were things like vendor lock-in, wanting to write in Jupyter notebooks, and wanting multi-site cross-referencing. I tried many along the way, and here are a few that stuck.

Nikola static site generator

Nikola is great and I've been using it for many years. It's a static site generator written in Python.

In goes content, out comes a website, ready to deploy.

I've used it to generate DataCrayon and the old PlotAPI gallery since their launch. With it being written in Python, I found it easy to modify and customize the source to add features.

It can generate posts from Jupyter notebooks, but I did find that some of the nice features were limited to posts in reStructuredText or Markdown format.

Ghost CMS

Ghost is great too and I've been using it for just over a year. I rarely use it directly for content management - mostly for user management. However, I'm not interested in having a database behind my posts, and it recently dropped official support for SQLite in production.

It has a newsletter feature built in, but unfortunately, it only has support for one provider.


I use WordPress (with WooCommerce) as the back-end for the DataCrayon shop, so it was convenient to get started with it for blogging too. I was using this quite some time ago, and experienced the switch from the classic editor to the newer one named Gutenberg. I quite like Gutenberg, the writing experience reminded me of Medium's editor, which I thought was nice at the time.

However, WordPress didn't suit how I prefer to organize my posts (as readable files, locally) and I didn't enjoy hacking away with PHP for the customization.

How does Nunlet work?

I'll be diving into the various parts of Nunlet in upcoming posts. There's too much to dig into all at once! In the meantime, I'll share a few highlights:

  • It's written mostly in Python.
  • It (currently) has two types of content.
    1. Article: this is like a blog post, and starts off as a Jupyter notebook.
    2. Page: this can be anything, and starts off as a Jinja 2 template (HTML file).
  • Sites are defined in a settings file. Nunlet can manage and deploy multiple websites. The current supported outputs are:
  • Because of the way articles and sites are managed, cross-site referencing is enabled.
    • Multiple sites can share the same content.
    • Sites can do things like list the latest posts from all other websites.
    • Everything can be an article, and these are composable. One example is my CV, which is a higher-level representation of many other articles.