--- 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. You’ve been warned. **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