DEV Community

Cover image for What is installed on ubuntu-latest: the GitHub Actions runner image
Kay
Kay

Posted on Originally published at latchkey.dev

What is installed on ubuntu-latest: the GitHub Actions runner image

What is installed on ubuntu-latest is whatever the current Ubuntu 24.04 runner image ships, because the ubuntu-latest label resolves to ubuntu-24.04 today. The versions below are from the image README read on 2026-09-20, beside the same commands run on a Latchkey runner the same day.

Label chain: ubuntu-latest resolves to ubuntu-24.04, then to a dated image version
Three names, three lifetimes. Only the middle one is stable enough to pin, and only the last one is what actually ran your job.

Recorded log of 21 tool versions printed on a Latchkey latchkey-small Ubuntu 24.04 runner
The same version script on a Latchkey latchkey-small runner, 2026-09-20, from content/repro/whats-installed-on-ubuntu-latest.sh.

Two questions bring people here. The first is whether a tool is on the machine already, so a workflow can call it without a setup step. The second is which version, because a job that works on one image release breaks on the next when a major version moves under it.

Both answers change every few weeks. Treat the table below as a snapshot with a date on it, and treat the last section, printing the versions from inside your own job, as the answer that never goes stale.

What ubuntu-latest resolves to today

ubuntu-latest is a label, not an image. It currently points at ubuntu-24.04, which GitHub states in its hosted-runner reference and in the runner-images repository, where the available-images table lists the YAML labels for that image as "ubuntu-latest" or "ubuntu-24.04". Both were read on 2026-09-20.

Behind the label sits a specific build. The Ubuntu 24.04 README on the same day reported OS version 24.04.5 LTS, kernel 6.17.0-1022-azure and image version 20260907.300.1. Those rebuild continuously, which is why a job can start failing without a workflow change: the image moved, not your YAML.

The label itself moves too, and slowly. The runner-images repository says the -latest label is used for the latest OS image version that is generally available, and that the migration is gradual, over one to two months, so workflows can adapt. GitHub's own reference adds the caveat that -latest images are the latest stable images GitHub provides and might not be the most recent version the OS vendor has released.

The versions, side by side

The left column is the GitHub image README read on 2026-09-20. The right is the same tool printed by content/repro/whats-installed-on-ubuntu-latest.sh on a Latchkey latchkey-small runner the same day. They are two different images and the second column is here to make that concrete rather than to claim parity.

Tool GitHub ubuntu-24.04 (image 20260907.300.1) Latchkey latchkey-small
OS Ubuntu 24.04.5 LTS, kernel 6.17.0-1022-azure Ubuntu 24.04.4 LTS, kernel 6.17.0-1019-aws
Node.js (default) 22.23.2 v20.20.2
Node.js (cached) 22.23.2, 24.20.0 Node 20, 22 and 24 in the toolcache
npm 10.9.8 10.8.2
Yarn 1.22.22 1.22.22
Python 3.12.3 (cached: 3.10 to 3.14) Python 3.12.3
pip 24.0 pip 24.0
Ruby 3.2.3 (cached: 3.2 to 4.0) ruby 3.2.3
Go 1.24.13, 1.25.14, 1.26.8 go1.24.13
Java (default) 17.0.20+1, plus 8, 11, 21 and 25 openjdk 17.0.20.1
.NET SDK 8.0.130 through 10.0.400 10.0.400
PHP 8.3.6 PHP 8.3.6
Docker client 28.0.4 29.7.2
Docker Compose 2.38.2 v5.5.0
Docker Buildx 0.37.0 v0.36.1
Git 2.55.0 2.55.0
GitHub CLI 2.100.0 2.98.0
AWS CLI 2.36.40 2.36.31
Azure CLI 2.90.0 not printed by the script
Google Cloud CLI 583.0.0 582.0.0
kubectl 1.37.0 a development build string, not a release
Helm 3.21.4 not printed by the script
gcc / GNU C++ 12.4.0, 13.3.0, 14.2.0 gcc 14.2.0
Clang 16.0.6, 17.0.6, 18.1.3 not printed by the script
CMake 3.31.6 4.4.2
Maven / Gradle / Ant 3.9.16 / 9.7.1 / 1.10.14 Maven, Gradle and Ant preinstalled
Google Chrome 152.0.7977.82 152.0.7977.64
Firefox / Edge 155.0 / 152.0.4191.66 Firefox and Edge preinstalled
Selenium server 4.48.0 Selenium preinstalled
PostgreSQL / MySQL 16.15 / 8.0.46, both disabled at boot 16 / 8.0, both disabled at boot
jq / yq 1.7 / 4.53.6 jq preinstalled

Rows on the right that say "preinstalled" are from the Latchkey runner image documentation rather than from the run: the script printed 21 versions and the table names more tools than that.

What is not on the image

Absent from the Ubuntu 24.04 README on 2026-09-20: pnpm, Bun, Deno and Terraform. pnpm and Bun arrive through Corepack or their own installers. Terraform is the one that catches people, because it shipped on ubuntu-22.04 and does not ship here: actions/runner-images#13876 is the tool request filed when ubuntu-latest moved to 24.04, and a workflow that expects it now needs hashicorp/setup-terraform or an explicit install step.

Present but not running: PostgreSQL 16.15 and MySQL 8.0.46 are installed and disabled at boot, so a job that needs either starts the service itself with sudo systemctl start postgresql.service or the MySQL equivalent. A service container is usually the better answer, because it pins the version.

The general rule is to install what you need rather than assume it. apt-get install in a step works for system packages, the setup-* actions are faster for language runtimes because they resolve from the toolcache, and anything heavier belongs in a container image you control.

- name: Install what the image does not ship
  run: |
    corepack enable pnpm
    sudo apt-get update && sudo apt-get install -y --no-install-recommends libgbm1
- uses: actions/setup-node@v7
  with:
    node-version: 22
    cache: npm
Enter fullscreen mode Exit fullscreen mode

Print the versions from inside your own job

The only list that is true for your run is the one your run prints. Drop this step in front of the job you are debugging: it costs a second, and it turns "the image must have changed" from a theory into a line you can diff against the previous run.

- name: Image and tool versions
  run: |
    cat /etc/os-release | head -2
    echo "image: ${ImageOS:-unknown} ${ImageVersion:-unknown}"
    node --version; npm --version; python3 --version
    go version; java -version; docker --version
    docker compose version; git --version; gh --version | head -1
Enter fullscreen mode Exit fullscreen mode

The runner sets ImageOS and ImageVersion in the environment, so a job can record exactly which image build it ran on without parsing anything.

The same script on a Latchkey runner

A Latchkey runner is not a GitHub-hosted runner, and the point of running the same script on one is to show the difference rather than to imply there is none. The documentation describes the fleet as Ubuntu 24.04 LTS on x86_64, on AWS m6a-class hardware, with a preinstalled toolchain and a GitHub-parity toolcache at /opt/hostedtoolcache, so actions/setup-node and friends resolve instantly instead of downloading.

What that parity does not mean is identical versions. In the run below Node is 20.20.2 rather than 22.23.2, Docker is a major version ahead, and CMake is 4.4.2 against the GitHub image's 3.31.6. If your workflow depends on a default version rather than a setup-* action pinning one, that is the kind of difference that will find you.

```latchkey run, recorded 2026-09-20 on latchkey-small
image: Ubuntu 24.04.4 LTS (x86_64, kernel 6.17.0-1019-aws)
node v20.20.2
npm 10.8.2
yarn 1.22.22
python3 Python 3.12.3
pip3 pip 24.0 from /usr/lib/python3/dist-packages/pip (python 3.12)
ruby ruby 3.2.3 (2024-01-18 revision 52bb2ac0a6) [x86_64-linux-gnu]
go go version go1.24.13 linux/amd64
java openjdk version "17.0.20.1" 2026-08-18
dotnet 10.0.400
php PHP 8.3.6 (cli) (built: Jul 16 2026 18:30:41) (NTS)
docker Docker version 29.7.2, build a7dcaa6
docker compose Docker Compose version v5.5.0
buildx github.com/docker/buildx v0.36.1 1d8dde89b8aba914e05e45366770736fea1fd690
git git version 2.55.0
gh gh version 2.98.0 (2026-08-20)
aws aws-cli/2.36.31 Python/3.14.6 Linux/6.17.0-1019-aws exe/x86_64.ubuntu.24
gcloud Google Cloud SDK 582.0.0
kubectl Client Version: v0.0.0-master+$Format:%H$
gcc gcc (Ubuntu 14.2.0-4ubuntu2~24.04.1) 14.2.0
cmake cmake version 4.4.2
chrome Google Chrome 152.0.7977.64




## The disk all of this is sitting on

A standard GitHub-hosted Linux runner gives the job 14 GB of SSD, with 4 vCPU and 16 GB of RAM on a public repository and 2 vCPU and 8 GB on a private one, per GitHub's hosted-runner reference read on 2026-09-20. The preinstalled toolchain in the table above is sitting in that 14 GB before your job starts.

That is why the two most common failures on this image are about space rather than software. [Freeing disk space on GitHub Actions runners](https://latchkey.dev/learn/failures/free-disk-space-on-github-actions-runners) measures what each toolchain directory gives back, and [no space left on device](https://latchkey.dev/learn/failures/no-space-left-on-device-github-actions) covers the failure itself, including the variant that reports the same error with a half-empty disk.

## FAQ

### Is ubuntu-latest 22.04 or 24.04?

24.04. The runner-images available-images table lists `"ubuntu-latest" or "ubuntu-24.04"` as the labels for that image, read on 2026-09-20. The Ubuntu 22 images are on the way out: the repository announcement (actions/runner-images#14254) says they began deprecation on 17 September 2026 and are fully unsupported from 17 April 2027 for GitHub Actions and Azure DevOps.

### How do I pin the runner image?

Use the versioned label, `runs-on: ubuntu-24.04`, rather than `ubuntu-latest`. That pins the OS release but not the build: image versions still roll forward underneath it, so record `ImageVersion` in the job if you need to know exactly which one ran. There is no label that pins a specific image build, which is what actions/runner-images#13034 asks for.

### How do I install a tool that is not preinstalled?

For system packages, `sudo apt-get update && sudo apt-get install -y <package>` in a step. For language runtimes, prefer the `setup-*` actions, which resolve from the toolcache instead of downloading. For anything large or slow, bake it into a container image. Tool requests such as actions/runner-images#13876, filed when Terraform left the image, are what the alternative looks like: waiting for someone else to add it.

### Why did my job run out of disk after an ubuntu-latest image update?

Because the image grew into headroom your job was quietly relying on. The toolchains in the table are rebuilt continuously and a new one can take a gigabyte you were using. Add a cleanup step ahead of the heavy work rather than treating it as a one-off, and record `df -h` on failure so the next occurrence arrives with evidence.

## References

- [actions/runner-images: Ubuntu 24.04 image README (read 2026-09-20)](https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md)
- [GitHub-hosted runners: labels and standard specifications](https://docs.github.com/en/actions/reference/runners/github-hosted-runners)
- [actions/runner-images: available images and the -latest migration](https://github.com/actions/runner-images)
- [Latchkey runner image and software](https://latchkey.dev/documentation/runner-image-software)
- [actions/runner-images#14254: Ubuntu 22 image deprecation schedule](https://github.com/actions/runner-images/issues/14254)
Enter fullscreen mode Exit fullscreen mode

Top comments (0)