I Made This Site

Blog From Scratch

First off, to all those who have subscribed to my feed: I am genuinely sorry for the massive amount of spam in the feed!

I have rewritten my blog from scratch. What was once a statically generated blog 1, is now a fully functioning web server written in Rust.

Why

TL;DR: Learning and flexibility.

SSGs are great. They make the web a better place to be. They make it faster, lighter, more accessible, and more stable.
You have a couple of pages, you create those pages, and… bam! You have a pretty and stable site for yourself. They have many, many varieties. Most, take a few templates and some Markdown files, and create a bunch of HTML pages. My favorite of this bunch is Hugo which pretty fast, feature-full, and well-structured. Others are more interesting; they basically create the same pipeline: content + template = HTML. Yet they have different presumptions. Of these bunch, I absolutely adore Soupault. This one uses HTML and CSS as their templating engine and does not presume any particular content type. You bring your own converter. You may use anyone or a few different Markup languages. It is fantastic.

However, each has its own flaws. And you can only go so far with static pages.

Some of the things that have constrained me:

  • I like to be able to choose which Markup language I use at each moment. I might like to write some on Markdown, others in Djot, Others still in Org-Mode. And then I do my own modifications, macros, and extensions. Some SSGs give you some choice in the matter. But other than Soupault, you are usually at the mercy of the choices of the SSG maintainer.
  • I would like to have dedicated feeds for each category. Most SSGs avoid letting you do that or give you limited choice on the format of the feed.
  • You can do wonders with templating languages. But the tooling is usually horrible; the disasters only manifest themselves when you visit and validate each page, and doing things like showing specific posts only on the feeds requires the SSG to make the metadata available to you and for you to do pretty clunky conditionals using the templating language.

And then, there are things that you can only do, when you have access to the Runtime of the server.

  • You cannot receive drafts and posts without having access to the coding environment — I don’t do it right now here, but I Intend to add one in the future.
  • Some things, like Web Mentions, require the access to server.
  • Having more interactivity, using things like HTMX, is simply not possible using SSGs.

Add to all of these the fact that I am a Back-end developer. Sometimes I may want to experiment with different ideas. Some of these ideas may not be a good fit for the projects I work on, during my day job. Or my teammates may not agree with me about their suitability. Having a web server as a home lab has been a great desire of mine!

What did I choose?

Over time, during the creation of the web application, I have experimented with different things. And I have made choices about others. Let me explain each.

Rust is a pretty great language. But One thing it is not is simple. I am very confident with Go. And I am also learning Gleam. Both of them later offer a much simpler workflow. However, wanting to have maximum flexibility, Rust is a better language for me. Added to that, things like dealing with nil-pointers in Go have been a source of constant pain in my ass. Rust offers less chance of Runtime error.

You may not have heard of Djot. Djot is created by the creator of Pandoc, the command-line utility that converts different document formats. It is pretty similar to Markdown, but with the significant advantage of being created by someone who has spent a significant amount of time working with markup languages instead of some random blogger suggesting something at some point in their blog post.

I am using a library called InkJet which uses tree-sitter to add syntax highlighting to my code blocks. I have injected it into the Djot parser I use, called Jotdown.

Also, I have decided to hard-code the contents for now. So at compilation time, every Djot file is read and embedded inside the web server. This just simplifies things for now for me. But I have implemented the whole thing so that I can inject dynamic contents later on.

During my API testing, I have noticed that the performance of the new version could not match the statically generated one, using the built-in server of Hugo. Upon further inspection, the culprit was the rendering process. Which took the time of serving up each page on the local storage from 5ms to 15ms. Though not a lot, It could be easily fixed using a lazily created on-memory cache layer for each page and feed. So each page gets rendered only once, right after I update the site. Easy.

Uberspace

Thanks to the suggestion of Benjamin Hollon, I have found a really small-web-friendly hosting service called uberspace.de with a generously priced €5/month service. The OS is a bit out of date right now 2, and that is causing a bit of a pain during the build process, but honestly, their service is so lovely and Linux-friendly that I could not consider any other service!

What have I lost?

Everything has its trade-offs. And there are some really great reasons for not doing what I have done.

  • Previously, adding a blog post was as simple as creating a file, and running a Justfile 3. Right now, I have to create a couple of files, add the post to the dedicated array, and then build it using Nix Flake 4, and then rsync the resulting binary to the server and restarting the service using ssh.
  • Added to that, the compilation time is significant. And CPU-intensive. For some reason, some of the C dependencies of Tree-sitter that I depend on are not friendly to incremental builds. I am seriously considering dropping syntax-highlighting as a result!
  • Another con of this method is obviously having to pay for it! I cannot mooch off of sourcehut pages anymore!
  1. It was generated using Hugo

  2. I have contacted their support team. They told me that they are working on an update.

  3. Or Hipster’s Makefile if you will!

  4. The build pain that I mentioned earlier!