242 lines
9.3 KiB
Markdown
242 lines
9.3 KiB
Markdown
---
|
||
title: Hello, Friend
|
||
description: So I've decided to have a blog
|
||
slug: hello-friend
|
||
date: 2025-04-20 22:28:00+0200
|
||
categories:
|
||
- Technology
|
||
- Projects
|
||
tags:
|
||
- homelab
|
||
- self-hosting
|
||
- docker-compose
|
||
- Hugo
|
||
---
|
||
|
||
## Introduction
|
||
This is -- technically -- not my first time hosting a blog, but it *is* the
|
||
first time this decade.
|
||
|
||
The first time I tried hosting a blog was in 2009, the year I got my domain.
|
||
The domain came with an introductory offer of free web hosting for a year. So I
|
||
figured out just enough MySQL and FTP to yeet a WordPress installation on there
|
||
and make it run. As I remember it, I got as far as installing a custom theme I
|
||
really liked and writing maybe three posts, all within the first month. Then
|
||
the blog just sat there until the hosting expired, untouched.
|
||
|
||
The second time was a bit later, after I got into self-hosting. The Internet
|
||
Archive says it was 2018 [[1]], so it must be true. That time, I never
|
||
actually posted anything—I was mainly just interested in deploying the site.
|
||
|
||
Which brings us to now: the third attempt. This time, the marginal cost of
|
||
setting up a blog is negligible since I’m already self-hosting plenty of
|
||
services for personal use. That doesn’t mean I’ll be more active than before,
|
||
but the blog will probably stay up longer than a few months.
|
||
|
||
## Why a blog?
|
||
What reasons do I have for keeping a blog, aside from the challenge of setting
|
||
up the infrastructure and implementation?
|
||
|
||
I guess I have a confession to make. Though maybe it’s not really a
|
||
confession—because I think it’s true for pretty much every technically inclined
|
||
person in a technical field for the love of it: I have way too many personal
|
||
projects. Some of them I finish, but most never get touched.
|
||
|
||
I see the blog as a way of keeping myself accountable: keep projects small
|
||
enough to fit in a blog post (or make them blog-post-divisible), and prioritize
|
||
the ones that are interesting enough to write about.
|
||
|
||
Also, finishing a personal project with a blog post gives it a sense of
|
||
closure. It’s like informal documentation or a post-mortem analysis. There’s
|
||
always stuff I’d like to improve, but if I keep focusing on the same thing
|
||
forever, the list of things I want to do will never shrink.
|
||
|
||
Another benefit of writing everything down: it’ll serve as a reference for
|
||
future-me when I inevitably forget what I did or how I did it. Thinking of
|
||
future-me using this as a reference also forces present-me to be a bit more
|
||
thorough, even if I don’t expect many people to read it.
|
||
|
||
And finally, as always, it’s about the journey, not the destination. I’m not
|
||
expecting every post here to be about a completed project—but they will all be
|
||
about something I found interesting enough to write down.
|
||
|
||
### Yes, but why a **blog**?
|
||
All those reasons are fine, but none of them actually require a blog. I could
|
||
write tweets (lol), Facebook posts, TikToks. Even if I want to self-host, I
|
||
could use Mastodon instead of a blog.
|
||
|
||
A blog is… *old people media*. And I’m not *old people*, right?
|
||
|
||
In this case (and not only… *womp womp*) I am old people. I still prefer the idea
|
||
of smaller, distributed, simple sites and services like in the old internet,
|
||
rather than the centralized, data-mining, attention-grabbing mess we have now.
|
||
Also, Mastodon just doesn’t feel like the right format. It’s definitely what
|
||
I’d use instead of Twitter, but not for “documentation.”
|
||
|
||
A blog feels more fitting. It’s extremely free-form, no size limits either way.
|
||
I can define groups, filters, organize stuff with labels…
|
||
|
||
## How a blog
|
||
Now that I'm settled on the *why*, let's take a look at the *how*.
|
||
|
||
### Different concepts (CMS vs static websites)
|
||
Keeping with the “old internet” spirit, one of the first requirements I nailed
|
||
down—besides self-hosting—was that the blog should be statically generated. I
|
||
did try deploying Ghost [[2]], and I appreciated its polish (everything looks
|
||
great on that platform). But I quickly realized any CMS would be overkill for
|
||
what I’m trying to do, and it’d diverge from the simple, GitOps-style
|
||
deployment I want.
|
||
|
||
So on one hand, CMSs are polished but come with way too many features I won’t
|
||
use. On the other hand, using a static site generator lets me write posts in
|
||
Markdown, push them to git, and have them deployed automatically with CI/CD.
|
||
EZ.
|
||
|
||
### The vast world of static website generators
|
||
Once I decided to go with a static site generator, I discovered—of course—there
|
||
are a lot of them. There’s even an `awesome` GitHub page [[3]] listing a bunch,
|
||
but it was honestly overwhelming.
|
||
|
||
After some browsing, mostly through Google and Reddit, I narrowed it down to
|
||
two main contenders: Jekyll and Hugo.
|
||
|
||
Jekyll is the older, more established option. Tons of GitHub stars, tons of
|
||
themes.
|
||
|
||
Hugo is the newer option—fewer stars, but written in Go and supposedly much
|
||
faster. Looked solid, too.
|
||
|
||
#### Decision
|
||
For my use case, the differences weren’t that important. Faster compilation
|
||
doesn’t matter when deploying via CI/CD. The language it’s written in doesn’t
|
||
make a real difference.
|
||
|
||
In the end, I just looked for themes. I stumbled on this one called Chirpy
|
||
[[4]] and liked it best. So: Jekyll it is.
|
||
|
||
#### Decision, revisited
|
||
Or… is it?
|
||
|
||
One thing I kept noticing when looking up “Jekyll vs Hugo” was how everyone
|
||
wrote blog posts about migrating from Jekyll to Hugo. I found none going the
|
||
other way. And everyone mentioned how annoying the migration was.
|
||
|
||
So I figured, why not try Hugo before committing? I quickly deployed a Hugo
|
||
site and… yeah, it compiled much faster, even with just three articles. Went
|
||
from four seconds to under one. Not a big deal now, but it scales.
|
||
|
||
Plus: Hugo auto-refreshes the page on save (nice), and it’s more
|
||
self-contained—easier to install and maintain, which is great for CI.
|
||
|
||
Long story short: I found a Hugo theme similar to Chirpy and migrated
|
||
everything over even before the blog technically went live. Win-win?
|
||
|
||
### Infrastructure
|
||
Both of the themes I looked at are designed for easy GitHub Pages deployment.
|
||
Fork the theme, add your content, push—CI/CD handles the rest.
|
||
|
||
But where’s the fun in that? Hosting it on GitHub isn't very “old internet”,
|
||
and I’m already self-hosting my own git forge, so everything’s ready to go.
|
||
|
||
#### Self-Hosting
|
||
I had a few options for deployment. My first idea was to go self-contained:
|
||
after the site is built, the files get copied into an nginx container and
|
||
pushed to my forgejo container registry. Then it’s deployed via Docker Compose
|
||
and updated by Renovate.
|
||
|
||
I tried this—and while it’s straightforward to implement—it didn’t sit well
|
||
with me. Renovate only updates the digest after a new image is published, so
|
||
every deployment meant two commits: noisy. Also, building a whole nginx image
|
||
just to serve a few static files felt redundant—especially when I already have
|
||
an nginx image serving static pages for another service.
|
||
|
||
So I went with a more “traditional” route: after the site is built, it’s copied
|
||
via SSH to a target machine where the folder is mounted to the existing nginx
|
||
container. Easier, faster, less overhead.
|
||
|
||
In the end, the meat of the deployment is in the publish workflow, which can
|
||
also be referenced in the repository itself [[5]]:
|
||
|
||
```yaml
|
||
name: "Build and deploy website"
|
||
|
||
on:
|
||
push:
|
||
branches:
|
||
- main
|
||
paths-ignore:
|
||
- .gitignore
|
||
- README.md
|
||
- LICENSE
|
||
|
||
# Allow one concurrent deployment
|
||
concurrency:
|
||
group: "blog"
|
||
cancel-in-progress: true
|
||
|
||
jobs:
|
||
build-push:
|
||
name: Build and Push Hugo Site
|
||
runs-on: ubuntu-latest
|
||
|
||
steps:
|
||
- name: Checkout
|
||
uses: actions/checkout@v4
|
||
with:
|
||
fetch-depth: 0
|
||
|
||
# Build and test site
|
||
|
||
- name: Cache Hugo resources
|
||
uses: actions/cache@v4
|
||
env:
|
||
cache-name: cache-hugo-resources
|
||
with:
|
||
path: resources
|
||
key: ${{ env.cache-name }}
|
||
|
||
- uses: actions/setup-go@v5
|
||
with:
|
||
go-version: "^1.17.0"
|
||
- run: go version
|
||
|
||
- name: Setup Hugo
|
||
uses: https://github.com/peaceiris/actions-hugo@v2
|
||
with:
|
||
hugo-version: "latest"
|
||
extended: true
|
||
|
||
- name: Build
|
||
run: hugo --minify --gc
|
||
|
||
- name: Copy website to destination server
|
||
uses: https://github.com/garygrossgarten/github-action-scp@release
|
||
with:
|
||
local: public
|
||
remote: /srv/docker/nginx/html/blog
|
||
host: ${{ secrets.SSH_HOST }}
|
||
port: ${{ secrets.SSH_PORT }}
|
||
username: ${{ secrets.SSH_USER }}
|
||
privateKey: ${{ secrets.SSH_PRIVATE_KEY }}
|
||
rmRemote: true
|
||
```
|
||
|
||
## Rules... Nay, guidelines
|
||
- I don’t do projects to write a blog post. I write blog posts to document
|
||
projects.
|
||
|
||
- No promises about future posts. That **never** works out (I’ll explain why
|
||
that is… in a future post).
|
||
|
||
## What to expect, and how often can be expected
|
||
No expectations. No schedule. No guarantees of quality or consistency.
|
||
|
||
**Postscriptum**: I started writing this post on April 7th, but only managed to
|
||
publish it now. Is that an indicator of how often I'm going to actually publish
|
||
stuff? Yes, probably it is. Aaaaanyway, I'm out.
|
||
|
||
[1]: https://web.archive.org/web/20180810023635/http://martin.md/
|
||
[2]: https://ghost.org/
|
||
[3]: https://github.com/myles/awesome-static-generators
|
||
[4]: https://chirpy.cotes.page/
|
||
[5]: https://git.martin.md/radu/blog
|