🌍 My website setup

2020-12-02

I often see articles about how people build their website and it is always interesting to see all the possibilities people are using to bring their thoughts to the web. So this write-up is my attempt at showing how I create and maintain mzumquadrat.de.

The HTML

Since static site generators (SSG in short) are the shit a hot comodity right now I am also guilty of generating my HTML with a SSG. After a lot of searching for the right tool I settled with eleventy. Eleventy gives me the right mix of flexibility and data aggregation to generate nice HTML from a bunch of markdown files and nunjuck templates. A feature I really like about eleventy is that it allows me to aggregate and use data in a lot of different ways.

For example: I created a navigation.json file in my eleventy _data folder and created a small function in my base tamplate to iterate over said json to generate the navigation entries for me. This is a very simple example.

In the early days of me using eleventy I had a feed of my mastodon posts on my frontpage. These toots were placed there during the buildprocess and required no client-side javascript. In order to place those toots on my frontpage I had a mastodon.js file in my _data folder which would be executed during buildtime, fetch the latest five toots using the mastodon API and would create a JSON object which was then added and iteratet over in the template for the frontpage. If you would like to know more about how you can get data into eleventy I recommend yo to take a look at their documentation.

The CSS

I am no designer. Especially no webdesigner. The fact that my website is actually decent looking is because I am using a tool called TailwindCSS. Tailwind follows the principle that you actually describe how your HTML should look via CSS classes. So for example to color the background of a <div> red you would write: <div class="bg-red"> While TailwindCSS initially offers a whole lot of options including flexbox/grid, different colors, several fontsizes, hover effects and so on. In order to have a lean CSS file in production TailwindCSS utilizes a tool like PurgeCSS to remove all unused styles from the final CSS. Eleventy also offers a plugin which allows you to use TailwindCSS and to minify everything in your build process.

The build process

I am hosting the source code of my website (including all the templates and so on) on my git repository over at codeberg.org. Since codeberg offers no CI/CD features I had to improvise and build my own. Which was not very difficult actually. The server my website lives on is a VPS which means not only SSH access but also the ability to run software on it. I am using nginx as a webserver/reverse proxy for all my web-facing stuff. In order to get notified every time a new commit to my repo is made I am using a tool called webhook which allows me to easily link a URL to a script which gets executed once the URL gets called. This URL then gets added to my codeberg repo as a webhook. Codeberg then sends a bunch of information to my webhook which then triggers the script with several parameters.

[
{
"id": "build-eleventy",
"execute-command": "<path-to-the-build-script>",
"include-command-output-in-response": false,
"trigger-rule":
{
"match":
{
"type": "value",
"value": "<secret>",
"parameter":
{
"source": "payload",
"name": "secret"
}
}
},
"pass-arguments-to-command":
[
{
"source": "payload",
"name": "commits.0.id"
},
{
"source": "payload",
"name": "commits.0.message"
}
]
}
]

The hook configuration is pretty forward. The trigger rule is there to make sure that the payload sent to this webhook is really coming from my codeberg repo by setting a mutual secret that has to be present. After that I define that exactly two arguments are passed to the build script which are the commit id and the corresponding commit message.

Since I do not have nodejs 15 installed on my server I am using podman to containerize the whole build process. The script for it is also pretty simple.

#!/bin/env bash
function send_message() {
podman run -it --rm --name eleventy-notify -e CHAT_ID=<ID of the chat the bot should send the message to> -e MESSAGE="$1" -e BOT_TOKEN=<your bot token> docker.io/containsliquid/telegram-send:latest
}

function check_error(){
if [ $? -ne 0 ]; then
send_message $1
exit 1
fi
}

podman run -it --rm --name eleventy-pull -v /home/mzumquadrat/website:/git -w /git docker.io/alpine/git pull
check_error "Pulling $1 failed"

podman run -it --rm --name eleventy-install-dep -e NODE_ENV=production -v /home/mzumquadrat/website:/usr/src/app -w /usr/src/app docker.io/node:15 npm install
check_error "Installing dependencies for commit $1 failed"

podman run -it --rm --name eleventy-build -e NODE_ENV=production -v /home/mzumquadrat/website:/usr/src/app -w /usr/src/app docker.io/node:15 npx @11ty/eleventy
check_error "Building HTML for commit $1 failed"

rm -rf "/home/mzumquadrat/website/node_modules"

send_message "Deployment of commit\n$1\n$2\nwas succesful"
exit 0

First I define a function to call a small container which uses the Telegram API to send a message via a bot to a defined user or group. After that I created a function to check if the return value of the statement before ran without error. Since I am using my own VPS where I can ssh into and debug things myself I renounced to do some precise error handling like printing the error message. The first oder of operations is to pull the actual changes. Technically I have git installed at my VPS but since I was using podman for nearly everything else I opted into using podman for that as well. The basic principle is that I mount the folder where all my files live into the corresponding container and operate on it.

After pulling everything I have two node 15 containers who install the basic dependencies and after that generate the HTML. At the end I am deleting the node_modules folder to conserve space and send a message via Telegram to myself.

After that the website itself is served with nginx.

Conclusion

Overall my whole "technology stack" is pretty boring but I would argue that this is a feature. This allows me to have a maximum of control over everything.

Codeberg is not available in the future? Switch to another provider push your changes there.

My hoster decides to shut down? Go to another hoster and set up my stuff there. Since nothing in my stack is really vendor specific (the only exception might be eleventy/TailwindCSS) I can easily switch and/or replace the key components of my stack.

I hope that this little post wasn't too boring and someone got some insights out of it.

Shoutout to Johann150 for sending me a bunch of suggestions and corrections to this article.

Tags: #web

If you want to send me a message feel free to contact me via my mastodon account. You can find me over at Fosstodon.