<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Forgejo on Andrew's Memory Blog</title><link>https://andrewmemory.acornwall.net/tags/forgejo/</link><description>Recent content in Forgejo on Andrew's Memory Blog</description><generator>Hugo -- gohugo.io</generator><image><url>https://andrewmemory.acornwall.net/img/rss_image.png</url><title>Forgejo on Andrew's Memory Blog</title><link>https://andrewmemory.acornwall.net/</link></image><language>en</language><managingEditor>andrewmemoryblog@gmail.com (Andrew's Memory Blog)</managingEditor><webMaster>andrewmemoryblog@gmail.com (Andrew's Memory Blog)</webMaster><copyright>Copyright 2009--2025</copyright><lastBuildDate>Wed, 08 Apr 2026 23:14:09 -0700</lastBuildDate><atom:link href="https://andrewmemory.acornwall.net/tags/forgejo/index.xml" rel="self" type="application/rss+xml"/><item><title>Uploading to the webserver from Forgejo</title><link>https://andrewmemory.acornwall.net/blog/2026-04-08-uploading-from-forgejo-to-the-webserver/</link><pubDate>Wed, 08 Apr 2026 23:14:09 -0700</pubDate><author>andrewmemoryblog@gmail.com (Andrew's Memory Blog)</author><guid>https://andrewmemory.acornwall.net/blog/2026-04-08-uploading-from-forgejo-to-the-webserver/</guid><description>&lt;p&gt;To get Hugo up to the webserver, I had to scp the files up there using my keyfile. For that, I started with the image that I gave the &lt;code&gt;docker&lt;/code&gt; tag, which is one of the default Forgejo images.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Setting up variables
&lt;div id="setting-up-variables" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#setting-up-variables" aria-label="Anchor"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;First I set up a bunch of variables:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;WEBSERVER_SSH_USERNAME&lt;/code&gt;: my username on the webserver&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WEBSERVER_SSH_HOST&lt;/code&gt;: the hostname of the webserver I upload to&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WEBSERVER_HTML_DIR&lt;/code&gt;: the directory where the webserver files get sent&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 class="relative group"&gt;Setting up secrets
&lt;div id="setting-up-secrets" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#setting-up-secrets" aria-label="Anchor"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;I ended up with two secrets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SSH_PRIV_KEY&lt;/code&gt;: my private key&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SSH_KNOWN_HOSTS&lt;/code&gt;: I also needed to set up my &lt;code&gt;~/.ssh/known_hosts&lt;/code&gt; so that I wouldn&amp;rsquo;t get errors when connecting via scp. I temporarily threw away my existing &lt;code&gt;deploy.yaml&lt;/code&gt; and built a new one that did a little (very little) ssh using sftp:&lt;/li&gt;
&lt;/ul&gt;
&lt;figure class="highlight"&gt;
&lt;figcaption&gt;&lt;span&gt;&lt;mark&gt;.forgejo/workflows/deploy.yaml&lt;/mark&gt;&lt;/span&gt; &lt;/figcaption&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-YAML" data-lang="YAML"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;push]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;deployscp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;docker&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo dir &amp;gt; ftpbatch
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo bye &amp;gt;&amp;gt; ftpbatch&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;setup ssh&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; mkdir -p ~/.ssh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; chmod 0700 ~/.ssh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#34;${{ secrets.SSH_PRIV_KEY }}&amp;#34; &amp;gt; ~/.ssh/id_rsa_webserver
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; chmod 0600 ~/.ssh/id_rsa_webserver
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; ssh-keyscan ${{ vars.WEBSERVER_SSH_HOST }} &amp;gt;&amp;gt; ~/.ssh/known_hosts
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; cat ~/.ssh/known_hosts&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;push public&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo about to sftp
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; sftp -i ~/.ssh/id_rsa_webserver -b ./ftpbatch ${{ vars.WEBSERVER_SSH_USERNAME }}@${{ vars.WEBSERVER_SSH_HOST }}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo did sftp&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;p&gt;The first time I ran that, I copied the output of the &lt;code&gt;cat ~/.ssh/known_hosts&lt;/code&gt; line into the secret &lt;code&gt;SSH_KNOWN_HOSTS&lt;/code&gt;. Then I updated &lt;code&gt;deploy.yaml&lt;/code&gt; to use the secret instead:&lt;/p&gt;
&lt;figure class="highlight"&gt;
&lt;figcaption&gt;&lt;span&gt;&lt;mark&gt;.forgejo/workflows/deploy.yaml&lt;/mark&gt;&lt;/span&gt; &lt;/figcaption&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-YAML" data-lang="YAML"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;push]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;deployscp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;docker&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo dir &amp;gt; ftpbatch
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo bye &amp;gt;&amp;gt; ftpbatch&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;setup ssh&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; mkdir -p ~/.ssh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; chmod 0700 ~/.ssh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#34;${{ secrets.SSH_PRIV_KEY }}&amp;#34; &amp;gt; ~/.ssh/id_rsa_webserver
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; chmod 0600 ~/.ssh/id_rsa_webserver
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#34;${{ secrets.SSH_KNOWN_HOST }}&amp;#34; &amp;gt; ~/.ssh/known_hosts
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; chmod 0600 ~/.ssh/known_hosts&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;push public&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo about to sftp
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; sftp -i ~/.ssh/id_rsa_webserver -b ./ftpbatch ${{ vars.WEBSERVER_SSH_USERNAME }}@${{ vars.WEBSERVER_SSH_HOST }}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo did sftp&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;p&gt;I was hoping that Forgejo would preserve the line separators that I pasted into the secret dialog. It did! Nice!&lt;/p&gt;
&lt;h2 class="relative group"&gt;Add Hugo back to the build and really upload
&lt;div id="add-hugo-back-to-the-build-and-really-upload" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#add-hugo-back-to-the-build-and-really-upload" aria-label="Anchor"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;With a working sftp (and presumably a working ssh) and a good private key, I could add the &lt;code&gt;hugo&lt;/code&gt; step back. It packages up the build, then the &lt;code&gt;docker&lt;/code&gt; image retrieves the build and uploads the important bit. Here&amp;rsquo;s the whole enchilada:&lt;/p&gt;
&lt;figure class="highlight"&gt;
&lt;figcaption&gt;&lt;span&gt;&lt;mark&gt;.forgejo/workflows/deploy.yaml&lt;/mark&gt;&lt;/span&gt; &lt;/figcaption&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-YAML" data-lang="YAML"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;push]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;buildhugo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;hugo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;hugo version&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;submodules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;recursive&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;run hugo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;hugo --gc --minify&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;fix up RSS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;cp ./public/index.xml ./public/rss.xml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;stash public files&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;https://code.forgejo.org/forgejo/upload-artifact@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;public&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./public/ &lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;deployscp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;docker&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;needs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;buildhugo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;grab public files&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;https://code.forgejo.org/forgejo/download-artifact@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;setup ssh&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; mkdir -p ~/.ssh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; chmod 0700 ~/.ssh
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#34;${{ secrets.SSH_PRIV_KEY }}&amp;#34; &amp;gt; ~/.ssh/id_rsa_webserver
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; chmod 0600 ~/.ssh/id_rsa_webserver
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#34;${{ secrets.SSH_KNOWN_HOST }}&amp;#34; &amp;gt; ~/.ssh/known_hosts
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; chmod 0600 ~/.ssh/known_hosts&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;push public&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; ssh -i ~/.ssh/id_rsa_webserver ${{ vars.WEBSERVER_SSH_USERNAME }}@${{ vars.WEBSERVER_SSH_HOST }} &amp;#39;rm -rf /home/${{ vars.WEBSERVER_SSH_USERNAME }}/${{ vars.WEBSERVER_HTML_DIR }}/*&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; scp -r -i ~/.ssh/id_rsa_webserver ./public/* ${{ vars.WEBSERVER_SSH_USERNAME }}@${{ vars.WEBSERVER_SSH_HOST }}:/home/${{ vars.WEBSERVER_SSH_USERNAME }}/${{ vars.WEBSERVER_HTML_DIR }}/&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;p&gt;This makes it look easy. It wasn&amp;rsquo;t. I was nervous about the process, and started with &lt;code&gt;echo ssh -i ~/...&lt;/code&gt; and &lt;code&gt;echo scp -r -i ~/...&lt;/code&gt; instead of actually running the ssh and scp commands. I made sure things looked right in the Actions window on the Forgejo server before I did the scp for real, and it took me a couple more deep breaths before I did the ssh (which does a &lt;code&gt;rm -rf&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Ultimately, I had to run things 70 times — I counted — before I got a working deploy. Lots of &lt;code&gt;echo&lt;/code&gt; and &lt;code&gt;ls -al&lt;/code&gt; to make sure I was oriented. And probably a third of the commits were fixes for this error:&lt;/p&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Workflow was not executed due to an error that blocked the execution attempt.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Unable to parse supported events in workflow: yaml: line 25: found a tab character where an indentation space is expected&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;p&gt;I really need to set my Emacs up so it uses only spaces for yaml files. Esc-X untabify was my best friend.&lt;/p&gt;
&lt;p&gt;Now, I have a website that updates automatically when I push the code. Not when I commit — I&amp;rsquo;m still trying to remember that &lt;code&gt;git push origin&lt;/code&gt; after publishing. I think I&amp;rsquo;ll commit this and do that now.&lt;/p&gt;</description></item><item><title>Running Hugo from the runner on Forgejo</title><link>https://andrewmemory.acornwall.net/blog/2026-04-08-running-hugo-from-the-runner/</link><pubDate>Wed, 08 Apr 2026 22:33:06 -0700</pubDate><author>andrewmemoryblog@gmail.com (Andrew's Memory Blog)</author><guid>https://andrewmemory.acornwall.net/blog/2026-04-08-running-hugo-from-the-runner/</guid><description>&lt;p&gt;I&amp;rsquo;m taking a shortcut here. With the benefit of foresight which is really hindsight, I &lt;a href="https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/" &gt;set my runner up to run Hugo already&lt;/a&gt; with the label &lt;code&gt;hugo&lt;/code&gt;. So this is really about the automation I used to do the build. I started with attempts to install Hugo using &lt;code&gt;apk add&lt;/code&gt; with the Docker image. That was a side quest that led nowhere.&lt;/p&gt;
&lt;p&gt;Then when I decided to split things up, it took me a while to find the right image. The &lt;a href="https://hub.docker.com/r/gohugoio/hugo" target="_blank" rel="noreferrer"&gt;official Hugo Docker image&lt;/a&gt; luckily it includes Node.js so it can run the &lt;code&gt;checkout&lt;/code&gt; and &lt;code&gt;upload-artifact&lt;/code&gt; actions.&lt;/p&gt;
&lt;p&gt;However, you can&amp;rsquo;t do everything on the Hugo image. That means the build has to be split up into two parts. This part is only for running Hugo. Luckily, when it runs, Hugo can store the files it built (&lt;code&gt;./public/&lt;/code&gt;) in an artifact, so it&amp;rsquo;s not &lt;em&gt;too&lt;/em&gt; hard to get them later for uploading.&lt;/p&gt;
&lt;p&gt;You also need to check out the right thing. For Hugo with Blowfish installed, that means checking out submodules.&lt;/p&gt;
&lt;p&gt;If you want to avoid the shortcut and do what I really did, check out the &lt;a href="https://forgejo.org/docs/latest/user/actions/quick-start/" target="_blank" rel="noreferrer"&gt;Forgejo Actions quick start guide&lt;/a&gt; and build a &amp;ldquo;hello world&amp;rdquo; action first.&lt;/p&gt;
&lt;p&gt;This is the file I ended up creating in my local repo:&lt;/p&gt;
&lt;figure class="highlight"&gt;
&lt;figcaption&gt;&lt;span&gt;&lt;mark&gt;.forgejo/workflows/deploy.yaml&lt;/mark&gt;&lt;/span&gt; &lt;/figcaption&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-YAML" data-lang="YAML"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="l"&gt;push]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;&lt;/span&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;buildhugo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;hugo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;hugo version&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;submodules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;recursive&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;run hugo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;hugo --gc --minify&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;fix up RSS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;cp ./public/index.xml ./public/rss.xml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;stash public files&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;https://code.forgejo.org/forgejo/upload-artifact@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;public&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./public/ &lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;p&gt;This checks out the git repo with submodules, runs &lt;code&gt;hugo --gc --minify&lt;/code&gt; on that, copies index.xml to rss.xml because I started with Astro and didn&amp;rsquo;t want to have to migrate everyone&amp;rsquo;s links, then uploads the &lt;code&gt;./public/&lt;/code&gt; directory with the name &lt;code&gt;public&lt;/code&gt; to &lt;a href="https://forgejo.org/docs/next/user/actions/advanced-features/#artifacts" target="_blank" rel="noreferrer"&gt;Forgejo&amp;rsquo;s artifact repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once you&amp;rsquo;ve done that, you can:&lt;/p&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git add .forgejo/workflows/deploy.yaml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git commit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git push origin&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;p&gt;That triggers the automation that will (first time) download the Hugo image, then build the website, then stash it in an artifact called &lt;code&gt;public&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But that&amp;rsquo;s only half the story for &lt;code&gt;deploy.yaml&lt;/code&gt;. &lt;a href="https://andrewmemory.acornwall.net/blog/2026-04-08-uploading-from-forgejo-to-the-webserver/" &gt;Read about uploading to the webserver here&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Setting up a Forgejo runner for Hugo and others</title><link>https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/</link><pubDate>Thu, 02 Apr 2026 22:16:05 -0700</pubDate><author>andrewmemoryblog@gmail.com (Andrew's Memory Blog)</author><guid>https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/</guid><description>&lt;p&gt;I spent a lot of time messing around because I thought the runner was the thing that ran my actions. Nope, it&amp;rsquo;s the thing that runs the Docker image that builds your images. That makes life easier. Here&amp;rsquo;s how I set up mine:&lt;/p&gt;
&lt;h2 class="relative group"&gt;Get the runner token
&lt;div id="get-the-runner-token" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#get-the-runner-token" aria-label="Anchor"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;You need the runner token to configure a runner. So do that first. You need to be an admin to set up the runner.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Under the profile menu on the far right, click on &amp;ldquo;Site administration.&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Under &amp;ldquo;Admin settings&amp;rdquo; expand Actions to click on &amp;ldquo;Runners&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="The Admin settings panel. Actions has been expanded so you can see Runners"
width="255"
height="467"
src="https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/images/admin-runner.webp"
srcset="https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/images/admin-runner.webp 800w, https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/images/admin-runner.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/images/admin-runner.webp"&gt;&lt;/figure&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Over on the right, click the &amp;ldquo;Create a new runner&amp;rdquo; button. This opens a dialog with a token in it. Copy that token to a text editor.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="A dialog titled &amp;ldquo;How to start a runner&amp;rdquo;. The registration token text field has a copy button to the right of it"
width="282"
height="243"
src="https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/images/create-runner.webp"
srcset="https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/images/create-runner.webp 800w, https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/images/create-runner.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/images/create-runner.webp"&gt;&lt;/figure&gt;
&lt;h2 class="relative group"&gt;Setting up a runner
&lt;div id="setting-up-a-runner" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#setting-up-a-runner" aria-label="Anchor"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;I spent a lot of time messing around because I thought the runner was the thing that ran my actions. Nope, it&amp;rsquo;s the thing that runs the Docker image that builds your images. That makes life easier.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Make a directory for the runner, put the Dockerfile in it, and grab the image. These instructions look at lot like the ones at the &lt;a href="https://forgejo.org/docs/latest/admin/actions/runner-installation/#oci-image-installation" target="_blank" rel="noreferrer"&gt;OCI image installation&lt;/a&gt; instructions on the Forgejo website.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /data/shared/forgejo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir runner
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; runner
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;wget https://code.forgejo.org/forgejo/runner/raw/branch/main/Dockerfile
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker run --rm data.forgejo.org/forgejo/runner:11 forgejo-runner --version&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Create a &lt;code&gt;setup.sh&lt;/code&gt; in &lt;code&gt;runner/&lt;/code&gt; that looks like this. (Make sure to update the chown line to use the UID and GID you specified in the Forgejo docker-compose.yaml):&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;figcaption&gt;&lt;span&gt;&lt;mark&gt;setup.sh&lt;/mark&gt;&lt;/span&gt; &lt;/figcaption&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/usr/bin/env bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /data/shared/forgejo/runner
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;set&lt;/span&gt; -e
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mkdir -p data/.cache
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chown -R 2000:2000 data
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chmod &lt;span class="m"&gt;775&lt;/span&gt; data/.cache
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chmod g+s data/.cache&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Edit the Dockerfile to use your user info. Look for the USER line and put in your UID and GID:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-Docker" data-lang="Docker"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;2000:2000&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Run setup.sh&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bash ./setup.sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="5"&gt;
&lt;li&gt;Create a docker-compose.yaml. Based on the Forgejo instructions, it should look like this (but remember to change the user: to your UID and GID). Yeah, I know it should be a different UID and GID but I&amp;rsquo;m lazy:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;figcaption&gt;&lt;span&gt;&lt;mark&gt;docker-compose.yaml&lt;/mark&gt;&lt;/span&gt; &lt;/figcaption&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-Docker" data-lang="Docker"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;version: &lt;span class="s1"&gt;&amp;#39;3.8&amp;#39;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt;services:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; docker-in-docker:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; image: docker:dind&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; container_name: &lt;span class="s1"&gt;&amp;#39;docker_dind&amp;#39;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; privileged: &lt;span class="s1"&gt;&amp;#39;true&amp;#39;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; command: &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;dockerd&amp;#39;&lt;/span&gt;, &lt;span class="s1"&gt;&amp;#39;-H&amp;#39;&lt;/span&gt;, &lt;span class="s1"&gt;&amp;#39;tcp://0.0.0.0:2375&amp;#39;&lt;/span&gt;, &lt;span class="s1"&gt;&amp;#39;--tls=false&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; restart: &lt;span class="s1"&gt;&amp;#39;unless-stopped&amp;#39;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; runner:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; image: &lt;span class="s1"&gt;&amp;#39;data.forgejo.org/forgejo/runner:11&amp;#39;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; links:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; - docker-in-docker&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; depends_on:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; docker-in-docker:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; condition: service_started&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; container_name: &lt;span class="s1"&gt;&amp;#39;runner&amp;#39;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; environment:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; DOCKER_HOST: tcp://docker-in-docker:2375&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; &lt;span class="c1"&gt;# User without root privileges, but with access to `./data`.&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; user: 2000:2000&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; volumes:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; - ./data:/data&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; restart: &lt;span class="s1"&gt;&amp;#39;unless-stopped&amp;#39;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; command: &lt;span class="s1"&gt;&amp;#39;/bin/sh -c &amp;#34;while : ; do sleep 1 ; done ;&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt;&lt;span class="c"&gt;# command: &amp;#39;/bin/sh -c &amp;#34;sleep 5; forgejo-runner daemon --config config.yml&amp;#34;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="6"&gt;
&lt;li&gt;Fire up the runner and attach to the shell in the runner image.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker &lt;span class="nb"&gt;exec&lt;/span&gt; -it runner /bin/sh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="7"&gt;
&lt;li&gt;From within the Docker image shell, run:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;forgejo-runner generate-config &amp;gt; config.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;forgejo-runner register&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="8"&gt;
&lt;li&gt;This will run a script. I used the following values. For the runner labels, what they really want is the label followed by a colon followed by the URL of the docker image, with each entry separated by commas. Since I wanted my runner to run Hugo, I added a label for hugo and a pointer to the Hugo image as well as a &amp;ldquo;docker&amp;rdquo; label which is one of the defaults:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;INFO Enter the Forgejo instance URL &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; example, https://next.forgejo.org/&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;http://myforgejoserver.acornwall.net:3000
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;INFO Enter the runner token:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;d41d8cd98f00b204e9800998ecf8427e32d6c11747e037155210
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;INFO Enter the runner name &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt; empty, use hostname: 3b586057879f&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;runner
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;INFO Enter the runner labels, leave blank to use the default labels &lt;span class="o"&gt;(&lt;/span&gt;comma-separated, &lt;span class="k"&gt;for&lt;/span&gt; example, ubuntu-20.04:docker://node:20-bookworm,ubuntu-18.04:docker://node:20-bookworm&lt;span class="o"&gt;)&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hugo:docker://ghcr.io/gohugoio/hugo:v0.152.2,docker:docker://data.forgejo.org/oci/node:20-bullseye&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="8"&gt;
&lt;li&gt;
&lt;p&gt;All that creates a &lt;code&gt;.runner&lt;/code&gt; file in the Docker image, which gets mapped to the &lt;code&gt;data&lt;/code&gt; directory that &lt;code&gt;setup.sh&lt;/code&gt; created.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, shut the image down.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose down&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="10"&gt;
&lt;li&gt;Next, edit the docker-compose.yaml. Comment out the command that just spins, and uncomment the command that does the thing:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;figcaption&gt;&lt;span&gt;&lt;mark&gt;docker-compose.yaml&lt;/mark&gt;&lt;/span&gt; &lt;/figcaption&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-Docker" data-lang="Docker"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;...&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt;&lt;span class="c"&gt;# command: &amp;#39;/bin/sh -c &amp;#34;while : ; do sleep 1 ; done ;&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; command: &lt;span class="s1"&gt;&amp;#39;/bin/sh -c &amp;#34;sleep 5; forgejo-runner daemon --config config.yml&amp;#34;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="11"&gt;
&lt;li&gt;Bring the runner back up:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose up -d&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="12"&gt;
&lt;li&gt;If you did everything right, you should see a runner show up under Admin settings - Actions - Runners.
&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="A runner with status Idle, ID 9, name runner, version v11.3.1, type Global, labels hugo and docker, last online time 1 minute ago, and an edit button"
width="800"
height="174"
src="https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/images/runner.webp"
srcset="https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/images/runner.webp 800w, https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/images/runner.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/images/runner.webp"&gt;&lt;/figure&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 class="relative group"&gt;Getting the runner to restart on reboot
&lt;div id="getting-the-runner-to-restart-on-reboot" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#getting-the-runner-to-restart-on-reboot" aria-label="Anchor"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;Another bout with systemd, &amp;lsquo;cause we&amp;rsquo;d like this to start on reboot as well.&lt;/p&gt;
&lt;figure class="highlight"&gt;
&lt;figcaption&gt;&lt;span&gt;&lt;mark&gt;/etc/systemd/system/forgejo-runner.service&lt;/mark&gt;&lt;/span&gt; &lt;/figcaption&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-SYSTEMD" data-lang="SYSTEMD"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Forgejo Runner&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;forgejo.service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Requires&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;forgejo.service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;oneshot&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;RemainAfterExit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/bash -c &amp;#34;/usr/bin/docker compose -f /data/shared/forgejo/runner/docker-compose.yaml up --detach&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ExecStop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/bash -c &amp;#34;/usr/bin/docker compose -f /data/shared/forgejo/runner/docker-compose.yaml down&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;p&gt;And then the requisite:&lt;/p&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/usr/bin/docker compose -f /data/shared/forgejo/runner/docker-compose.yaml down
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl start forgejo-runner
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl status forgejo-runner
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl stop forgejo-runner
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; forgejo-runner
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl start forgejo-runner&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;</description></item><item><title>Installing a minimal Forgejo via Docker on Ubuntu 24.04</title><link>https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/</link><pubDate>Thu, 02 Apr 2026 20:54:05 -0700</pubDate><author>andrewmemoryblog@gmail.com (Andrew's Memory Blog)</author><guid>https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/</guid><description>&lt;p&gt;I&amp;rsquo;ve been wanting to play with &lt;a href="https://forgejo.org/" target="_blank" rel="noreferrer"&gt;Forgejo&lt;/a&gt; for a while. It&amp;rsquo;s the open source DevOps platform behind Codeberg and I like the idea of self-hosted automation. I wanted a minimal one — I didn&amp;rsquo;t need mail but I wanted automation on push.&lt;/p&gt;
&lt;p&gt;If you know Docker (the current version of Docker) you&amp;rsquo;ll find it pretty easy to install. Here&amp;rsquo;s how to do it.&lt;/p&gt;
&lt;h2 class="relative group"&gt;Pull Forgejo from Docker
&lt;div id="pull-forgejo-from-docker" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#pull-forgejo-from-docker" aria-label="Anchor"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Ubuntu 24.04 doesn&amp;rsquo;t come with the new Docker, or sqlite3. So:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install docker-compose-v2 sqlite3&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="2"&gt;
&lt;li&gt;Grab the Docker image:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker pull codeberg.org/forgejo/forgejo:14&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Create a docker-compose.yaml based on the one at &lt;a href="https://forgejo.org/docs/next/admin/installation/docker/" target="_blank" rel="noreferrer"&gt;forgejo.org/docs/next/admin/installation/docker/&lt;/a&gt;. I changed three places the &lt;code&gt;USER_UID&lt;/code&gt;, &lt;code&gt;USER_GID&lt;/code&gt;, and the path to the volume (I used a hard-coded one rather than &lt;code&gt;./forgejo&lt;/code&gt;. The file I ended up with was:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;figcaption&gt;&lt;span&gt;&lt;mark&gt;docker-compose.yaml&lt;/mark&gt;&lt;/span&gt; &lt;/figcaption&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-Docker" data-lang="Docker"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;networks:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; forgejo:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; external: false&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt;services:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; server:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; image: codeberg.org/forgejo/forgejo:14&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; container_name: forgejo&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; environment:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; - &lt;span class="nv"&gt;USER_UID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2000&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; - &lt;span class="nv"&gt;USER_GID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2000&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; restart: always&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; networks:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; - forgejo&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; volumes:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; - /data/shared/forgejo/forjego:/data ← was ./forgejo:/data&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; - /etc/localtime:/etc/localtime:ro&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; ports:&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; - &lt;span class="s1"&gt;&amp;#39;3000:3000&amp;#39;&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;&lt;/span&gt; - &lt;span class="s1"&gt;&amp;#39;222:22&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Bring the image up:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose up -d&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="5"&gt;
&lt;li&gt;
&lt;p&gt;Open a browser on the host&amp;rsquo;s port 3000 (which I&amp;rsquo;ll call myforgejoserver.acornwall.net:3000) to initialize Forgejo.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the browser, leave everything as default except the server domain and base URL. That means I&amp;rsquo;m using the default sqlite&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="The onboarding screen. All the defaults are checked except the server domain, which is masked, and the base URL, which is masked except you can see it starts with http and ends with :3000/"
width="800"
height="702"
src="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/onboarding-1.webp"
srcset="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/onboarding-1.webp 800w, https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/onboarding-1.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/onboarding-1.webp"&gt;&lt;/figure&gt;
&lt;ol start="7"&gt;
&lt;li&gt;I skipped email settings and server/third-party service settings, and entered the administrator account settings, then clicked Install Forgejo:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="More of the onboarding screen. Email settings and server/third party settings are collapsed. The administrator username is andrew, the email address is andrewmemoryblog@gmail.com, and the password is masked. A button at the bottom reads &amp;ldquo;Install Forgejo&amp;rdquo;."
width="800"
height="503"
src="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/onboarding-2.webp"
srcset="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/onboarding-2.webp 800w, https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/onboarding-2.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/onboarding-2.webp"&gt;&lt;/figure&gt;
&lt;ol start="8"&gt;
&lt;li&gt;Next I restarted Forgejo and made sure that the settings stuck. If you have the storage volume wrong in the docker-compose.yaml, as I did the first time, you get to enter everything again after fixing it.&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose down
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;docker compose up -d&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;h2 class="relative group"&gt;Creating a Git Repo Into Forgejo
&lt;div id="creating-a-git-repo-into-forgejo" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#creating-a-git-repo-into-forgejo" aria-label="Anchor"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;Once things were set up, I could log in and create a repo in Forgejo. Here&amp;rsquo;s what I did.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Navigate to the + dropdown and click &amp;ldquo;New repository&amp;rdquo;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fill out the form. I called my repo &amp;ldquo;andrewmemory&amp;rdquo; and set my user as the owner. I checked &amp;ldquo;Make repostitory private.&amp;rdquo; I also checked &amp;ldquo;Initialize repository&amp;rdquo; which lead to some issues later, but hey, it looked good at the time. I added both &amp;ldquo;Emacs&amp;rdquo; and &amp;ldquo;Hugo&amp;rdquo; to .gitignore — I didn&amp;rsquo;t realize I could do both the first time I tried. I went with CC-BY-NC-ND-4.0 for the license. Then I clicked &amp;ldquo;Create repository.&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="The repository creation screen. Owner is andrew, repo name is andrewmemory. Make repository private and initialize repository are checked. .gitignore has both Emacs and Hugo, and the license is CC-BY-NC-ND-4.0. A button at the bottom reads &amp;ldquo;Create repository&amp;rdquo;."
width="800"
height="939"
src="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/new-repo.webp"
srcset="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/new-repo.webp 800w, https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/new-repo.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/new-repo.webp"&gt;&lt;/figure&gt;
&lt;p&gt;There, the repo is created!&lt;/p&gt;
&lt;h2 class="relative group"&gt;Verifying your public key
&lt;div id="verifying-your-public-key" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#verifying-your-public-key" aria-label="Anchor"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Once the repo was created on Forgejo, I needed to add my private cert to Forgejo. That meant clicking the dropdown on the right with my avatar, clicking &amp;ldquo;Settings,&amp;rdquo; then navigating to &amp;ldquo;SSH / GPG keys&amp;rdquo;. From there I could click &amp;ldquo;Add key&amp;rdquo; under SSH Keys. I pasted my public key in there. Then click &amp;ldquo;Add key.&amp;rdquo;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Once you&amp;rsquo;ve done that, you&amp;rsquo;ve got something like this. Click &amp;ldquo;Verify&amp;rdquo;:
&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="A key called Test key. Two buttons are on the right: &amp;ldquo;Remove&amp;rdquo; and &amp;ldquo;Verify&amp;rdquo;"
width="800"
height="73"
src="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/verify-button.webp"
srcset="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/verify-button.webp 800w, https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/verify-button.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/verify-button.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After clicking verify, you&amp;rsquo;re presented with a token and a string to paste into a terminal. Copy the string, paste it into the terminal, but edit the filename if your private key is not &lt;code&gt;id_ed25519&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; -n &lt;span class="s1"&gt;&amp;#39;ee2cd80b5f8f4e95800ddca4d92053998bc3f8e7a9cc1dd60fe30e7ad83b87d6&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; ssh-keygen -Y sign -n myforgejoserver.acornwall.net -f ~/.ssh/id_rsa&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Running that command will dump a wad into your terminal that looks like this:&lt;/li&gt;
&lt;/ol&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0"&gt;&lt;code class="language-" data-lang=""&gt;-----BEGIN SSH SIGNATURE-----
U1mIU0lHAAAAAQA
...
&amp;#43;wrNHO9W
-----END SSH SIGNATURE-----&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;ol start="5"&gt;
&lt;li&gt;
&lt;p&gt;Copy the whole wad including the BEGIN and END parts, then paste that into the section that says &amp;ldquo;Armored SSH signature&amp;rdquo; and press the &amp;ldquo;Verify&amp;rdquo; button.
&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="A text field that is labeled Armored SSH signature. The text field begins with &amp;mdash;&amp;ndash;BEGIN SSH SIGNATURE&amp;mdash;&amp;ndash; and has random looking characters until the end, where it says &amp;mdash;&amp;ndash;END SSH SIGNATURE&amp;mdash;&amp;ndash;. Below that are Verify and Cancel buttons."
width="800"
height="203"
src="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/armored-signature.webp"
srcset="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/armored-signature.webp 800w, https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/armored-signature.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/images/armored-signature.webp"&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you&amp;rsquo;ve done everything right your key is now verified.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 class="relative group"&gt;Connecting the old repo
&lt;div id="connecting-the-old-repo" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#connecting-the-old-repo" aria-label="Anchor"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;Finally, I could start moving my website (which is in a git repo on disk) into Forgejo. That wasn&amp;rsquo;t terrible, although it took me a couple of tries because I wasn&amp;rsquo;t sure how to do it. Here&amp;rsquo;s what I did.&lt;/p&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; andrewmemory-hugo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git remote rm origin &lt;span class="c1"&gt;# It took me a couple of tries, so I had to remove the old origin&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git remote add origin ssh://git@myforgejoserver.acornwall.net:222/andrew/andrewmemory
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git pull --allow-unrelated origin main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;p&gt;This resulted in a bunch of merge conflicts in .gitignore that I manually took care of. Once that was done, I could:&lt;/p&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git push --set-upstream origin main&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;h2 class="relative group"&gt;Setting Forgejo to restart after reboot
&lt;div id="setting-forgejo-to-restart-after-reboot" class="anchor"&gt;&lt;/div&gt;
&lt;span
class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
&lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#setting-forgejo-to-restart-after-reboot" aria-label="Anchor"&gt;#&lt;/a&gt;
&lt;/span&gt;
&lt;/h2&gt;
&lt;p&gt;This is all groovy, but next time I install Linux patches, I&amp;rsquo;m going to have to reboot my machine. So I created a systemd (&lt;a href="https://andrewmemory.acornwall.net/blog/2026-02-15-setting-up-systemd-resolv-conf/" &gt;boo&lt;/a&gt;) service to do that.&lt;/p&gt;
&lt;figure class="highlight"&gt;
&lt;figcaption&gt;&lt;span&gt;&lt;mark&gt;/etc/systemd/system/forgejo.service&lt;/mark&gt;&lt;/span&gt; &lt;/figcaption&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-SYSTEMD" data-lang="SYSTEMD"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Forgejo&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;After&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Requires&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;docker.service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;oneshot&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;RemainAfterExit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/bash -c &amp;#34;/usr/bin/docker-compose -f /data/shared/forgejo/docker-compose.yaml up --detach&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;ExecStop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/bin/bash -c &amp;#34;/usr/bin/docker-compose -f /data/shared/forgejo/docker-compose.yaml down&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="na"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;p&gt;Then, I made sure it worked and enabled it on boot:&lt;/p&gt;
&lt;figure class="highlight"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl start forgejo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl status forgejo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl stop forgejo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl &lt;span class="nb"&gt;enable&lt;/span&gt; forgejo
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemctl start forgejo&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/figure&gt;
&lt;p&gt;Ok, Forgejo is set up!&lt;/p&gt;</description></item><item><title>Website automation with Forgejo</title><link>https://andrewmemory.acornwall.net/blog/2026-03-29-website-automation-with-forgejo/</link><pubDate>Sun, 29 Mar 2026 21:54:05 -0700</pubDate><author>andrewmemoryblog@gmail.com (Andrew's Memory Blog)</author><guid>https://andrewmemory.acornwall.net/blog/2026-03-29-website-automation-with-forgejo/</guid><description>&lt;figure&gt;&lt;img
class="my-0 rounded-md"
loading="lazy"
decoding="async"
fetchpriority="auto"
alt="The Forgejo word mark"
width="300"
height="113"
src="https://andrewmemory.acornwall.net/blog/2026-03-29-website-automation-with-forgejo/images/forgejo-wordmark.webp"
srcset="https://andrewmemory.acornwall.net/blog/2026-03-29-website-automation-with-forgejo/images/forgejo-wordmark.webp 800w, https://andrewmemory.acornwall.net/blog/2026-03-29-website-automation-with-forgejo/images/forgejo-wordmark.webp 1280w"
sizes="(min-width: 768px) 50vw, 65vw"
data-zoom-src="https://andrewmemory.acornwall.net/blog/2026-03-29-website-automation-with-forgejo/images/forgejo-wordmark.webp"&gt;&lt;/figure&gt;
&lt;p&gt;If you see this, then my Forgejo web automation is working.&lt;/p&gt;
&lt;p&gt;It took a few steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://andrewmemory.acornwall.net/blog/2026-04-02-installing-forgejo/" &gt;Installing Forgejo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://andrewmemory.acornwall.net/blog/2026-04-02-setting-up-a-forgejo-runner-for-hugo-and-others/" &gt;Setting up the Forgejo runner for Hugo and others&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://andrewmemory.acornwall.net/blog/2026-04-08-running-hugo-from-the-runner/" &gt;Figuring out how to run Hugo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://andrewmemory.acornwall.net/blog/2026-04-08-uploading-from-forgejo-to-the-webserver/" &gt;Figuring out how to upload&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>