🌌 How I Created This Blog

The beginning of this blog — its first version.

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:

Center

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![Center](/HBS-list-feat.PNG#center)

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:

GitHub workflows

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. 😄

Sources

Sunday, October 4, 2026 Sunday, October 1, 2023