A switch to Jekyll

I have migrated this website again, and now it’s a static site generated by Jekyll and served by GitHub Pages! Hopefully I won’t have to perform another migration any time soon.

Having existed for almost 15 years now, this website has had a long history of backends. Initially, I used a custom blogging system which I developed in PHP (all the rage back in 2008). Eventually, that got way too cumbersome to maintain, and I moved it to WordPress. After a while of that, however, I found WordPress too feature-heavy and complex, especially in the theme (since WordPress themes are just PHP files) and editor (it’s basically a full WYSIWYG page designer now) departments. Therefore, in 2016 I migrated once again to Ghost, which this site has thus been using until shortly before this post was published.

Ghost itself is a fairly nice blogging platform, and much more streamlined than WordPress, however it is still software that has to run on a server. I was self-hosting the site, and so I had to personally maintain the server, including continuously applying security patches for the OS and especially Ghost—since of course, being a dynamic backend exposed to the Internet and written in Node.js, new security holes regularly presented themselves. This also led to a fairly fragile setup, since the entire website was being run from a single VM, which also held the underlying storage system, so there was no redundancy and also no content distribution network—all requests had to come all the way back to that single VM. Lucky this site isn’t popular! Of course, I could have attempted to replicate the system (though I’m not sure how well Ghost supports this), or integrate with a CDN, but that would have required even more maintenance overhead.

At the same time, I was barely using any of the features of Ghost, especially as the product added new functionality such as engagement tracking and paid subscriptions. The editor also got really complex after a while! In fact, I didn’t use basically any of the dynamic functionality of the system, with my site being entirely static. Ironically, this resulted in Ghost being both unnecessarily complex, and at the same time not sufficient to provide long-term stability for the website (at my desired level of maintenance).

There was also one feature Ghost was lacking that I really missed—the ability to add truncation markers (i.e., like “more blocks” in WordPress) to posts so that only a part of the text appeared in the list of posts. The Ghost authors seem philosophically opposed to this feature, and instead, Ghost has a separate “excerpt” metadata field which can be used in a website’s post list view in place of a post’s actual text. The excerpt, however, can’t contain any formatting. For a long time I patched the post content helper in Ghost to add back support for truncation markers, but I had to repeat this every time Ghost was updated, and the structure of the underlying Ghost code also changed of time, resulting in even more overhead while I figured out the best way to modify each version, and making me averse to even updating the site.

I also didn’t like the inability to add arbitrary metadata to posts in Ghost—for example, some articles on this site have outbound links which are rendered specially (e.g., posts in the lab section), and I would have liked to have been able to incorporate those links directly into my theme. There was no way in Ghost, however, to mark up posts with this information, and so I had to just place the links in the post body and then use some formatting workarounds to make them appear nicely.

Nevertheless, I continued using Ghost for quite a while (six years). Eventually, however, after upgrading my Ghost installation many times, something ended up breaking, and I was unable to fully use the administrative UI on the site. By that time, I had also forgotten the basic setup for the server, including the web server configuration, Let’s Encrypt integration, and most other aspects of the system. Therefore, I decided it was probably time to make a change.

I considered hosted options for a blogging system like Ghost, but these tend to be fairly expensive (e.g., Ghost’s own “Ghost(Pro)” hosted platform starts at $25/month if you want your own theme). In addition, as mentioned, I still wouldn’t use most of the features of such a platform. After some investigation, therefore, I decided, as mentioned above, that my site was basically static, and so I didn’t require an actual CMS or blogging platform at all—just a way to generate the pages for each blog post and the post list. This would also mean, no dynamic logic, and so no security holes (server platform notwithstanding, but at least I wouldn’t need to manage that)!


Jekyll is probably the classic choice for this sort of site. Following the step by step guide, I found it took a surprisingly small amount of effort to convert my existing Ghost theme to Jekyll, and with the help of the Jekyll Ghost importer, I was able to quickly import my existing content as well (though media had to be manually fixed with some find and replace action). Overall, it only took a few hours, and my website was now entirely static! Of course, this website wasn’t that complex to begin with.

During the process of developing my site on Jekyll, I also found it to be a lot more flexible, in many ways, than Ghost. In particular, the two main gripes I described above are solved in Jekyll. Truncation markers are fully supported, but more significantly, all pages can have arbitrary metadata (called “front matter” in Jekyll). This allowed me to easily distinguish the special outbound links discussed earlier, as well as add other fields as necessary such as header images and summaries, without these needing to be built into Jekyll itself.

The only feature I would be interested in that Jekyll doesn’t support is pagination for anything except the main post list (e.g., tag pages). Fortunately I don’t need this at the moment since the only other list of posts is on the lab page, which isn’t too long at the moment. A search feature would also be nice but of course you can’t have that on a static site (besides Google search, perhaps).

GitHub Pages

The next question, then, was where to host it. I could, of course, have continued using my existing VM based infrastructure, however this would have defeated the point of minimizing the maintenance I had to do, since while I wouldn’t have to update Ghost itself any more, I would still need to manage the remainder of the server stack on the VM. It also wouldn’t have helped improve the stability of my website as it would still have all been on one machine.

Instead, I wanted a hosted platform, so that I didn’t have to actually maintain any infrastructure. There are actually many choices here as several providers offer Jekyll “as a service”. In the end, however, I decided to go with GitHub Pages, as it (obviously) has very good GitHub integration, with continuous builds powered by GitHub actions. I’m going to assume GitHub’s CDN is also very good. In addition, having not just the site’s source code, but also the content (posts and media) on GitHub also provides a very permanent storage mechanism, unlike previously when everything was on a single VM I ran myself (backups notwithstanding). It’s also pretty cheap, as GitHub Pages is free for public repositories, and $4/month for GitHub Pro, which allows private repositories to be published.

All in all, after following the documentation, in less than a day I was able to rewrite this site’s front-end to work with Jekyll and and migrate my site to GitHub Pages. GitHub Pages was also able to seamlessly issue a TLS certificate for the site once I had changed my DNS record. No more server maintenance for me! Now I just need to find a good Markdown editor, since of course unlike an actual CMS there is no integrated online editor for Jekyll. Unless you count the editor you get when you press “edit this file” on the GitHub website, I guess…