Skip to content

About

Self-hosted GitHub Actions runner image based on the official runner image, with extra build tooling

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

actions-runner

A self-hosted GitHub Actions runner image based on the official ghcr.io/actions/actions-runner image, extended with build tooling: a JDK, common command-line tools, and on-demand Android SDK setup.

ghcr.io/blafry/actions-runner:latest

What's inside

Component Details
Base The official runner image as published by GitHub (user runner, Docker CLI, runner container hooks)
JDK Eclipse Temurin 21 from the Adoptium APT repository (JAVA_HOME=/usr/lib/jvm/temurin-21-jdk-amd64)
Tools python3, zip, unzip, curl, jq, git
Android SDK Installed on first use by ensure-android-sdk (see below) into ANDROID_HOME=/home/runner/android-sdk

PATH includes the JDK, cmdline-tools/latest/bin and platform-tools (adb).

Why the Android SDK is installed on first use

The Android SDK License Agreement does not allow redistributing the SDK, so the published image doesn't contain it. Instead, the runner runs /usr/local/bin/ensure-android-sdk before every job (ACTIONS_RUNNER_HOOK_JOB_STARTED):

  • On the first job it downloads the command-line tools (SHA-1 verified) and installs ANDROID_SDK_PACKAGES with the Android CLI. That takes about 20-60 seconds.
  • On later jobs it returns immediately, as long as ANDROID_HOME is on a persistent volume.

Installing the SDK this way accepts the Android SDK Terms of Service on behalf of whoever runs the runner. To bake the SDK into a private image instead, build it yourself with --build-arg BAKE_ANDROID_SDK=true.

Building locally

RUNNER_VERSION is required (there is deliberately no default):

docker build --build-arg RUNNER_VERSION=2.x.y -t actions-runner .

Configuration

Variable Default Meaning
ANDROID_HOME / ANDROID_SDK_ROOT /home/runner/android-sdk SDK location; mount a volume here
ANDROID_SDK_PACKAGES platform-tools platforms/android-37.0 build-tools/36.0.0 Packages to keep installed (Android CLI syntax)
ANDROID_SDK_SKIP unset 1 skips the SDK setup (runners that never build Android)
CMDLINE_TOOLS_ZIP, CMDLINE_TOOLS_SHA1 pinned Command-line tools archive and checksum

Usage

Docker

docker volume create android-sdk
docker run -d --name runner \
  -v android-sdk:/home/runner/android-sdk \
  -v gradle-cache:/home/runner/.gradle \
  ghcr.io/blafry/actions-runner:latest \
  bash -c './config.sh --url https://github.com/<owner>/<repo> --token <registration-token> --unattended --ephemeral && ./run.sh'

actions-runner-controller (Kubernetes)

Runner scale set values:

template:
  spec:
    securityContext:
      fsGroup: 1001            # lets the runner write to the mounted volumes
    containers:
      - name: runner
        image: ghcr.io/blafry/actions-runner:latest
        command: ["/home/runner/run.sh"]
        resources:
          requests: { cpu: "2", memory: 6Gi }
          limits: { cpu: "4", memory: 8Gi }
        volumeMounts:
          - { name: android-sdk, mountPath: /home/runner/android-sdk }
          - { name: gradle, mountPath: /home/runner/.gradle }
    volumes:
      - name: android-sdk
        persistentVolumeClaim: { claimName: android-sdk }
      - name: gradle
        persistentVolumeClaim: { claimName: gradle-cache }

Device tests

Instrumented tests need a device:

  • adb over Wi-Fi (simplest in containers): adb connect <device-ip>:5555 at the start of the job, after enabling wireless debugging on the device once.
  • USB: pass the device into the container (--device /dev/bus/usb with Docker, a hostPath mount and a node selector on Kubernetes) and accept the debugging prompt on the device. Persist /home/runner/.android so the adb key survives restarts.

Emulators need /dev/kvm and a lot more resources; they are not the focus of this image.

Sizing

Typical Gradle Android builds with unit tests:

vCPU RAM Disk
Minimum 2 6 GB 20 GB
Recommended 4 8 GB 30 GB
Large projects 8 16 GB 50 GB

On small runners, limit Gradle in ~/.gradle/gradle.properties, e.g. org.gradle.jvmargs=-Xmx2g, kotlin.daemon.jvmargs=-Xmx1536m, org.gradle.workers.max=2.

Tags and updates

Tag Meaning
latest Newest build
<runner version> (e.g. 2.x.y) Newest build on that upstream runner version
<runner version>-<YYYYMMDD> A specific build

A build happens only when it is needed (.github/scripts/resolve-versions.sh):

  • Weekly (Fridays 04:20 UTC), the workflow compares the newest version of the official runner image (its newest x.y.z tag) with the newest version built here, and builds only if upstream is newer.
  • Pushes to master that change the image (Dockerfile, rootfs/, scripts, the workflow) always build, on the newest upstream version.
  • On demand (workflow_dispatch) it behaves like the weekly run; force builds anyway.

Every build is smoke-tested before it is published (runner user and version, JDK, SDK setup, adb).

License

The files in this repository are under the MIT License. The base image and the software installed into containers keep their own licenses (GitHub Actions runner: MIT; Eclipse Temurin: GPLv2 with Classpath Exception; Android SDK: Android SDK License Agreement, accepted by whoever runs ensure-android-sdk).

About

Self-hosted GitHub Actions runner image based on the official runner image, with extra build tooling

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages