The problem
GitHub is moving the ubuntu-latest runner label from Ubuntu 24.04 to Ubuntu
26.04 between 19 October and 19 November 2026. Three kinds of failure can be
predicted from the workflow alone: apt packages that 26.04 no longer has (for
example gcc-9, gcc-10, clang-14 to clang-16, postgresql-client-16,
lzma-dev, libatlas-base-dev), Python versions that actions/setup-python
cannot install on 26.04, and Playwright versions without 26.04 support.
The demand is documented:
- actions/runner-images#14748,
the announcement of the switch, has 241 reactions and 90 comments. The
mitigation it offers is to pin
ubuntu-24.04or to test againstubuntu-26.04. - Since the rollout began, projects have reported the same failures one by one: Python 3.8 and 3.9 in setup-python (BOSWatch/BW3-Core#163, konflux-ci/diffused#56), Playwright (DataBiosphere/data-browser#4996, cncf/maintainer-d#179, CieloVistaSoftware/wb-starter#1955) and old compilers from apt (graph-algorithms/edge-addition-planarity-suite#377). Each was found after CI failed.
- The previous switch to 24.04 had the same pattern: actions/runner-images#10636 (384 reactions, 168 comments) and tauri-apps/tauri#9662 (150 reactions, 93 comments).
- actionlint checks that runner labels are valid. lastgreen compares a failed run with the last green one, which helps after a failure.
How it works
ubuntu-latest-check parses
the workflow files and expands matrices with GitHub's include and exclude
rules, so each finding names the combinations it applies to. For every job on
ubuntu-latest it runs three checks:
- Every package named in
apt-get installorapt installinrun:steps, or inawalsh128/cache-apt-pkgs-action, is looked up in the Ubuntu archive indexes of both releases (release,-updatesand-securitypockets, all four components, includingProvides). A package that exists in 24.04 but not in 26.04 is reported. - Requested Python versions are compared with the actions/python-versions
manifest. On 26.04 there are no builds for 3.8 and 3.9, and only for patch
releases built after 26.04 support was added, so exact pins such as
3.12.3also fail. - For jobs that run
playwright install, the locked Playwright version is read frompackage-lock.json,pnpm-lock.yamloryarn.lock. Versions before 1.61.0 fail; 1.61.0 is the first release withubuntu26.04entries in its browser registry.
Jobs in a container: or on self-hosted runners are skipped. --repo and
--owner scan remote repositories through gh, --label ubuntu-24.04 also
checks jobs that were pinned, and the exit code is 1 when there is an error,
so the command can run as a CI step.
This is the output for the demo workflow in the repository:
$ ubuntu-latest-check examples/demo
examples/demo/.github/workflows/ci.yml:9: error: job test [os=ubuntu-latest, python-version=3.9]: python 3.9: actions/setup-python has no Python 3.9 build for Ubuntu 26.04 (it has one for 24.04)
examples/demo/.github/workflows/ci.yml:23: error: job test: apt gcc-10: apt package exists in Ubuntu 24.04 but not in 26.04
examples/demo/.github/workflows/ci.yml:23: error: job test: apt postgresql-client-16: apt package exists in Ubuntu 24.04 but not in 26.04
examples/demo/.github/workflows/ci.yml:34: error: job e2e: playwright 1.59.1: Playwright 1.59.1 (from web/package-lock.json) does not support Ubuntu 26.04, 'playwright install' fails there; 1.61.0 is the first release that does
examples/demo/.github/workflows/ci.yml:33: error: job e2e: apt libgdk-pixbuf2.0-0: apt package exists in Ubuntu 24.04 but not in 26.04
examples/demo/.github/workflows/ci.yml:33: error: job e2e: apt libx264-164: apt package exists in Ubuntu 24.04 but not in 26.04
1 workflow files, 2 jobs on ubuntu-latest, 6 errors, 0 warnings (Ubuntu 24.04 -> 26.04)
On the default branches of public repositories on 29 October 2026 it reported,
among others, lzma-dev in python/mypy, libatlas-base-dev in numpy/numpy and
Playwright 1.58.2 in CieloVistaSoftware/wb-starter. With
--label ubuntu-24.04 it reported the Playwright 1.57.0 lock in
cncf/maintainer-d that its issue describes.
Testing
The unit tests run against fixed package and version lists. They cover the
extraction of apt package names from shell scripts, matrix expansion, the
detection of runs-on and container, Python version matching, the three
lock file formats, apt sources that turn an error into a warning, skipped jobs
and invalid YAML. Two live tests use the real Ubuntu archive and the
python-versions manifest: the demo workflow must produce exactly the six
findings above, and the repository's own workflow must pass. CI runs all tests
on Python 3.9 and 3.13, on every push and once a week.
Limitations
- Only what is written in the workflow is checked. Packages installed by scripts, Makefiles or through shell variables are not seen.
- If a job adds an apt source, a missing package is reported as a warning. Whether a PPA builds for 26.04 is not checked.
- Only x64 is checked.
- Expressions other than
matrix.*references, literal strings and||are not evaluated. - Other changes in the 26.04 image (Ruby versions, the default Java version,
/tmpas tmpfs, newer compilers) are not checked. - Remote scans read only the default branch.
The code and installation instructions are at github.com/Jwrede/ubuntu-latest-check.