Making a website with Scotty and Hakyll

Posted on September 4, 2021 by Jacob Walters

I’m (currently) a Haskell nut, and it’s a rite of passage for any CS student to have a neglected blog. Why not kill two birds with one stone, and write a blog using Haskell tools?

Laziness is one of Haskell’s primary features, and I’ve taken this to heart in the form of minimising the effort required to do anything for this site, once everything is set up. What this means is that my site has the following goals:

  • Blog posts should be written in markdown or some other easily editable format.

  • Pushing content to the blog should be pretty painless, ideally with one command.

  • All administrative actions (updating SSL certificates, starting the webserver on my VPS’s startup, etc.) should be performed automatically wherever possible.

And, last but certainly not least,

  • The code behind the site should be 100% Haskell.


So, let’s entertain for a second the wild idea that I have something of significance to say. I want to be able to write this down in MarkdownBecause I’m lazy, and Markdown is very close to being pure plaintext. It’s also compatible with practically every blog platform, should I choose to switch at some point.

and have it converted to HTML automatically. such that it may be rendered by a browser.

Thankfully for me, most of the work has been done for me! Enter Hakyll, a static site generator written and configured in Haskell. It basically takes a list of posts, and some HTML template files, mashes them together and works out the hyperlinks, and spits out a fully organised directory structure of HTML files that can be copied straight over into your webserver.

The input files are converted using Pandoc, so nearly any input source can be used. This is nice, as it means that I can also use LaTeX if I have a particularly mathsy topic.


As far as CSS goes, I liked the idea of using sidenotes,Like these!

as they’re a great way of adding extra information without breaking the flow of the main body.

I’ve been a fan of Gwern’s site for a good few years, and while the sidenotes there (which can be on either side of the text) are something I’d really like to have, they require javascript, which I’d rather avoid.

So, I instead opted to use tufte-css, which gets me most of the way there with pure CSS. I’ve made some slight modifications (namely removing the custom fonts and relying on the browser’s defaults, which is better for accessibility and load times), but it’s mostly the same.

To properly integrate this with Hakyll, I use pandoc-sidenote, which allows me to use normal Markdown footnote syntax and have it rendered as sidenotes.


The accessibility of my site is something that I’ve put a fair amount of thought into. I ensure that no part of my site uses JavaScript,In the future, I may make some small JS (or more likely Type/PureScript) experiments. These will live under their own directory, and it will be made obvious that JS is required for such pages.

which makes the site compatible with nearly every browser since the inception of the web.

Unfortunately, tufte-css doesn’t render very cleanly when CSS is disabled; the text is inserted at the point of reference, which tends to break the sentence order. This is a compromise that I had to make so as to avoid using JavaScript. I reason that since the Markdown source of all of my posts is available on GitHub, this isn’t that big of a problem.

Alongside being more widely accessible, there are other benefits to running such a minimalist site: pages can be served nearly instantly. (Rudimentary) testing suggests that page requests are fully processed in 100 microseconds on average, meaning that the vast majority of waiting time is simply in network delay and browser rendering. I haven’t even implemented page caching yet!


The source for my site is available on my GitHub.


So we have our static files that we want to serve to the user. We could be boring and chuck them into apache or nginx and call it a day. Or, we could write our own webserver in Haskell.

Or, we could steal someone else’s! Scotty will do the job nicely. The gist is that we receive an HTTP request to our webserver, figure out what it’s asking for, and then reply with what it wants.

In our case, this basically means we just figure out which file it’s asking for. This is slightly more complicated than just copying the file path, since we want to e.g. redirect /posts to /archive.html.

Scotty effectively


Other shenanigans


A site’s gotta have a server to run on, otherwise you wouldn’t be able to access it! I’m (at the time of writing) renting a VPS from Contabo, who for 5 EUR/month will give you 8 GB of RAM, 4 CPU cores and 200 GB of SSD storage.

This is clearly overkill for a mere website, but it gives me the freedom to run lots of other services also, including a Discord bot for our year at uni, game servers, etc.

Point is, I’m getting my money’s worth.


Any site worth its salt must have a good domain name. I’m lucky in that my name: 1. forms a valid URL if you put a dot in it, 2. forms a URL that isn’t already taken, and 3. forms a relatively cheap URL.This last point is pretty huge; the equivalent domain for one of my friends would have cost him over £3,000 a year!

I rent my domain from Njalla, for 30 euros per year. Not a bad price at all, if you ask me. I have full control over the DNS, and the whois details opint to njalla, not me.Not that this matters all too much, given that the domain is my legal name anyways.


One of the biggest advantages of having a vanity domain like mine is making vanity email addresses. Unfortunately, almost all email providers require monthly fees if you want to use your own domain. And since I’m a fan of not being blacklisted from every major email service, self-hosting my email isn’t an option either.

So, I settled for mailbox, who provide custom domain support for only three euros a month. This price also nets you a lot of other things, so in my eyes it’s not a bad investment.