<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>drobilla.net - Forgejo</title><link>https://drobilla.net/</link><description/><atom:link href="https://drobilla.net/category/forgejo/feed/rss2" rel="self"/><lastBuildDate>Wed, 08 Jul 2026 13:38:10 -0400</lastBuildDate><item><title>Running buildah in forgejo-runner</title><link>https://drobilla.net/2026/07/08/running-buildah-in-forgejo-runner.html</link><description>&lt;p&gt;I've been trying to get &lt;a href="https://buildah.io/"&gt;buildah&lt;/a&gt; working in &lt;a href="https://code.forgejo.org/forgejo/runner"&gt;forgejo-runner&lt;/a&gt; for building container images on CI, and it's been a frustrating process.  There are some posts around the web about this, but nothing that sums up a working solution that doesn't simply disable all security measures.  I eventually pieced it together with a lot of trial and error, so here's a summary to hopefully save somebody else (or me in the future) several days of pain.&lt;/p&gt;
&lt;p&gt;The basic situation is that &lt;code&gt;docker build&lt;/code&gt; requires a running docker daemon, either by sharing the host docker socket, which is a gaping security hole, or docker-in-docker (dind), which has its own set of problems.  Either way, this won't work on public forges like &lt;a href="https://codeberg.org/"&gt;codeberg&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Using buildah avoids this problem, but it still has requirements that are denied by standard security policies.  If you try this on a stock configuration, you'll get errors because buildah tries to create a user namespace:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Error during unshare(CLONE_NEWUSER): Permission denied
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then tries to remount the image filesystem:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;ApplyLayer stdout:  stderr: remount /, flags: 0x44000: permission denied exit status 1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;A lot of advice on the web suggests using these docker options for the runner container, which will work, but is a bad idea because it completely disables security protections:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;--security-opt seccomp=unconfined --security-opt apparmor=unconfined
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You definitely shouldn't do this for a public runner in particular.  A better solution is to use security profiles that only allow the additional operations on top of the default profiles used by docker.  The following instructions are for forgejo-runner running in docker on Debian 13 (trixie) on x86-64, but should be straightforward to adjust for other situations.&lt;/p&gt;
&lt;p&gt;First, you need a seccomp profile which is defined in a JSON file.  This needs to be available to the runner in its container, mine is at &lt;code&gt;/data/seccomp.json&lt;/code&gt; in the same directory as forgejo-runner's &lt;code&gt;config.yml&lt;/code&gt;.  The default profile is available at &lt;a href="https://github.com/moby/profiles/blob/main/seccomp/default.json"&gt;https://github.com/moby/profiles/blob/main/seccomp/default.json&lt;/a&gt;.  Copy this, and add these rules to the &lt;code&gt;syscalls&lt;/code&gt; dictionary:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;names&amp;quot;&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="s2"&gt;&amp;quot;unshare&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;action&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SCMP_ACT_ALLOW&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;comment&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Allow unshare for buildah&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;names&amp;quot;&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="s2"&gt;&amp;quot;mount&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;action&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;SCMP_ACT_ALLOW&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;&amp;quot;comment&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Allow mount for buildah&amp;quot;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Note that these rules allow &lt;em&gt;any&lt;/em&gt; &lt;code&gt;unshare&lt;/code&gt; and &lt;code&gt;mount&lt;/code&gt; syscalls, although it should be possible to further restrict to only the specific arguments used by buildah.  The default profile this is based on presumably allows many things which aren't actually required, so that could be further locked down as well.&lt;/p&gt;
&lt;p&gt;Apparmor works differently, where profiles are installed on the system and referenced by name.  Add this file at &lt;code&gt;/etc/apparmor.d/runner-buildah&lt;/code&gt; on the host:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;#include &amp;lt;tunables/global&amp;gt;

profile runner-buildah flags=(attach_disconnected,mediate_deleted) {
  #include &amp;lt;abstractions/base&amp;gt;

  network,
  capability,
  file,
  umount,
  mount,
  userns,

  signal (receive) peer=unconfined,
  signal (receive) peer=runc,
  signal (receive) peer=crun,
  signal (receive) peer=unconfined,
  signal (send,receive) peer=runner-buildah,

  deny @{PROC}/* w,
  deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9/]*}/** w,
  deny @{PROC}/sys/[^k]** w,
  deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w,
  deny @{PROC}/sysrq-trigger rwklx,
  deny @{PROC}/kcore rwklx,

  deny /sys/[^f]*/** wklx,
  deny /sys/f[^s]*/** wklx,
  deny /sys/fs/[^c]*/** wklx,
  deny /sys/fs/c[^g]*/** wklx,
  deny /sys/fs/cg[^r]*/** wklx,
  deny /sys/firmware/** rwklx,
  deny /sys/devices/virtual/powercap/** rwklx,
  deny /sys/kernel/security/** rwklx,

  ptrace (trace,read,tracedby,readby) peer=runner-buildah,
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;You can use a name other than &lt;code&gt;runner-buildah&lt;/code&gt;, but remember to change the names in the profile itself or it won't work.  After adding this file, either reboot the host or use &lt;code&gt;apparmor_parser&lt;/code&gt; to add/replace the new profile:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;apparmor_parser -r -W /etc/apparmor.d/runner-buildah
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;(As an aside, the &lt;code&gt;ptrace&lt;/code&gt; permissions are required by gcc/clang sanitizers, so if your sanitizer builds don't work in a container, this is probably why.  I'm not sure if/when they're required by buildah.)&lt;/p&gt;
&lt;p&gt;Then, add the corresponding options to the runner's docker run command line.  For forgejo-runner, this goes in the &lt;code&gt;container&lt;/code&gt; section of &lt;code&gt;config.yml&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="nt"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="nt"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;quot;--security-opt&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;seccomp=/data/seccomp.json&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;--security-opt&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;apparmor=runner-buildah&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Restart the runner container to complete the configuration.  Now, jobs that use buildah to build images like this should work:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&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 l-Scalar l-Scalar-Plain"&gt;build-my-image&lt;/span&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="nt"&gt;push&lt;/span&gt;&lt;span class="p"&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 class="nt"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&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 l-Scalar l-Scalar-Plain"&gt;docker&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nt"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l l-Scalar l-Scalar-Plain"&gt;quay.io/buildah/stable&lt;/span&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 class="p p-Indicator"&gt;-&lt;/span&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="s"&gt;&amp;#39;Clone&amp;#39;&lt;/span&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 l-Scalar l-Scalar-Plain"&gt;git clone --depth 1 --branch ${FORGEJO_REF_NAME} ${FORGEJO_SERVER_URL}/${FORGEJO_REPOSITORY}.git .&lt;/span&gt;
&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="p p-Indicator"&gt;-&lt;/span&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="s"&gt;&amp;#39;Build&amp;#39;&lt;/span&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 l-Scalar l-Scalar-Plain"&gt;buildah --storage-driver=vfs bud --isolation chroot -t myorg/myimage&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;This is just a simple build job for demonstration, you'll need to come up with a suitable workflow for building and publishing your image.  The buildah options used here are conservative for maximum compatibility (the main downside being that &lt;code&gt;vfs&lt;/code&gt; is slow), so this should also work on shared forges like codeberg.&lt;/p&gt;
&lt;p&gt;Some caveats: I'm not an expert in this sort of thing, and these profiles are definitely not as tight as they could be.  In my case, I'm using this in a private runner for branches that only I can push in specific repositories in a private forge that runs in docker in a VM all behind a firewall, so runner security isn't much of a concern anyway.  You shouldn't blindly copy this configuration for a public runner, although it's certainly better than using &lt;code&gt;unconfined&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you're more experienced in Linux security and spot any problems or potential improvements here, let me know via email and I'll update this article accordingly.&lt;/p&gt;</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">drobilla</dc:creator><pubDate>Wed, 08 Jul 2026 13:38:10 -0400</pubDate><guid>tag:drobilla.net,2026-07-08:/2026/07/08/running-buildah-in-forgejo-runner.html</guid><category>misc</category><category>Docker</category><category>Forgejo</category><category>Hacking</category><category>Software</category></item></channel></rss>