Skip to main content
DevOps Insightsguides & answers

ubuntu-latest is moving to Ubuntu 26.04

Between 19 October 2026 and 19 November 2026, GitHub moves the ubuntu-latest label from Ubuntu 24.04 to 26.04, gradually. A workflow that worked last week can fail without a single change to it, because Docker, Compose, Helm, Python, Java, CMake and PostgreSQL all jump a major version and some tools are removed. This page lists what breaks, how to check, and how to pin.

Checked against GitHub’s announcement and both image software lists · 19 October 2026

On this page · 8 sections

Are you affected?

Only jobs that say runs-on: ubuntu-latest move. Jobs that name a version, such as ubuntu-24.04, stay where they are.

Find the jobs that will move

grep -rn 'ubuntu-latest' .github/workflows/

See which image a run actually used

echo "$ImageOS $ImageVersion"

Add it as a step. The Ubuntu runner images set both variables: ImageOS is ubuntu24 on the old image and ubuntu26 on the new one (with -arm64 on Arm). During the rollout, this is how to tell whether a sudden failure came with the new image.

Pin, or test first

GitHub’s advice is to test against ubuntu-26.04 before the move reaches you, and to pin ubuntu-24.04 if you are not ready. Pinning makes the date yours rather than GitHub’s; it is not a reason to stay on 24.04 for good.

Stay on 24.04 for now

jobs:
  build:
    runs-on: ubuntu-24.04   # stays on 24.04 after the move

Run both, and see what differs

jobs:
  test:
    strategy:
      matrix:
        os: [ubuntu-24.04, ubuntu-26.04]
    runs-on: ${{ matrix.os }}

What “checked” means here. The dates and the advice come from GitHub’s announcement. The version changes come from diffing the two images’ software lists (20261012.334.1 and 20261012.163.1) line by line, not from the preview announcement’s table, which is out of date on Python, Swift and Clang. What each jump breaks comes from that project’s own release notes, linked on every item. Nothing here was run on a 26.04 runner.

What changes on the image

Preinstalled versions on Ubuntu 24.04 and 26.04 runner images
Tool24.0426.04
Docker (client and server)28.0.429.4.2
Docker Compose2.38.25.1.3
Helm3.22.04.3.0
Python (default python3)3.12.33.14.4
Node.js (default)22.23.324.21.0
npm10.9.911.19.0
Java (default JAVA_HOME)1725 (Java 8 removed)
Ruby3.2.33.3.8
PHP8.3.68.5.4
GCC / g++12, 13, 1413, 14, 15
Clang16, 17, 1820, 21, 22
CMake3.31.64.4.4
PostgreSQL16.1518.6
Podman4.9.35.7.0
OpenSSL3.0.133.5.5

Removed: Pulumi, Miniconda, Julia, Fastlane, Mercurial, Haveged, MediaInfo, Sphinx Open Source Search Server, global npm modules: Lerna, Newman, Parcel and others.

The operating system

steps fail

/tmp is a RAM disk with a quota: large builds fail with "disk quota exceeded"

Affects: Jobs that write gigabytes to /tmp: large Go, Rust or C++ builds, Docker exports, test suites with big fixtures.

Ubuntu 26.04 mounts /tmp as a tmpfs (systemd's upstream tmp.mount: 50% of RAM, about 7.8G on a standard runner, with a per-user quota). The runner-images maintainers measured the runner user hitting the quota at about 6.3G, while the root disk still has about 90G free. A fix that keeps /tmp on the root disk was merged on 20 Oct 2026 (runner-images #14814); images built before it, including 20261018, still have the tmpfs.

Check before upgrading

findmnt -no FSTYPE,SIZE,OPTIONS /tmp

tmpfs with usrquota means the job has the RAM-backed /tmp. Anything else: the fixed image.

Fix

  • Point temporary files at the work disk: as the first step, echo "TMPDIR=$RUNNER_TEMP" >> "$GITHUB_ENV" (later steps inherit it), and set GOTMPDIR the same way for Go builds.
  • Or, as the first step of the job, put /tmp back on the root disk, as the maintainers suggest: sudo systemctl mask tmp.mount && sudo umount -l /tmp. Files already in /tmp are hidden after that, so it must run first.
  • Or run the job on ubuntu-24.04 until an image with the fix reaches your runners.

In depth: disk quota exceeded writing to /tmp on Ubuntu 26.04

Sources: runner-images#14777: /tmp is a RAM-backed tmpfs on 26.04 · runner-images#14814: keep /tmp on the root disk (merged 20 Oct 2026) · Ubuntu 26.04 release notes: /tmp is now a tmpfs

Docker and Compose

no error, different result

Containers get 1024 open files instead of 1048576

Affects: Jobs that start databases, search engines or JVM services with docker run, Compose or service containers.

Docker 29.0 moved to containerd 2.1.5, which uses systemd's default LimitNOFILE for containers. In the release notes' words, this changes ulimit -n from 1048576 to 1024. Docker prints nothing; a busy service fails later with the operating system's "Too many open files" (EMFILE).

Check before upgrading

docker run --rm <your-image> sh -c 'ulimit -n'

Run it as a step on ubuntu-26.04. 1024 means the new default applies to that job.

Fix

  • Raise it where the container starts: docker run --ulimit nofile=65536:65536, or ulimits: nofile in the Compose file.
  • Give the service a limit it actually needs rather than restoring 1048576 everywhere.

Source: Docker Engine 29 release notes

worth knowing

"Client version is too old": the Docker 29 floor is lower than most guides say

Affects: Jobs where an older client or library talks to the runner's Docker daemon: SDKs embedded in tools, or an old CLI in a container.

Docker 29.0 made the daemon require API v1.44 (Docker 25) or later, and many guides still say so. Docker 29.3 lowered the minimum back to API v1.40, which is Docker 19.03 (moby#52067). The runner's 29.4.2 has that lower floor, so only clients older than 19.03 are refused.

What you see

client version %s is too old. Minimum supported API version is %s, please upgrade your client to a newer version

From moby source at docker-v29.4.2, daemon/server/middleware/version.go; the two values are filled in from the request.

Check before upgrading

docker version

Server: Engine 29.4.2 on the new image; its API version line shows the minimum it accepts.

Fix

  • Upgrade the client or library that sends the old API version. Pinning DOCKER_API_VERSION to an old value makes it worse, not better.

Source: Docker Engine 29 release notes

no error, different result

docker image ls prints a different view

Affects: Scripts that parse the plain output of docker images or docker image ls.

Docker 29's docker image ls uses the new collapsed tree view by default. Output parsed with awk or cut can stop matching.

Check before upgrading

grep -rnE 'docker (image ls|images)' .github/ scripts/ 2>/dev/null | grep -v -- '--format'

Any match that parses the table rather than asking for a format.

Fix

  • Ask for exactly what you need: docker image ls --format '{{.Repository}}:{{.Tag}}', or --quiet for IDs.

Source: Docker Engine 29 release notes

worth knowing

Compose jumps from v2 to v5, and builds go through Bake

Affects: Jobs that run docker compose build, or check the Compose version in a script.

Compose v5.0.0 removed its internal builder; builds are delegated to Docker Bake, the same path as docker build. The version skips 3 and 4 on purpose, to avoid confusion with the old file-format versions. The runner also ships buildx 0.38.0, so BuildKit is there for Bake to use.

Check before upgrading

docker compose version

v5.1.3 on the new image. A script that expects a version starting with 2 needs updating.

Check before upgrading

docker compose build

Run it once on ubuntu-26.04 before the migration reaches you.

Fix

  • Fix version checks to accept 5. If a build behaves differently, compare it with the same Dockerfile under docker buildx bake.

Source: Docker Compose v5.0.0 release notes

Helm

job fails at once

Helm 4: --post-renderer needs a plugin name, not a script

Affects: Deploy jobs that pass a script to --post-renderer, often a kustomize wrapper.

The runner's Helm goes from 3.22.0 to 4.3.0. The Helm 4 overview lists this as breaking: post-renderers are plugins now, and passing an executable to --post-renderer no longer works.

Check before upgrading

grep -rn -- '--post-renderer' .github/ 2>/dev/null

Any use with a path to a script.

Fix

  • Package the post-renderer as a Helm 4 plugin and pass its name.
  • Or keep Helm 3 for now by pinning it in the workflow with Azure/setup-helm.

Source: Helm 4 overview: breaking changes

warning now

Helm 4: renamed flags, registry login by hostname, server-side apply

Affects: Any job that runs helm.

--atomic is now --rollback-on-failure and --force is now --force-replace; the old flags still work but print a deprecation warning. helm registry login must be given the domain name only, not a full URL. New releases are installed with server-side apply; upgrades keep the method a release already used.

Check before upgrading

grep -rnE 'helm .*(--atomic|--force[^-])|helm registry login [^ ]*/' .github/ 2>/dev/null

Old flag names, and registry logins with a path after the host.

Fix

  • Rename the flags, and log in with the bare registry host.

Source: Helm 4 overview: breaking changes

Languages

steps fail

python3 is 3.14, and 19 standard-library modules are gone

Affects: Steps that call python3 or pip without actions/setup-python.

The system Python goes from 3.12.3 to 3.14.4. Python 3.13 removed 19 "dead batteries" deprecated since 3.11 (PEP 594), including cgi, cgitb, crypt, audioop, aifc and chunk, so an import of one fails with ModuleNotFoundError. The image still caches 3.10 to 3.14 for setup-python.

Check before upgrading

python3 --version

3.14.x on the new image. Then grep your scripts for the removed modules.

Fix

  • Pin the version you test against with actions/setup-python, and replace removed modules with the alternatives the Python docs give.

Sources: What's New in Python 3.13: removed modules (PEP 594) · Ubuntu 26.04 image: installed software (actions/runner-images)

steps fail

Default Java is 25 and Java 8 is gone; default Node.js is 24

Affects: Jobs that rely on the image's default JAVA_HOME or node without a setup action.

The default JAVA_HOME moves from Java 17 to Java 25, and JAVA_HOME_8_X64 no longer exists; 11, 17, 21 and 25 remain. The default Node.js moves from 22 to 24 (npm 10 to 11); 22 stays cached.

Check before upgrading

java -version; echo "$JAVA_HOME_8_X64"; node --version

25, an empty Java 8 variable and v24 on the new image.

Fix

  • Pin with actions/setup-java and actions/setup-node. A job that needs Java 8 must install it.

Source: Ubuntu 26.04 image: installed software (actions/runner-images)

Build tools and databases

steps fail

CMake 4 refuses projects that claim support for CMake older than 3.5

Affects: C and C++ builds whose CMakeLists.txt, or a vendored dependency's, has cmake_minimum_required below 3.5.

CMake goes from 3.31.6 to 4.4.4. CMake 4.0 removed compatibility with versions older than 3.5: cmake_minimum_required or cmake_policy calls that set an older policy version now stop with an error.

Check before upgrading

grep -rn 'cmake_minimum_required' --include=CMakeLists.txt .

VERSION values below 3.5, including in vendored dependencies.

Fix

  • Raise the minimum, or use a range such as cmake_minimum_required(VERSION 3.5...3.31).
  • To configure an old dependency you cannot edit, CMake 4 added the CMAKE_POLICY_VERSION_MINIMUM variable and environment variable for exactly this.

Source: CMake 4.0 release notes

no error, different result

The preinstalled PostgreSQL is 18

Affects: Jobs that use the runner's own PostgreSQL rather than a service container or setup action.

PostgreSQL goes from 16 to 18. Among 18's compatibility notes, initdb now enables data checksums by default. On Debian-style layouts the binaries move from /usr/lib/postgresql/16/bin to /usr/lib/postgresql/18/bin.

Check before upgrading

grep -rn 'postgresql/16' .github/ scripts/ 2>/dev/null

Hard-coded 16 paths.

Fix

  • Use a service container with a pinned postgres image tag, so the version is yours, not the image's.

Sources: PostgreSQL 18 release notes: migration · Ubuntu 26.04 image: installed software (actions/runner-images)

job fails at once

Tools removed from the image

Affects: Jobs that call any of: Pulumi, Miniconda, Julia, Fastlane, Mercurial, Haveged, MediaInfo, Sphinx search, or globally installed npm tools such as Lerna, Newman or Parcel. GCC 12 and Clang 16 to 18 are gone too (26.04 has GCC 13 to 15 and Clang 20 to 22).

These are not on the 26.04 image. A step that calls one fails with "command not found".

Check before upgrading

grep -rnwE 'pulumi|conda|julia|fastlane|hg|lerna|newman|parcel|gcc-12|clang-1[678]' .github/ 2>/dev/null

Any step that uses one without installing it first.

Fix

  • Install what you use in the workflow: pulumi/actions, conda-incubator/setup-miniconda and julia-actions/setup-julia are the maintained actions for the first three; npx or npm install -g for npm tools.

Sources: actions/runner-images #14226: software removed in Ubuntu 26.04 · Ubuntu 26.04 image: installed software (actions/runner-images)

Deploying with Helm? Compare your charts first: Helm 3 to Helm 4. Running Kubernetes too? See what breaks from Kubernetes 1.34 to 1.37. A job that passes locally and fails in CI has its own page: passes locally, fails in CI.

Sources

Every item above links its own sources; this is the full list.

Did this get you to an answer?

No text box on purpose — please do not paste production logs anywhere

Something wrong or out of date? Report it