<?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>GitHub Pages on Omegion</title><link>https://omegion.dev/tags/github-pages/</link><description>Recent content in GitHub Pages on Omegion</description><generator>Hugo</generator><language>en-us</language><copyright>Omegion</copyright><lastBuildDate>Thu, 27 Aug 2026 09:08:45 +0200</lastBuildDate><atom:link href="https://omegion.dev/tags/github-pages/index.xml" rel="self" type="application/rss+xml"/><item><title>How to Host a Personal Blog with Hugo on GitHub Pages</title><link>https://omegion.dev/2024/02/how-to-host-a-personal-blog-with-hugo-on-github-pages/</link><pubDate>Tue, 13 Feb 2024 00:00:00 +0000</pubDate><guid>https://omegion.dev/2024/02/how-to-host-a-personal-blog-with-hugo-on-github-pages/</guid><description>Building a Hugo blog from scratch and hosting it for free on GitHub Pages - a new site, a first post, a GitHub Actions deploy, and a theme, the same setup this blog runs on.</description><content:encoded><![CDATA[<h2 id="introduction">Introduction</h2>
<p>I run EKS clusters and edge infrastructure for a living, and I still don&rsquo;t
want to run or pay for any of it on my own time. That&rsquo;s a rule I keep for
side projects: no self-hosting, no Kubernetes, no Cloudflare Workers, nothing
with a bill attached. When I started this blog in 2020 that rule meant two
decisions: what to write it in, and where to put it. I landed on Hugo and
GitHub Pages, and this post is the whole path from an empty folder to a live
site, the same setup <a href="https://omegion.dev">omegion.dev</a>
 runs on today.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ol>
<li><a href="https://gohugo.io/installation/" target="_blank" rel="noopener noreferrer">Hugo Extended</a>
, <code>0.146.0</code> or newer. On
macOS: <code>brew install hugo</code>.</li>
<li>Git.</li>
<li>A GitHub account, and an empty repository created there to push this
site into.</li>
</ol>
<h2 id="what-is-hugo">What is Hugo</h2>
<p>Hugo is a static site generator written in Go. You write content in
Markdown, Hugo runs it through a set of templates, and the output is plain
HTML files, nothing server-side, no database, no request-time rendering. It
ships as a single binary, no Node toolchain, no Ruby, nothing to install
beyond the binary itself. I&rsquo;m a Go person, so that was an easy sell on its
own, but the part that actually matters for &ldquo;no infrastructure&rdquo; is the
output: a folder of HTML files is the cheapest possible thing to host,
because it doesn&rsquo;t need a running process at all.</p>
<h2 id="create-a-new-site">Create a new site</h2>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">shell</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">❯ hugo new site myblog
</span></span><span class="line"><span class="cl">Congratulations! Your new Hugo project was created in /Users/hakan/myblog.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Just a few more steps...
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">1. Change the current directory to /Users/hakan/myblog.
</span></span><span class="line"><span class="cl">2. Create or install a theme:
</span></span><span class="line"><span class="cl">   - Create a new theme with the <span class="nb">command</span> <span class="s2">&#34;hugo new theme &lt;THEMENAME&gt;&#34;</span>
</span></span><span class="line"><span class="cl">   - Or, install a theme from https://themes.gohugo.io/
</span></span><span class="line"><span class="cl">3. Edit hugo.toml, setting the <span class="s2">&#34;theme&#34;</span> property to the theme name.
</span></span><span class="line"><span class="cl">4. Create new content with the <span class="nb">command</span> <span class="s2">&#34;hugo new content &lt;SECTIONNAME&gt;/&lt;FILENAME&gt;.&lt;FORMAT&gt;&#34;</span>.
</span></span><span class="line"><span class="cl">5. Start the embedded web server with the <span class="nb">command</span> <span class="s2">&#34;hugo server --buildDrafts&#34;</span>.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">See documentation at https://gohugo.io/.</span></span></code></pre></div>
</div>
<p>That scaffolds <code>content/</code>, <code>layouts/</code>, <code>static/</code>, <code>archetypes/</code>, and a
<code>hugo.toml</code> with just a <code>baseURL</code>, <code>title</code>, and <code>locale</code>. <code>cd myblog</code> and
turn it into a git repo:</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">shell</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">git init
</span></span><span class="line"><span class="cl">git add .
</span></span><span class="line"><span class="cl">git commit -m <span class="s2">&#34;Initial commit&#34;</span></span></span></code></pre></div>
</div>
<h2 id="write-your-first-post">Write your first post</h2>
<p><code>hugo new content</code> uses the default archetype to stamp out a new page with
frontmatter already filled in:</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">shell</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">❯ hugo new content posts/hello-world.md
</span></span><span class="line"><span class="cl">Content <span class="s2">&#34;/Users/hakan/myblog/content/posts/hello-world.md&#34;</span> created</span></span></code></pre></div>
</div>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">toml</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="err">+++</span>
</span></span><span class="line"><span class="cl"><span class="nx">date</span> <span class="p">=</span> <span class="s1">&#39;2026-08-27T08:56:30+02:00&#39;</span>
</span></span><span class="line"><span class="cl"><span class="nx">draft</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="nx">title</span> <span class="p">=</span> <span class="s1">&#39;Hello World&#39;</span>
</span></span><span class="line"><span class="cl"><span class="err">+++</span></span></span></code></pre></div>
</div>
<p>Add a line or two of body text under that frontmatter, and flip <code>draft</code> to
<code>false</code> once you actually want it live, Hugo skips draft content by default
outside of local preview.</p>
<h2 id="build-it-locally">Build it locally</h2>
<p><code>hugo server --buildDrafts</code> runs a local dev server with live reload:</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">shell</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">❯ hugo server --buildDrafts
</span></span><span class="line"><span class="cl">Watching <span class="k">for</span> changes in /Users/hakan/myblog/<span class="o">{</span>archetypes,assets,content,data,i18n,layouts,static<span class="o">}</span>
</span></span><span class="line"><span class="cl">Watching <span class="k">for</span> config changes in /Users/hakan/myblog/hugo.toml
</span></span><span class="line"><span class="cl">Start building sites …
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">WARN  found no layout file <span class="k">for</span> <span class="s2">&#34;html&#34;</span> <span class="k">for</span> kind <span class="s2">&#34;home&#34;</span>: You should create a template file which matches Hugo Layouts Lookup Rules <span class="k">for</span> this combination.
</span></span><span class="line"><span class="cl">WARN  found no layout file <span class="k">for</span> <span class="s2">&#34;html&#34;</span> <span class="k">for</span> kind <span class="s2">&#34;page&#34;</span>: You should create a template file which matches Hugo Layouts Lookup Rules <span class="k">for</span> this combination.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">                  │ EN
</span></span><span class="line"><span class="cl">──────────────────┼────
</span></span><span class="line"><span class="cl"> Pages            │  <span class="m">5</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Web Server is available at http://localhost:1313/
</span></span><span class="line"><span class="cl">Press Ctrl+C to stop</span></span></code></pre></div>
</div>
<p>Those <code>WARN</code> lines are expected right now. A freshly scaffolded site has no
theme, so there&rsquo;s nothing telling Hugo how to turn a post into a page. It&rsquo;ll
build, the server will start, and the browser tab will be blank. That gets
fixed in the last section, once the deploy pipeline is in place.</p>
<p>For a real build instead of the dev server:</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">shell</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">❯ hugo --gc --minify</span></span></code></pre></div>
</div>
<p>That writes the finished site to <code>public/</code>, the same directory the deploy
workflow below picks up and publishes.</p>
<h2 id="deploy-it-to-github-pages-with-github-actions">Deploy it to GitHub Pages with GitHub Actions</h2>
<p>Push the repo to GitHub, then add a workflow that builds the site and
deploys it on every push, this is the exact file I run for this blog:</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">yaml</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="c"># .github/workflows/deploy.yaml</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Deploy Hugo site to Pages</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">on</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">push</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">branches</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="l">master</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">workflow_dispatch</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">permissions</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">contents</span><span class="p">:</span><span class="w"> </span><span class="l">read</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">pages</span><span class="p">:</span><span class="w"> </span><span class="l">write</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">id-token</span><span class="p">:</span><span class="w"> </span><span class="l">write</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">concurrency</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">group</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;pages&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">cancel-in-progress</span><span class="p">:</span><span class="w"> </span><span class="kc">false</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="nt">jobs</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">build</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">HUGO_VERSION</span><span class="p">:</span><span class="w"> </span><span class="m">0.165.0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Install Hugo CLI</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb
</span></span></span><span class="line"><span class="cl"><span class="sd">          sudo dpkg -i ${{ runner.temp }}/hugo.deb</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Checkout</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/checkout@v7</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">fetch-depth</span><span class="p">:</span><span class="w"> </span><span class="m">0</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Setup Pages</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">pages</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/configure-pages@v6</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Build with Hugo</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">env</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">HUGO_ENVIRONMENT</span><span class="p">:</span><span class="w"> </span><span class="l">production</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">HUGO_ENV</span><span class="p">:</span><span class="w"> </span><span class="l">production</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">run</span><span class="p">:</span><span class="w"> </span><span class="p">|</span><span class="sd">
</span></span></span><span class="line"><span class="cl"><span class="sd">          hugo --gc --minify --baseURL &#34;${{ steps.pages.outputs.base_url }}/&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Upload artifact</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/upload-pages-artifact@v5</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">with</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">          </span><span class="nt">path</span><span class="p">:</span><span class="w"> </span><span class="l">./public</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">  </span><span class="nt">deploy</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">environment</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">github-pages</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">${{ steps.deployment.outputs.page_url }}</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">runs-on</span><span class="p">:</span><span class="w"> </span><span class="l">ubuntu-latest</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">needs</span><span class="p">:</span><span class="w"> </span><span class="l">build</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="nt">steps</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span>- <span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="l">Deploy to GitHub Pages</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">deployment</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">uses</span><span class="p">:</span><span class="w"> </span><span class="l">actions/deploy-pages@v5</span></span></span></code></pre></div>
</div>
<p><code>fetch-depth: 0</code> in the checkout step matters if you turn on
<code>enableGitInfo</code> later. Without full history Hugo can&rsquo;t read a page&rsquo;s last
commit date. One setting has to happen by hand: in the repo&rsquo;s <em>Settings &gt;
Pages</em>, set <em>Source</em> to <em>GitHub Actions</em>, not <em>Deploy from a branch</em>. That&rsquo;s
what lets <code>actions/deploy-pages</code> publish without a <code>gh-pages</code> branch or a
committed <code>CNAME</code> file at all. A custom domain is set in that same <em>Pages</em>
screen. GitHub writes and manages the <code>CNAME</code> file for you from there.</p>
<pre class="mermaid" id="mermaid-7"></pre>
<script>(window.__mermaidSources ||= {})["mermaid-7"] = "flowchart TD\n  Push[\"git push\"] --\u003e Build[\"GitHub Actions: hugo --minify\"]\n  Build --\u003e Artifact[\"Pages artifact (public/)\"]\n  Artifact --\u003e Pages[\"GitHub Pages\"]";</script>

<p>Push, and the Actions tab shows the build and deploy jobs running. A few
minutes later the site is live at your GitHub Pages URL, or your custom
domain if you set one, no server, no SSH key, no FTP, just Actions minutes
GitHub already gives away for free on a public repo.</p>
<h2 id="add-a-theme">Add a theme</h2>
<p>This is the piece that fixes the blank page and <code>WARN</code> lines from earlier.
Hugo doesn&rsquo;t ship with a default look, a theme is what supplies the actual
templates. <a href="https://themes.gohugo.io/" target="_blank" rel="noopener noreferrer">themes.gohugo.io</a>
 has a few hundred
to pick from, or you can build your own the way I did:
<a href="https://github.com/omegion/hugo-omegion" target="_blank" rel="noopener noreferrer">hugo-omegion</a>
, the theme this
blog runs. Sidebar-first layout, dark/light mode, client-side search with
no backend, Mermaid diagrams, syntax-highlighted code blocks with a copy
button, RSS, and the SEO/OpenGraph/JSON-LD metadata a blog actually needs.
MIT licensed, free to take.</p>
<p>Whichever theme you pick, I&rsquo;d recommend installing it as a Hugo Module
instead of the classic git submodule under <code>themes/</code>, it&rsquo;s one version
pinned in <code>go.mod</code> like any other dependency, instead of a submodule
pointer nobody remembers to update:</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">shell</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">hugo mod init github.com/&lt;you&gt;/myblog</span></span></code></pre></div>
</div>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">toml</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-toml" data-lang="toml"><span class="line"><span class="cl"><span class="c"># hugo.toml</span>
</span></span><span class="line"><span class="cl"><span class="nx">enableEmoji</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"><span class="nx">enableGitInfo</span> <span class="p">=</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[[</span><span class="nx">module</span><span class="p">.</span><span class="nx">imports</span><span class="p">]]</span>
</span></span><span class="line"><span class="cl"><span class="nx">path</span> <span class="p">=</span> <span class="s1">&#39;github.com/omegion/hugo-omegion&#39;</span></span></span></code></pre></div>
</div>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">shell</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">❯ hugo mod tidy
</span></span><span class="line"><span class="cl">go: added github.com/omegion/hugo-omegion v1.0.16
</span></span><span class="line"><span class="cl">hugo: collected modules in <span class="m">607</span> ms</span></span></code></pre></div>
</div>
<p>Rebuilding after that pulls in the theme&rsquo;s <code>layouts/</code>, <code>assets/</code>, and
<code>static/</code>, the <code>WARN</code> lines are gone and the page count jumps once there&rsquo;s
an actual template rendering each post:</p>
<div class="code-block">
  <div class="code-block-header">
    <span class="code-block-lang">shell</span>
    <button type="button" class="code-copy" aria-label="Copy code">
      <span class="code-copy-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
</span>
      <span class="code-copy-icon code-copy-icon-check"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>
</span>
      <span class="code-copy-label">Copy</span>
    </button>
  </div>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">❯ hugo --buildDrafts
</span></span><span class="line"><span class="cl">                  │ EN
</span></span><span class="line"><span class="cl">──────────────────┼────
</span></span><span class="line"><span class="cl"> Pages            │ <span class="m">11</span></span></span></code></pre></div>
</div>
<p>What a theme doesn&rsquo;t inherit automatically is the site&rsquo;s own config.
<code>enableGitInfo</code>, the <code>searchindex</code> output format a search box needs, and
<code>goldmark.renderer.unsafe = true</code> for raw HTML in posts all have to be set
in your own <code>hugo.toml</code>, not the theme&rsquo;s. hugo-omegion&rsquo;s README spells out
the exact block to copy. Skip one and the symptom is quiet: a blank search
box, no &ldquo;Updated&rdquo; date on a post, nothing that looks like an error.</p>
<p>Bumping the theme later is <code>hugo mod get -u github.com/omegion/hugo-omegion</code>,
a normal diff in <code>go.mod</code> and <code>go.sum</code>, the same motion as updating any
other Go module.</p>
<h2 id="conclusion">Conclusion</h2>
<p>That&rsquo;s the whole setup: a new Hugo site, a first post, a GitHub Actions
workflow that builds and deploys it on every push, and a theme on top. No
server to run, no database, nothing to patch, and no bill at the end of
the month. Push a commit and the site is live a few minutes later. For a
personal blog, that&rsquo;s exactly the amount of infrastructure I wanted: none.</p>
]]></content:encoded></item></channel></rss>