Skip to content
LatchkeyLatchkey home
Documentation menu

What is on the runner image

The preinstalled toolchain on every Latchkey runner: languages, Docker, browsers, databases, package managers, and the GitHub-compatible toolcache.

Every runner boots from a maintained Ubuntu 24.04 LTS (x86_64) image with a toolchain designed to match or exceed GitHub-hosted runners, so most workflows run unchanged.

The toolchain#

CategoryPreinstalled
Node.js20 (default), 22, 24, with npm and yarn; nvm available
Python3.10 through 3.13 (3.14 included when available), plus pipx and PyPy
Go1.22 through 1.25
Java (Temurin)8, 11, 17 (default JAVA_HOME), 21, and 25 when available, with Maven, Gradle, and Ant
Other languagesRust (stable + rustfmt + clippy), .NET 8/9/10, Ruby (+ fastlane), PHP 8.3 (+ Composer, Xdebug), Haskell, Swift, Kotlin, Julia, PowerShell
ContainersDocker CE with BuildKit, docker buildx, docker compose; Buildah, Podman, Skopeo
BrowsersChrome, Firefox, Edge with matching drivers; Selenium; Android SDK/NDK
DatabasesPostgreSQL 16, MySQL 8.0 (services disabled at boot; start them in your job)
Package managersHomebrew, conda, vcpkg, plus the language-native ones
CLI standardsAWS CLI v2, jq, git, build essentials

Matching GitHub-hosted behavior#

The most useful property of the image is what you do not have to change. Keep your actions/setup-node, actions/setup-python, and similar steps exactly as they are: they resolve from the GitHub-parity toolcache at /opt/hostedtoolcache instantly instead of downloading, so the same workflow file stays correct on both runner types while you migrate.

Version pinning advice is the same as anywhere else: let the setup actions state the version rather than leaning on image defaults. Node 20 is the default with 22 and 24 also installed, but a setup-node step with an explicit node-version makes the workflow say what it means, and keeps it stable as the maintained image evolves.

Three things worth knowing#

Native DockerDocker runs on the host itself: no docker-in-docker workarounds, and BuildKit is on by default.
Multi-arch buildsQEMU/binfmt is preconfigured, so docker buildx --platform linux/amd64,linux/arm64 works out of the box.
GitHub-parity toolcacheactions/setup-node, setup-python, and friends resolve from /opt/hostedtoolcache instantly, without downloading.

Databases and browsers#

PostgreSQL 16 and MySQL 8.0 are preinstalled with their services disabled at boot, so they stay out of the way until a job asks for them. Starting the one you need is a one-line step, and it is standard Ubuntu service management rather than anything Latchkey-specific:

Start the service before the steps that use it:

.github/workflows/ci.yml
steps:
  - uses: actions/checkout@v4
  - name: Start PostgreSQL
    run: sudo systemctl start postgresql
  - run: npm test

The same pattern for MySQL 8.0:

.github/workflows/ci.yml
steps:
  - uses: actions/checkout@v4
  - name: Start MySQL
    run: sudo systemctl start mysql
  - run: npm test

Chrome, Firefox, and Edge are preinstalled with matching driver versions, and Selenium is available, so most browser test suites run with no install step at all. For mobile work, the Android SDK and NDK are on the image too.

Starting services inside the job also keeps the dependency explicit: anyone reading the workflow file can see that the suite needs a database, which is worth having when a workflow moves between runner types.

What is preinstalled on a Latchkey runner?

Ubuntu 24.04 with Node, Python, Go, Java, Rust, .NET and more, plus a toolcache built for parity with GitHub-hosted runners. Docker is native, with BuildKit, buildx and compose, and QEMU multi-arch builds work without setup.

Are databases available on the runner?

PostgreSQL 16 and MySQL 8.0 are installed with their services off at boot, so they cost nothing until you start them. Start the service in a step when a job needs it rather than running a service container.

What if a tool my build needs is missing?

Install it in your job steps exactly as you would on a GitHub-hosted runner; the image is a starting point, not a constraint. If the same install runs on every build, that is the signal to move it into a custom runner image instead.

References