I'm siGit, running on siGit Code & Deploy platform, browsing siGit Code coding agent repo.
Add a guest image for Actions jobs in a microVM
packaging/actions-runner has a Dockerfile with two targets: the pinned Actions runner with git, node, pnpm, jq and gh, and a variant with browsers, a virtual display and tauri-driver. The runner is checked against its published sha256. The image holds no credentials; the runner starts from a one-job configuration that arrives with the job. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Seto Elkahfi committed
Nov 1, 2026 at 10:32 UTC
f8f0d8e073d3a24d148f4775df74c9166e5261fd
2 files changed
+132
packaging/actions-runner/Dockerfile
new
+98
@@ -0,0 +1,98 @@
1
+# Guest image for GitHub Actions jobs that run in a microVM. It holds the pinned Actions runner
2
+# and the tools most workflows call. The runner starts from a just-in-time configuration that
3
+# arrives with the job, takes one job and exits, so the image holds no credentials and no
4
+# registration state.
5
+#
6
+# Two targets share one base:
7
+#
8
+# actions-runner runner, git, node, pnpm, jq, gh (the default)
9
+# actions-runner-qa the above plus browsers, a virtual display and tauri-driver
10
+#
11
+# docker build --target actions-runner -t actions-runner packaging/actions-runner
12
+# docker build --target actions-runner-qa -t actions-runner-qa packaging/actions-runner
13
+#
14
+# The image has to boot systemd: the microVM's init and its guest agent are units. See README.md
15
+# for turning the image into a root filesystem.
16
+
17
+ARG BASE=ubuntu:24.04
18
+
19
+# tauri-driver is cargo-only; build it here and copy the binary so the guest has no Rust.
20
+FROM rust:1-bookworm AS tauri-driver
21
+RUN cargo install tauri-driver --locked --root /out
22
+
23
+FROM ${BASE} AS actions-runner
24
+
25
+LABEL org.opencontainers.image.source=https://github.com/getsigit/sigit \
26
+ org.opencontainers.image.description="Guest image for GitHub Actions jobs in a microVM"
27
+
28
+ARG RUNNER_VERSION=2.338.2
29
+# sha256 of actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz, from the release notes.
30
+ARG RUNNER_SHA256=baebf204307bac6193480852732f0aba2ae97b6c5597c244f4d5558390147c40
31
+ARG NODE_VERSION=24.12.0
32
+ARG PNPM_VERSION=10
33
+
34
+ENV DEBIAN_FRONTEND=noninteractive
35
+
36
+# Init, the packages the guest agent and the host tooling need (python3, git, CA certificates,
37
+# zlib), and the usual build tools.
38
+RUN apt-get update \
39
+ && apt-get install -y --no-install-recommends \
40
+ systemd systemd-sysv dbus iproute2 \
41
+ ca-certificates zlib1g python3 git curl jq unzip xz-utils zstd \
42
+ build-essential pkg-config libssl-dev openssh-client sudo \
43
+ && rm -rf /var/lib/apt/lists/*
44
+
45
+# The GitHub CLI, from its apt repository.
46
+RUN install -d -m 0755 /etc/apt/keyrings \
47
+ && curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
48
+ -o /etc/apt/keyrings/githubcli-archive-keyring.gpg \
49
+ && echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
50
+ > /etc/apt/sources.list.d/github-cli.list \
51
+ && apt-get update \
52
+ && apt-get install -y --no-install-recommends gh \
53
+ && rm -rf /var/lib/apt/lists/*
54
+
55
+# Node from nodejs.org, then pnpm.
56
+RUN curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz" \
57
+ | tar -xJ -C /usr/local --strip-components=1 \
58
+ && npm install -g "pnpm@${PNPM_VERSION}"
59
+
60
+# The Actions runner, checked against the published digest, with its system dependencies. The
61
+# runner refuses to start as root unless RUNNER_ALLOW_RUNASROOT is set; the host sets it, since
62
+# the guest has a single user and the VM lives for one job.
63
+RUN set -eu; \
64
+ mkdir -p /opt/actions-runner; \
65
+ curl -fsSL --retry 2 -o /tmp/runner.tar.gz \
66
+ "https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz"; \
67
+ echo "${RUNNER_SHA256} /tmp/runner.tar.gz" | sha256sum -c -; \
68
+ tar -xzf /tmp/runner.tar.gz -C /opt/actions-runner; \
69
+ rm /tmp/runner.tar.gz; \
70
+ /opt/actions-runner/bin/installdependencies.sh; \
71
+ rm -rf /var/lib/apt/lists/*; \
72
+ test -x /opt/actions-runner/run.sh
73
+
74
+WORKDIR /workspace
75
+
76
+FROM actions-runner AS actions-runner-qa
77
+
78
+LABEL org.opencontainers.image.description="Guest image for GitHub Actions jobs in a microVM, with browsers"
79
+
80
+# Must match the consumer's @playwright/test. A job's `playwright install` is a no-op when it
81
+# does and a browser download when it doesn't, and visual baselines are only stable against the
82
+# browsers they were recorded with.
83
+ARG PLAYWRIGHT_VERSION=1.64.1
84
+
85
+ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright
86
+
87
+# Tauri runtime on Linux (AppImage under tauri-driver) and a virtual display.
88
+RUN apt-get update \
89
+ && apt-get install -y --no-install-recommends \
90
+ xvfb libwebkit2gtk-4.1-0 webkit2gtk-driver bubblewrap \
91
+ && rm -rf /var/lib/apt/lists/*
92
+
93
+# Browsers and their system libraries and fonts, at the pinned Playwright version.
94
+RUN npx -y "playwright@${PLAYWRIGHT_VERSION}" install --with-deps chromium webkit \
95
+ && chmod -R a+rX /ms-playwright \
96
+ && rm -rf /var/lib/apt/lists/*
97
+
98
+COPY --from=tauri-driver /out/bin/tauri-driver /usr/local/bin/tauri-driver
packaging/actions-runner/README.md
new
+34
@@ -0,0 +1,34 @@
1
+# actions-runner
2
+
3
+Guest image for GitHub Actions jobs that run in a microVM. See the Dockerfile header for the
4
+two targets.
5
+
6
+## What the image holds
7
+
8
+- The Actions runner at `/opt/actions-runner`, pinned by version and sha256. Bump
9
+ `RUNNER_VERSION` and `RUNNER_SHA256` together; the digest is in the release notes.
10
+- git, node, pnpm, jq, gh, python3 and a C toolchain.
11
+- systemd as init. The microVM boots it, and the guest agent that talks to the host is installed
12
+ as a unit when the root filesystem is baked.
13
+
14
+It holds no credentials. The runner starts with `run.sh --jitconfig`, using a one-job
15
+configuration that arrives with the job, takes that job and exits.
16
+
17
+## Making a root filesystem
18
+
19
+```sh
20
+docker build --target actions-runner -t actions-runner packaging/actions-runner
21
+cid=$(docker create actions-runner)
22
+docker export "$cid" | (mkdir rootfs && tar -x -C rootfs)
23
+docker rm "$cid"
24
+truncate -s 8G actions-runner.ext4
25
+mkfs.ext4 -F -d rootfs actions-runner.ext4
26
+```
27
+
28
+The host-side tooling then installs the guest agent into the image and snapshots it. That step
29
+lives with the runner, not here.
30
+
31
+## Egress
32
+
33
+Inside the microVM the runner reaches the network only through the host's allowlisting proxy,
34
+through `HTTPS_PROXY`. A workflow that needs a host beyond the allowlist fails at connect time.