Self-hosted GitHub Actions runners: Ubuntu 24.04 and Windows Server Core LTSC 2022 (x64).
467
Self-hosted GitHub Actions runners for Ubuntu 24.04 and Windows Server Core LTSC 2022. Self-hosted GitHub-Actions-Runner fuer Ubuntu 24.04 und Windows Server Core LTSC 2022.
Documentation / Dokumentation | Releases | Runner upstream
Both images target x64. The Linux image uses Ubuntu 24.04; the Windows image uses Windows Server Core LTSC 2022 and requires a compatible Windows container host. A Linux Docker engine cannot run the Windows image. Both Dockerfiles use separate download/extraction stages. Their final images include the runner, Docker CLI, Buildx and Git, but no Docker daemon or download archives. Linux also includes the Docker repository key, certificates and required runtime libraries. Windows uses SHA256-verified static Docker/Buildx binaries and MinGit for command-line Git; MinGit does not provide a general-purpose Git Bash shell. Windows tool versions and hashes are pinned in build arguments and must be updated together; the automatic updater only updates the runner.
| Platform | Version tag (example) | Moving tag |
|---|---|---|
| Ubuntu 24.04 | 1.0.0-ubuntu24.04 | latest-ubuntu24.04 |
| Windows LTSC 2022 | 1.0.0-windows | latest-windows |
Images are published to <dockerhub-user>/github-actions-runner and
<registry>.azurecr.io/github-actions-runner. Use versioned tags for reproducibility.
The container release version is independent of the bundled actions/runner version.
Generate a runner registration token, not a personal access token, in the target organization or repository under Settings > Actions > Runners > New self-hosted runner. Registration tokens expire after one hour. Supply credentials at runtime only.
| Environment variable | Required | Meaning |
|---|---|---|
RUNNER_URL | Yes | Organization or repository URL; no default |
RUNNER_TOKEN | Yes | Registration token for that exact URL |
RUNNER_NAME | Yes | Runner name and additional runs-on label |
Linux example, after setting these three variables in your shell:
docker run -d --name gha-linux \
-e RUNNER_URL -e RUNNER_TOKEN -e RUNNER_NAME \
<dockerhub-user>/github-actions-runner:latest-ubuntu24.04
Windows example, after setting the variables in your PowerShell environment:
docker run -d --name gha-windows `
-e RUNNER_URL -e RUNNER_TOKEN -e RUNNER_NAME `
<dockerhub-user>/github-actions-runner:latest-windows
For private-registry images, authenticate with docker login <registry>.azurecr.io
and replace the image prefix. Select your runner by name-label and operating system:
jobs:
example:
runs-on: [self-hosted, Linux, X64, my-linux-runner]
steps:
- run: echo "Running on my Linux runner"
For Windows use [self-hosted, Windows, X64, my-windows-runner].
Organization runner-group access rules still determine which repositories may use a runner.
Configure these repository Actions secrets before publishing:
| Secret | Purpose |
|---|---|
USER_DOCKERHUB | Docker Hub username and image namespace |
PAT_DOCKERHUB | Docker Hub access token with push permission; overview sync requires read/write/delete scope |
URL_AZURECONTAINERREG | Registry hostname, e.g. example.azurecr.io, or its HTTPS origin; no repository path |
USER_AZURECONTAINERREG | Registry login username |
PW_AZURECONTAINERREG | Registry password or token with push permission |
Both registries must be reachable from GitHub-hosted runners. A registry behind a
private endpoint requires a network-connected self-hosted build runner instead.
Docker Hub must allow the supplied user to push to github-actions-runner.
Enable Settings > Actions > General > Allow GitHub Actions to create and approve
pull requests. Set Settings > Pages > Build and deployment > Source to GitHub Actions.
Ensure luth2 can be assigned to repository PRs.
release.yml: validates the project, runs semantic-release on main, then builds
Ubuntu and Windows images in a matrix and pushes each image to both registries.
Pull requests build and smoke-test both images without registry credentials or pushes.fix: creates a patch, feat: a minor, and a
BREAKING CHANGE: footer a major release. The first release is 1.0.0.
Use a release-worthy first commit such as feat: add runner release pipeline.
Squash-merge PRs with a matching Conventional Commit title.chore: commits do not publish images.
Release notes and Git tags are generated by semantic-release; nothing is published to npm.update-runner.yml: checks official stable actions/runner releases daily at
06:23 UTC and updates both versions and SHA256 digests in a fix(deps) PR assigned to luth2.
Both assets must be present; Docker builds verify their SHA256 checksums.luth2.pages.yml: publishes this README as English/German documentation.dockerhub-overview.yml: syncs this README to the Docker Hub repository overview
on README changes to main or a manual run. The existing PAT_DOCKERHUB must have
read/write/delete scope; a push-only token is insufficient for this action.Runner-update PRs use GITHUB_TOKEN, which does not trigger normal PR CI workflows.
Manually run Validate, release and publish containers on the update branch to
validate it before merging. Non-main manual runs build without publishing.
Main-branch releases and image publication share one workflow, so no release-event PAT is needed.
If image publishing fails after a release was created, rerun the workflow: it reuses
the existing version at that commit. Older releases cannot overwrite latest through a rerun.
Registry pushes are not transactional; a failed run can leave one registry or platform updated.
The Linux image includes Docker CLI and Buildx from Docker's signed APT repository. It connects to a host Docker engine; it does not start Docker-in-Docker. Start it on a dedicated Linux Docker host with the socket and its numeric group ID:
docker run -d --name gha-docker \
-e RUNNER_URL -e RUNNER_TOKEN -e RUNNER_NAME \
--mount type=bind,src=/var/run/docker.sock,dst=/var/run/docker.sock \
--group-add "$(stat -c '%g' /var/run/docker.sock)" \
<dockerhub-user>/github-actions-runner:latest-ubuntu24.04
Set RUNNER_NAME=docker-runner for this workflow example:
jobs:
image:
runs-on: [self-hosted, Linux, X64, docker-runner]
permissions:
contents: read
steps:
- uses: actions/checkout@v6
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
username: ${{ secrets.USER_DOCKERHUB }}
password: ${{ secrets.PAT_DOCKERHUB }}
- uses: docker/build-push-action@v6
with:
context: .
file: Dockerfile
push: true
tags: ${{ secrets.USER_DOCKERHUB }}/my-image:latest
Use the Dockerfile path in the job's repository, e.g. Dockerfile.linux when building
this project. Verify daemon access with docker exec gha-docker docker info.
No --privileged or world-writable socket permissions are needed.
The socket gives workflows effectively root access to the host, not just the runner
container: never run untrusted PRs on this runner. Container-based Actions and jobs.container
also require runner work directories mounted at the same absolute path on host and runner;
the socket-only example is for JavaScript Actions and streamed Docker build contexts.
Start on a compatible, dedicated Windows container host with its Docker engine pipe:
docker run -d --name gha-windows-docker `
-e RUNNER_URL -e RUNNER_TOKEN -e RUNNER_NAME `
--mount 'type=npipe,source=\\.\pipe\docker_engine,target=\\.\pipe\docker_engine' `
<dockerhub-user>/github-actions-runner:latest-windows
docker exec gha-windows-docker docker info
The runner and jobs run as ContainerAdministrator, so jobs can install tools without
sudo. Git, Docker CLI and Buildx are available on startup; no Docker service starts
inside the container. The named pipe gives jobs administrative control over the host
Docker engine: only run trusted workflows and never unreviewed PRs.
For native Windows image builds, use PowerShell steps with docker build, docker login
and docker push; set $env:DOCKER_BUILDKIT = '0' for the legacy Windows builder.
Installing Buildx does not install a BuildKit server. Windows BuildKit support is
experimental and requires a separately configured compatible remote builder. The Linux
docker/setup-buildx-action example above is not a drop-in Windows configuration.
Docker container Actions and jobs.container are unsupported on Windows runners.
Building Linux images requires a remote Linux engine or BuildKit builder, not the local Windows engine.
docker build -f Dockerfile.linux -t github-actions-runner:local .
npm ci --ignore-scripts
npm test
npm run docs:build
Build Windows with docker build -f Dockerfile.windows -t github-actions-runner:local .
on a Windows container host. The generated Pages site is in docs/_site/.
The runner name must be unique. --replace replaces an existing registration of the
same name; do not run two containers with that name. Registration is skipped when
the container already has .runner; changed URL/name values require recreating it.
The token is unset before the runner starts, but Docker administrators can still
inspect the container's configured environment. Do not bake credentials into images.
These are persistent runners: shutdown does not automatically deregister them.
Delete stale registrations in GitHub when replacing/removing containers. Jobs share
the runner workspace until the container is recreated; only run trusted workflows.
No host Docker socket/pipe or language-specific build toolchains are included. Both images
include command-line Git; install additional tools as your jobs require.
The Linux runner user has passwordless sudo: jobs can run sudo apt-get update
and sudo apt-get install -y <package>. This grants full root access inside the
container; use it only for trusted workflows. The runner itself starts as a non-root user.
Windows starts as ContainerAdministrator, with full administrative rights inside the container.
Runner self-updates remain enabled; rebuild and redeploy for fresh base-image patches.
Both Dockerfiles declare owner=luth2 and the OCI author luth2 (https://github.com/luth2).
The npm release-tool dependency tree currently has upstream security advisories that
compatible updates do not resolve. Install scripts are disabled, PR jobs have no registry
credentials, and publishing runs only on trusted main; review npm audit before deployment.
Beide Images sind fuer x64: Ubuntu 24.04 beziehungsweise Windows Server Core
LTSC 2022. Windows benoetigt einen kompatiblen Windows-Containerhost, keine Linux-Engine.
Beide Multistage-Builds trennen Download und Entpacken vom Laufzeitimage. Dieses
enthaelt Runner, Docker CLI, Buildx und Git, aber keinen Docker-Daemon oder Downloadarchive.
Linux enthaelt ausserdem den Docker-Repository-Schluessel, Zertifikate und Laufzeitbibliotheken.
Windows verwendet SHA256-gepruefte Docker-/Buildx-Binaerdateien und MinGit fuer die
Git-Kommandozeile, ohne allgemeine Git-Bash-Shell. Windows-Werkzeugversionen und Hashes
sind als Build-Argumente festgelegt und gemeinsam zu aktualisieren; der automatische Updater pflegt nur den Runner.
Veroeffentlicht wird nach Docker Hub und in die private Registry, jeweils unter
github-actions-runner. Tags sind beispielsweise 1.0.0-ubuntu24.04 und
1.0.0-windows; bewegliche Tags heissen latest-ubuntu24.04 und latest-windows.
Image-Version und enthaltene GitHub-Runner-Version sind voneinander unabhaengig.
Beim Start muessen RUNNER_URL, RUNNER_TOKEN und RUNNER_NAME gesetzt sein.
Die URL bezeichnet die Zielorganisation oder das Zielrepository und hat keinen Standardwert.
Der Token ist ein dazu passender Registrierungstoken, kein PAT; er laeuft nach einer Stunde ab.
Den Token erzeugst du unter Settings > Actions > Runners > New self-hosted runner.
Nutze die Linux-/PowerShell-Startbefehle im englischen Abschnitt mit deinen Umgebungsvariablen.
Der Name wird auch als Label hinterlegt:
runs-on: [self-hosted, Linux, X64, mein-linux-runner]
Unter Windows verwende [self-hosted, Windows, X64, mein-windows-runner].
Bei Organisations-Runnern gelten zusaetzlich die Zugriffsregeln der Runner-Gruppe.
Die fuenf oben beschriebenen Secrets sind erforderlich: USER_DOCKERHUB,
PAT_DOCKERHUB, URL_AZURECONTAINERREG, USER_AZURECONTAINERREG und
PW_AZURECONTAINERREG. Die Registry-URL muss ein Hostname oder eine HTTPS-Adresse
ohne Repository-Pfad sein. Beide Registries muessen von GitHub-hosted Runnern
erreichbar sein; fuer einen privaten Netzwerk-Endpunkt brauchst du eigene Build-Runner.
Erlaube GitHub Actions unter Settings > Actions > General das Erstellen von PRs.
Waehle unter Settings > Pages > Build and deployment > Source die Option
GitHub Actions. luth2 muss im Repository als Assignee verfuegbar sein.
Auf main erzeugt semantic-release anhand von Conventional Commits die Version:
fix: = Patch, feat: = Minor, BREAKING CHANGE: = Major; erste Version = 1.0.0.
Der erste Commit sollte beispielsweise feat: add runner release pipeline heissen.
Beim Squash-Merge muss der PR-Titel diesem Format entsprechen. Reine Doku- oder
normale chore:-Commits loesen keine Image-Veroeffentlichung aus.
Nach dem Release baut die Matrix Ubuntu und Windows auf passenden Hosts, prueft die Runner-Binaerdatei und pusht Versionstags und bewegliche Tags in beide Registries. PRs bauen und testen ohne Registry-Secrets und ohne Push. npm wird nicht veroeffentlicht.
Docker CLI und Buildx sind installiert, aber kein Docker-Daemon. Verwende den
Startbefehl im englischen Abschnitt auf einem dedizierten Linux-Dockerhost: Er bindet
/var/run/docker.sock ein und uebergibt dessen numerische Gruppen-ID mit --group-add.
Dadurch kann runner ohne sudo auf Docker zugreifen. Kein --privileged und kein
chmod 666 auf dem Socket sind erforderlich.
Mit RUNNER_NAME=docker-runner kannst du die gezeigten Actions docker/setup-buildx-action,
docker/login-action und docker/build-push-action verwenden. Passe Image-Tag und
Dockerfile-Pfad an das jeweilige Repository an. docker exec gha-docker docker info
prueft den Zugriff auf den Host-Daemon.
Der Socket gibt Jobs praktisch Root-Kontrolle ueber den Host: keine unbekannten
oder ungeprueften PRs darauf ausfuehren. Container-Actions und jobs.container brauchen
zusaetzlich Arbeitsverzeichnisse, die auf Host und Runner unter demselben absoluten
Pfad gemountet sind. Das Socket-Beispiel deckt JavaScript-Actions und gestreamte Build-Kontexte ab.
Der PowerShell-Startbefehl im englischen Windows-Docker-Abschnitt bindet die Named Pipe
\\.\pipe\docker_engine des Windows-Hosts ein. docker exec gha-windows-docker docker info
prueft die Verbindung. Der Runner startet als ContainerAdministrator: Jobs koennen
Werkzeuge direkt installieren, ohne sudo. Docker CLI, Buildx und Git sind vorinstalliert;
im Container wird kein Docker-Dienst gestartet.
Die Named Pipe gibt Jobs administrative Kontrolle ueber die Docker-Engine des Hosts:
nur vertrauenswuerdige Workflows, keine ungeprueften PRs ausfuehren.
Fuer Windows-Images nutze PowerShell-Schritte mit docker build, docker login und
docker push; $env:DOCKER_BUILDKIT = '0' waehlt den klassischen Windows-Builder.
Buildx bringt keinen BuildKit-Server mit. Windows-BuildKit ist experimentell und
braucht einen separat konfigurierten kompatiblen Remote-Builder; das Linux-Action-Beispiel
ist nicht unveraendert auf Windows uebertragbar. Container-Actions und jobs.container
werden auf Windows-Runnern nicht unterstuetzt. Linux-Images brauchen eine entfernte
Linux-Engine oder einen passenden BuildKit-Builder, nicht die lokale Windows-Engine.
Der Runner-Update-Workflow prueft taeglich um 06:23 UTC offizielle stabile Releases
aus actions/runner. Beide Downloads und SHA256-Pruefsummen muessen vorliegen.
Ein fix(deps)-PR aktualisiert beide Dockerfiles und wird luth2 zugewiesen.
Der Workflow dockerhub-overview.yml synchronisiert diese README als Docker-Hub-Overview
bei README-Aenderungen auf main oder einem manuellen Run. PAT_DOCKERHUB braucht
dafuer den Scope read/write/delete; reine Push-Rechte reichen fuer diese Action nicht.
Dependabot aktualisiert Actions und npm-Werkzeuge woechentlich. Auch alle anderen
neu erstellten oder wieder geoeffneten PRs werden luth2 zugewiesen.
Da Update-PRs mit GITHUB_TOKEN keine normale PR-CI ausloesen, starte vor dem Merge
Validate, release and publish containers manuell auf dem Update-Branch.
Manuelle Runs ausserhalb von main bauen ohne Veroeffentlichung.
Bei einem fehlgeschlagenen Registry-Push kann der Run dieselbe Release-Version
wiederverwenden. Alte Releases duerfen latest nicht zuruecksetzen; Registry-Pushes
sind trotzdem nicht atomar und koennen teilweise erfolgreich sein.
Runner-Namen muessen eindeutig sein: --replace ersetzt die gleichnamige Registrierung.
Vorhandene Container behalten ihre Registrierung; fuer eine neue URL oder einen neuen
Namen erstelle den Container neu. Der Token wird vor dem Runner-Start entfernt,
bleibt aber fuer Docker-Administratoren in der Container-Konfiguration einsehbar.
Keine Secrets ins Image schreiben.
Beim Stoppen erfolgt keine automatische Abmeldung. Entferne veraltete Runner in
GitHub. Persistente Runner teilen ihren Arbeitsbereich zwischen Jobs: nur
vertrauenswuerdige Workflows ausfuehren. Host-Docker-Socket/-Pipe und Sprach-Toolchains sind
nicht enthalten; Git ist in beiden Images vorhanden. Runner-Selbstupdates
bleiben aktiv. Fuer aktuelle Basisimage-Patches Images neu bauen und Container ersetzen.
Der Linux-Benutzer runner hat passwortloses sudo, etwa fuer sudo apt-get update
und sudo apt-get install -y <paket>. Jobs erhalten damit volle Root-Rechte im
Container: nur vertrauenswuerdige Workflows ausfuehren. Der Runner startet weiterhin ohne Root-Rechte.
Windows startet als ContainerAdministrator mit vollen Administratorrechten im Container.
Lokale Pruefung: npm ci --ignore-scripts, npm test, npm run docs:build.
Die generierte zweisprachige Seite liegt unter docs/_site/.
Beide Images tragen owner=luth2 sowie das OCI-Autorenlabel mit dem GitHub-Profil.
Die npm-Releasewerkzeuge enthalten derzeit nicht kompatibel behebbare
Upstream-Sicherheitsmeldungen. Installationsskripte sind deaktiviert; PR-Jobs erhalten
keine Registry-Zugangsdaten. Pruefe npm audit vor der Inbetriebnahme.
Content type
Image
Digest
sha256:89d63f02c…
Size
2.2 GB
Last updated
about 16 hours ago
docker pull ltamp/github-actions-runner:latest-windows