<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Hugo on brodsbytes blog</title>
    <link>https://brodsbytes.blog/tags/hugo/</link>
    <description>Recent content in Hugo on brodsbytes blog</description>
    <generator>Hugo</generator>
    <language>en-au</language>
    <lastBuildDate>Tue, 07 Jul 2026 17:01:32 +1000</lastBuildDate>
    <atom:link href="https://brodsbytes.blog/tags/hugo/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Tech Blog Stack in 2026</title>
      <link>https://brodsbytes.blog/posts/tech-blog-stack-in-2026/</link>
      <pubDate>Tue, 07 Jul 2026 17:01:32 +1000</pubDate>
      <guid>https://brodsbytes.blog/posts/tech-blog-stack-in-2026/</guid>
      <description>The stack I used to setup this tech blog in one afternoon</description>
      <content:encoded><![CDATA[<h2 id="the-stack-at-a-glance">The stack at a glance</h2>
<ul>
<li>Static site generator: <a href="https://gohugo.io/">Hugo</a> (extended)</li>
<li>Theme: <a href="https://github.com/adityatelange/hugo-PaperMod">PaperMod</a></li>
<li>Host: GitHub Pages, live at <code>brodsbytes.github.io/blog/</code> (optionally buy your own domain, i.e. <code>brodsbytes.blog</code>)</li>
<li>CI/CD: GitHub Actions, build + deploy on every git push</li>
</ul>
<h2 id="how-does-it-work">How does it work?</h2>
<p><strong>Hugo</strong> is a tool that turns Markdown files (.md) into a folder of HTML/CSS/JS files. This is a static state instead of a live web server or CMS rendering and serving the content (e.g. WordPress). Then, in our case, we add <strong>PaperMod</strong> on top as an optional theme for a better look and additional personalisation options. <strong>GitHub Actions</strong> is the automation in the middle - everytime we push changes to the GitHub repo, it automatically runs Hugo to Build the site again and hands over the output folder <code>./public</code> to GitHub Pages. <strong>GitHub Pages</strong> is the free web hosting that is serving those outputted files. Finally, adding your own <strong>Custom Domain</strong> on top, is essentially just pointing a friendlier name to where the files are on GitHub Pages.</p>
<p>Note, this exact stack isn&rsquo;t required. You don&rsquo;t need to use GitHub Pages for hosting or GitHub Actions for CI/CD, this is just the easiest and cheapest option in my scenario. There&rsquo;s also no lock in since it&rsquo;s easy to change hosting later - run Hugo on a new host, point your domain to the new site.</p>
<h2 id="why-this-stack">Why this stack</h2>
<p>For a basic tech blog, this makes sense. First, it&rsquo;s free to run - GitHub Pages hosting + GitHub Actions cost $0. The only optional spend is a domain which you can do after a test run. It&rsquo;s also simple, content is just .md files in git; the whole site is a folder of static files. Hugo takes your Markdown, outputs it into plain HTML/CSS/JS. No server, no database, no CMS. Iny opinion, the best benefit of this is security. There&rsquo;s nothing to patch with this approach - no server-side code, no plugins, no admin login to protect. Having ran a WordPress site previously, these were all things I needed to mitigate with hardening the admin login, automatic plugin updates and even additional security contorls. Less to tinker with, but also much more peace of mind. I&rsquo;d recommend grabbing a domain - <code>brodsbytes.blog</code> costs $20 a year and helps with SEO rankings. Having all the files of the site yourself makes it easy, no webhosting / vendor specific migrations when it comes time to change hosting.</p>
<h3 id="privacy-by-default">Privacy by default</h3>
<p>Another big plus for me - privacy. No Google AdSense, ad plugins or no third-party ad/tracking scripts. No visitor to my site will ever have advertising tracking as a result for visiting my site. I will setup analytics with Cloudflare as that&rsquo;s where my domain is bought from. However this is cookieless - no cross-site tracking and even better, no cookie consent banner needed!</p>
<p>Honourable mention goes to GoatCounter as an open-source, privacy-friendly fallback option. Cloudflare was just the easiest for me and saved me having another account.</p>
<h2 id="examples-of-other-live-sites-built-on-hugo">Examples of other live sites built on Hugo</h2>
<p>Ok cool so the tech checks out, but I&rsquo;ve never heard of this, are people actually using this? Or is it only used by a few nerds running blogs?</p>
<p>I&rsquo;m glad you asked! The stack is pretty versatile for most websites - it&rsquo;s not limited to blogs only. Here are some different examples:</p>
<ul>
<li><a href="https://www.testingwithmarie.com/posts/20241126-create-a-static-blog-with-hugo/">TestingWithMarie</a> - Actual installation guide I used as a reference, alongside seeing another solo indie style bloger has their site setup</li>
<li><a href="https://letsencrypt.org">Let&rsquo;s Encrypt</a> - The non-profit certificate authority that issues free TLS certificate, which could be credited to running much of the internets HTTPS (the S stands for Secure!). Their entire site source is also available on their <a href="https://github.com/letsencrypt/website">GitHub</a>, including their Docs. Very solid example of a highly credible and heavily used public site running Hugo.</li>
<li><a href="https://kubernetes.io/">kubernetes</a> - The industry standard, open source container orchestration platform for automating deployment, scaling, and management of containerized applications. More proof Hugo is scalable.</li>
</ul>
<h2 id="the-technical-bit-set-up-in-an-afternoon">The technical bit (set up in an afternoon)</h2>
<p>For the installation, I&rsquo;m not going to go into too much detail here. The documentation for both <a href="https://gohugo.io/installation/">Hugo</a> and <a href="https://github.com/adityatelange/hugo-PaperMod/wiki/Installation#installingupdating-papermod">PaperMod</a> are excellent. I just wanted to give the high level steps on what&rsquo;s involed, to show you it can be done in an afternoon.</p>
<ol>
<li>Install Hugo extended (extended needed for the PaperMod theme)</li>
<li>Add PaperMod (I did as a git submodule, the recommended method)</li>
<li>Push repo to GitHub, enable Pages, set the build source to GitHub Actions</li>
<li>Create Workflow file <code>.github/workflows/hugo.yaml</code> from Hugo&rsquo;s own official <strong>GitHub Pages</strong> template</li>
<li>End result: git push to <code>main</code> &gt; GitHub Actions builds &gt; deploys to GitHub Pages.</li>
</ol>
<h2 id="caveat-with-github-pages--public-repos">Caveat with GitHub Pages &amp; public repos</h2>
<p>Free GitHub Pages requires a <strong>public</strong> repo, this means whatever you commit is world-readable. I work directly out of the repo (single source of truth) instead of a separate &ldquo;clean&rdquo; publish directory. This raises a potential issue for publicly broadcasting my personal files by accident. <code>.gitignore</code> and a git pre-push hook mitigates most of the user error risk. My .gitignore prevents my drafts and internal notes pushing, which works alongside my pre-push script that checls only required files for the site are being pushed, and will error before pushing. This needs maintenance if you scale the site later, but im happy with the trade off in my case.</p>
<p>Why not make 2 directories? I dont wanan deal with copy/sync step, no &ldquo;which copy is current?&rdquo;, version control, etc. Also my drafts + research + published posts are all in one place which makes navigation easier when writing.</p>
<h4 id="gitignore-snippet">.gitignore snippet:</h4>
<pre tabindex="0"><code># Internal notes — local only, never published
docs/

# Work-in-progress posts — local only until moved up to content/posts/
content/posts/drafts/

# Hugo build output
public/
resources/_gen/
.hugo_build.lock
</code></pre><h4 id="git-pre-push-hook-snippet">git pre-push hook snippet:</h4>
<pre tabindex="0"><code>#!/usr/bin/env bash
# Pre-push allowlist guard: blocks pushing any file outside the paths a
# public Hugo blog repo strictly needs. Master copy lives in docs/hooks/
# (docs/ is gitignored); install by copying to .git/hooks/pre-push (+x).
# Bypass deliberately (only if you&#39;re sure): git push --no-verify
set -euo pipefail

ALLOW_REGEX=&#39;^(content/|archetypes/|layouts/|static/|assets/|data/|i18n/|\.github/|themes/PaperMod$|hugo\.yaml$|\.gitignore$|\.gitmodules$|README\.md$)&#39;

ZERO=0000000000000000000000000000000000000000
violations=&#34;&#34;

while read -r _local_ref local_sha _remote_ref remote_sha; do
  [ &#34;$local_sha&#34; = &#34;$ZERO&#34; ] &amp;&amp; continue   # branch deletion — nothing to check
  if [ &#34;$remote_sha&#34; = &#34;$ZERO&#34; ]; then
    # New branch: remote has nothing, so audit every file in the tree
    files=$(git ls-tree -r --name-only &#34;$local_sha&#34;)
  else
    files=$(git diff --name-only &#34;$remote_sha&#34; &#34;$local_sha&#34;)
  fi
  bad=$(echo &#34;$files&#34; | grep -Ev &#34;$ALLOW_REGEX&#34; || true)
  [ -n &#34;$bad&#34; ] &amp;&amp; violations=&#34;$violations$bad&#34;$&#39;\n&#39;
done

if [ -n &#34;${violations%$&#39;\n&#39;}&#34; ]; then
  echo &#34;PUSH BLOCKED — files outside the public allowlist:&#34; &gt;&amp;2
  echo &#34;$violations&#34; | sed &#39;/^$/d; s/^/  /&#39; &gt;&amp;2
  echo &#34;If intentional, update ALLOW_REGEX in .git/hooks/pre-push&#34; &gt;&amp;2
  echo &#34;(master copy: docs/hooks/pre-push), or bypass with --no-verify.&#34; &gt;&amp;2
  exit 1
fi
</code></pre><h2 id="my-actual-writing-workflow-so-far">My actual writing workflow (so far)</h2>
<p>Ok cool, what&rsquo;s it like actually writing blog posts? Here&rsquo;s my rough workflow - which i&rsquo;ve just used for the first time on this first blog post.</p>
<ol>
<li>Run <code>hugo new posts/drafts/my-post-title.md</code> to create new post from template in ./archetypes/. Adds front matter (draft: true, empty tags/summary) stamped automatically
<em>(Filename becomes the URL slug, try to aim for descriptive and searchable. e.g. <code>nas-backups-with-borg.md</code>, not <code>backup-post.md</code>)</em></li>
<li>I use VSCode when writing in the .md file: split screen editor window with one in Preview mode to see how it&rsquo;s being formatted.</li>
<li>Final formatting and structure check in browser, by running the Hugo server locally <code>hugo server -D</code>. This updates previewing after saving the document - VSCode is instant, which is why i&rsquo;m using that.</li>
<li>Fill <code>summary:</code> and <code>tags:</code> for SEO at the top of the .md file, set <code>draft: false</code></li>
<li>Publish: move the file out of the gitignored drafts folder into ./posts/, then commit + push.</li>
<li>GitHub Actions redeploys the new site in ~1 minute.</li>
</ol>
]]></content:encoded>
    </item>
  </channel>
</rss>
