This article describes the old version of this blog.
After a Hugo upgrade caused problems with the original theme, I eventually moved to HBStack.
I decided to keep this article because it was the very first post published on this blog and documents how the project originally started.
When I first considered creating a blog, I went through the usual questions:
Why would I spend precious hours of my life writing something that perhaps nobody will ever read?
This article contains my thoughts about that question, what I wanted from a blog, and what I learned while building the first version of this site.
My reflections on the topic
In short, I went through a few questions:
- Why create a blog?
- Where should I host it?
- Which technology should I use?
- Which theme should I choose?
You may notice that these questions gradually move from theory to implementation.
The big question: why create a blog?
Why would anyone spend their precious time maintaining a blog?
Searching for “Why should I have a blog?” usually leads to answers related to business:
- Increase your visibility
- Build credibility
- Demonstrate expertise
- Improve your online presence
Those can all be valid reasons.
But I also remember watching someone on YouTube criticizing blogs.
His argument was simple: everybody starts one, but very few people continue writing regularly.
As a result, many blogs end up looking like abandoned showcases of their authors.
And there is some truth in that.
A blog only becomes interesting if you keep feeding it with new ideas, experiments, and discoveries.
But during the same video, he also showed several interesting blogs containing unexpected and personal content.
That was a useful reminder:
blogging is still worthwhile, but it requires effort.
So why am I doing it?
First: it is an exercise
Because I work in IT, I wanted to understand how blogging platforms work.
What technologies are available?
How do static websites work?
How are they built and deployed?
How much can I customize?
Building the blog itself became part of the learning process.
Second: I wanted one place for several purposes
I wanted somewhere to publish articles about ideas, experiments, and discoveries.
But I also wanted somewhere to centralize my technical documentation.
A long time ago, when I was starting in IT and had absolutely no idea what I was doing, I simply stored my notes in OneNote.
Yes, I know.
I was young.
For technical documentation, that eventually became frustrating:
- No proper version control
- Poor code highlighting
- Proprietary storage
- Awkward sharing
- Copy/paste sometimes turning code into images
- Search that did not always give useful results
- Increasing difficulty keeping years of notes organized
Over time, my personal documentation kept growing, and maintaining it became tedious.
I wanted something simpler.
My requirements were roughly:
- Markdown
- Editable from a normal text editor or terminal
- Version controlled
- Easy to search
- Easy to publish when I want to share something
- Portable between hosting solutions
- Open source whenever possible
At first, I considered moving everything into Markdown and publishing it with something like mdBook.
That would have worked well for documentation.
But I also wanted to write less formal posts about ideas, experiments, and personal reflections.
That pushed me toward a blog.
So, let’s build one.
It is also a way to open the door to the 🌏.
Where should I host my blog?
My first thought was naturally:
self-host it.
A VPS would give me complete control.
But that also means maintaining another server, web service, TLS configuration, backups, updates, monitoring, and so on.
That seemed like unnecessary work for a static blog.
There are easier solutions such as WordPress, HubSpot, and other CMS platforms.
They handle most of the infrastructure for you.
But I was not completely comfortable with the idea of building everything around a platform that could become difficult to leave later.
Yes, content can usually be exported.
But exporting from one CMS and importing into another often means:
- Converting formats
- Fixing layouts
- Recovering metadata
- Rebuilding themes
- Understanding why something does not render correctly anymore
I wanted to keep vendor lock-in as low as possible.
My content should remain simple files that I own.
GitHub Pages
GitHub provides free static-site hosting through GitHub Pages.
One major advantage is that the website can live directly next to its source code.
That means:
1Markdown
2 │
3 ▼
4Git repository
5 │
6 ▼
7GitHub Actions
8 │
9 ▼
10Static website
The entire site remains portable.
If I decide to move away from GitHub later, I still have:
- My Markdown files
- My configuration
- My theme
- My assets
- My build process
I can rebuild the same website somewhere else.
Another useful aspect is GitHub Actions, which lets us automate the build and deployment process.
GitHub Pages supports account or organization sites such as:
1https://username.github.io
and project sites such as:
1https://username.github.io/repository
The latter can also be useful for project documentation.
Which technology should I choose?
Static site generators are a natural companion to GitHub Pages.
The idea is straightforward:
1Markdown + templates + configuration
2 │
3 ▼
4 Static site generator
5 │
6 ▼
7 HTML / CSS / JS
The generated files can then be served by almost any web server.
For this first version of the blog, I chose:
- GitHub Pages
- Hugo
- Markdown
Markdown was particularly attractive because it keeps the content simple and portable.
One thing I appreciate about Markdown is how consistent documentation becomes.
Instead of thinking constantly about presentation, I can focus on content.
Why Hugo?
Working with Hugo is convenient.
While writing an article, I can simply run:
1hugo server -D
and preview the entire website locally, including draft posts.
I also considered other static-site generators.
Jekyll is historically very well integrated with GitHub Pages and uses Ruby.
Zola was another interesting option, but at the time I found fewer themes that matched what I wanted.
Hugo was popular, fast, and had plenty of documentation, tutorials, and themes available.
That was enough for me.
Keep it simple.
Which theme should I use?
Choosing a theme may sound trivial, but good documentation makes a huge difference.
Some themes look great but provide almost no documentation beyond pointing you back to the Hugo documentation.
I wanted a few basic features:
- Table of contents
- Multilingual support
- Local search
- Syntax highlighting
- Comments
- Font-size controls
- Responsive layout
- Light and dark modes
Nothing extraordinary.
Just the features I expected from a technical blog.
For the first version, I chose Hugo Theme Bootstrap.
It looked fairly classical, but it was efficient and included many useful widgets.
It was also relatively easy to configure and extend.
Here is a quick overview of what the theme provided:
Let’s build it
Everything below describes the setup I used for this first version of the blog.
The commands and configuration are specific to the version of Hugo Theme Bootstrap I was using at the time, so newer versions or different themes may require changes.
Prerequisites
The original setup required:
- Node.js
- npm
- Go
- Dart Sass
- Hugo Extended
The installation was done from Ubuntu running under WSL.
1# Install Node.js, npm and Git
2sudo apt install nodejs npm git
3
4# Install Go
5wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz
6sudo tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz
7export PATH=$PATH:/usr/local/go/bin
8
9# Install Dart Sass
10DART_SASS_VERSION="1.66.1"
11curl -LJO https://github.com/sass/dart-sass/releases/download/${DART_SASS_VERSION}/dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz
12tar -xf dart-sass-${DART_SASS_VERSION}-linux-x64.tar.gz
13sudo cp -r dart-sass/* /usr/local/bin
14rm -rf dart-sass*
15
16# Install Hugo Extended (.deb)
17HUGO_VERSION="0.117.0"
18curl -LJO https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb
19sudo apt install -y ./hugo_extended_${HUGO_VERSION}_linux-amd64.deb
20
21# Install Hugo Extended on RHEL 9
22HUGO_VERSION="0.135.0"
23curl -LJO https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz
24tar -xzf hugo_extended_${HUGO_VERSION}_linux-amd64.tar.gz
25sudo mv hugo /usr/bin/hugo
26
27hugo version
At the time, my Ubuntu installation reported:
1hugo v0.117.0-b2f0696cad918fb61420a6aff173eb36662b406e+extended linux/amd64 BuildDate=2023-08-07T12:49:48Z VendorInfo=gohugoio
You could also install some of the tools using Snap:
1sudo snap install dart-sass
2sudo snap install hugo
3
4which hugo
5/snap/bin/hugo
6
7hugo version
Creating the project
Create an empty GitHub repository and clone it locally.
For this version of the site, the theme was installed as a Git submodule:
1cd myblog
2
3git submodule add https://github.com/razonyang/hugo-theme-bootstrap themes/hugo-theme-bootstrap
4
5git clone https://github.com/razonyang/hugo-theme-bootstrap-skeleton /tmp/hbs-skeleton
6
7mkdir config
8
9cp -a /tmp/hbs-skeleton/config/* ./config
10cp -r /tmp/hbs-skeleton/content/* ./content
11cp -r /tmp/hbs-skeleton/archetypes/* ./archetypes
12cp -r /tmp/hbs-skeleton/static/* ./static
13cp -r /tmp/hbs-skeleton/assets/* ./assets
14
15sed -i "s/theme:.*/theme: hugo-theme-bootstrap/g" config/_default/config.yaml
16
17hugo mod npm pack
18npm install
19
20hugo server
At that point, the first version of the blog was already running locally.
A few settings
Two important configuration files were:
1author.yaml
2params.yaml
author.yaml contained information about the author and social links.
params.yaml controlled global appearance and theme options.
Add another language
Languages were declared inside:
1./config/_default/languages.yaml
Additional configuration and menu files could then be created for each language.
For example:
1config git:main ❯ tree -L 2
2.
3├── _default
4│ ├── author.yaml
5│ ├── config.fr.yaml
6│ ├── config.pl.yaml
7│ ├── config.yaml
8│ ├── languages.yaml
9│ ├── menu.en.yaml
10│ ├── menu.fr.yaml
11│ ├── menu.pl.yaml
12│ ├── params.yaml
13│ ├── server.yaml
14│ └── social.yaml
15└── production
16 ├── config.yaml
17 └── params.yaml
Translated content could then use files such as:
1index.md
2index.fr.md
3index.pl.md
A word about Giscus
The theme supported Giscus, a comment system based on GitHub Discussions.
Comments posted on the blog are stored as discussions in the GitHub repository.
To configure it, I needed to:
- Make the repository public
- Enable GitHub Discussions
- Install the Giscus GitHub App
- Configure the application for the blog repository
- Provide the repository information in the Hugo configuration
For example:
1# See https://giscus.app
2giscus:
3 repo: "MozeBaltyk/mozebaltyk.github.io"
4 repoId: "R_kgDOKJSCfA"
5 category: "General"
6 categoryId: "DIC_kwDOKJSCfM4CYvA_"
Visitors need a GitHub account to post comments through Giscus.
Change the table of contents
At the time, the table of contents displayed headings from specific levels, and I did not find a theme parameter in ./config/_default/params.yaml to change all of its behavior directly.
This was one of those small details that required looking beyond the theme’s main configuration.
Change syntax highlighting
Hugo can generate Chroma stylesheets.
For example:
1hugo gen chromastyles --style=dracula > assets/main/scss/_highlight.scss
That allowed me to customize code highlighting.
Add extra icons
Additional Font Awesome icons could be imported through:
1./assets/icons/custom.js
For example:
1import {
2 faBlog,
3 faBook,
4 faFile,
5 faNewspaper,
6 faAnchor,
7 faInfinity,
8 faCode,
9 faBug,
10 faLightbulb,
11 faTerminal,
12} from '@fortawesome/free-solid-svg-icons';
13
14const icons = [
15 faBook,
16 faBlog,
17 faFile,
18 faNewspaper,
19 faAnchor,
20 faInfinity,
21 faCode,
22 faBug,
23 faLightbulb,
24 faTerminal,
25];
26
27export default icons;
Those icons could then be reused by the theme.
Writing articles
Of course, you can write articles with Vim, Neovim, or whichever editor you prefer.
One simple approach is to use one directory per article.
For multilingual content:
1hugo new news/new-post/index.md
2hugo new news/new-post/index.fr.md
3hugo new news/new-post/index.pl.md
Another approach is to organize several articles under a section:
1vi docs/Devops/Containers/_index.md
2
3hugo new docs/Devops/Containers/docker.md
4hugo new docs/Devops/Containers/podman.md
In Hugo, _index.md can define a section.
New posts are usually created as drafts.
To preview drafts:
1hugo server -D
Before publishing, either remove the draft parameter or set:
1draft: false
Images
There were several ways to manage images.
One approach was to store an image under the static directory and reference it from the front matter.
For example:
1---
2title: 📡 The Bad, the Good and the Ugly Git
3# [...]
4authors:
5 - mozebaltyk
6images:
7 - ./bad-good-ugly-git/carousel.webp
8---
Another approach was to keep images directly next to the article as page resources.
The theme could automatically recognize filenames such as:
1feature.*
2cover.*
3thumbnail.*
These resources could then be resized into several versions for different screen sizes.
Other article images could simply be stored in the article directory and referenced from Markdown:
1
Organization
Hugo taxonomies help classify relationships between pieces of content.
For this blog, the main concepts were:
- Series
- Categories
- Tags
- Featured posts
Understanding these early makes the site much easier to organize as the number of articles grows.
Publishing the blog
For deployment, I used GitHub Actions to build the Hugo website and publish it through GitHub Pages.
My workflow also evolved over time.
Initially, every push triggered a deployment.
That was convenient, but it gave me almost no time to reread an article after committing it.
I later experimented with manual deployment using:
1workflow_dispatch
Eventually, I moved toward a branch-based workflow where publication happens only after changes reach the branch used for production.
Branch protection can also help prevent accidental publication.
For example:
Lock branch
prevents direct modifications to a protected branch.
Another useful setting is:
Require a pull request before merging
which encourages reviewing changes before publication.
I will not include the entire workflow here.
The current workflow files can be found in the blog repository:
At a high level, the workflow contained two jobs:
1jobs:
2
3 # Build job
4 build:
5 runs-on: ubuntu-latest
6
7 env:
8 HUGO_VERSION: 0.117.0
9
10 steps:
11 - name: Install Hugo CLI
12
13 - name: Checkout 🛎️
14
15 - name: Setup Node
16
17 - name: Cache dependencies
18
19 - name: Install dependencies
20
21 - name: Setup Hugo
22
23 - name: Setup Pages
24
25 - name: Install Node.js dependencies
26
27 - name: Build with Hugo
28
29 - name: Upload artifact
30
31 # Deployment job
32 deploy:
33 environment:
34 name: github-pages
35 url: ${{ steps.deployment.outputs.page_url }}
36
37 runs-on: ubuntu-latest
38 needs: build
39
40 steps:
41 - name: Deploy to GitHub Pages 🚀
42 id: deployment
43 uses: actions/deploy-pages@v2
Conceptually:
1Markdown / Hugo
2 │
3 ▼
4 Git repository
5 │
6 ▼
7GitHub Actions
8 │
9 ├── Build
10 │
11 ▼
12 Static files
13 │
14 ▼
15 GitHub Pages
Updating the blog
Some generated files and dependencies should not be stored in Git.
My .gitignore included:
1.hugo_build.lock
2hugo_stats.json
3node_modules/
4resources/
The original theme was installed as a Git submodule, so updating it looked like this:
1cd themes/hugo-theme-bootstrap
2
3git fetch
4git checkout [version]
5
6cd ../../
7
8hugo mod npm pack
9npm update
10
11git add \
12 themes/hugo-theme-bootstrap \
13 package.hugo.json \
14 package.json \
15 package-lock.json
16
17git commit -m 'Bump theme to [version]'
Looking back
This setup was the first version of the blog.
It was not perfect, and the theme was eventually replaced, but the important decisions survived:
- Write content in Markdown
- Keep everything in Git
- Generate a static website
- Automate deployment
- Avoid unnecessary vendor lock-in
- Keep the content portable
The implementation has changed since then.
The philosophy has not changed very much.
That is probably the most interesting part of looking back at this first version.
💡 Bonus point
For anyone who made it all the way to the end:
do not forget to add a few ridiculous Markdown emojis to your posts. 😄








