Recent development · Practical technical guide · 18 October 2026
From 19 October 2026 the ubuntu-latest label begins resolving to Ubuntu 26.04, finishing by 19 November. The verified image differences, including Java 25 as default, Node 24, Helm 4 and Docker 29, the setup-python gap for old interpreters, and a pin, test and migrate procedure.
What was announced
Issue 14748 in the actions/runner-images repository, opened on 17 September 2026 and pinned, announces that the ubuntu-latest label is ready to become the default for Ubuntu 26.04. The rollout runs over several weeks beginning 19 October 2026 and is planned to complete by 19 November 2026. The stated motivation is that Ubuntu 26.04 finished its public preview and is generally available for production workloads. Both GitHub Actions and Azure DevOps are affected. The mitigations offered are the usual three: file an issue, switch back explicitly with the ubuntu-24.04 label, or test early with ubuntu-26.04. Since the announcement, every job that uses ubuntu-latest has carried an annotation pointing at the issue, which is how most teams noticed.
The GitHub-hosted runners reference already lists ubuntu-latest, ubuntu-24.04 and ubuntu-26.04 as labels for the same runner sizes, and ubuntu-24.04-arm and ubuntu-26.04-arm for the Arm variants, so pinning either way is supported today.
What actually differs on the image
The issue carries a short comparison table, but the authoritative list is the README published for each image version. The two READMEs for image version 20261012, read on 18 October 2026, differ in more places than the issue suggests, and in one place they contradict it: the issue table says Java 17 remains the default on both images, while the 26.04 README lists Java 25.0.4 as the default and drops Java 8 entirely. Trust the README of the image version your job actually runs, which the job log prints.
| Component | Ubuntu 24.04 image | Ubuntu 26.04 image |
|---|---|---|
| Operating system and kernel | 24.04.5 LTS, 6.17 azure kernel | 26.04.1 LTS, 7.0 azure kernel |
| systemd | 255 | 259 |
| Java default (JAVA_HOME) | 17.0.20; also 8, 11, 21, 25 | 25.0.4; also 11, 17, 21; Java 8 removed |
| Node.js system default | 22.23.3, npm 10 | 24.21.0, npm 11 |
| Python system default | 3.12.3, pip 24 | 3.14.4, pip 25 |
| Helm | 3.22.0 | 4.3.0 |
| Docker client and server, Compose | 28.0.4, Compose 2.38 | 29.4.2, Compose 5.1 |
| PostgreSQL and MySQL services | 16.15 and 8.0 | 18.6 and 8.4 |
| GCC, Clang, CMake | 12 to 14; 16 to 18; 3.31 | 13 to 15; 20 to 22; 4.4 |
| OpenSSL, PHP, Ruby, Perl | 3.0.13; 8.3; 3.2.3; 5.38 | 3.5.5; 8.5; 3.3.8; 5.40 |
| Removed from the image | Fastlane, Julia, Lerna, Mercurial, Miniconda, Newman, Parcel, Pulumi, MediaInfo, Haveged, Sphinx | |
| Cached toolchains (unchanged) | Go 1.24 to 1.26, Node 22 and 24, Python 3.10 to 3.14, Ruby 3.2 to 4.0 | Same list |
The cached toolchains being identical is the good news: jobs that use setup-node, setup-go or setup-python with a version that exists in the cache behave the same. The breakage comes from everything that relied on the system default or on a package that was removed.
The setup-python gap, verified
setup-python resolves a requested version first from the image tool cache and then from the versions manifest published by actions/python-versions, which lists prebuilt interpreters per Ubuntu release. Parsing that manifest on 18 October 2026 gives a clear picture. For Ubuntu 24.04 there are 140 builds, including Python 3.8 and 3.9. For Ubuntu 26.04 there are 22: 3.10.20 to 3.10.22, 3.11.15 to 3.11.17, 3.12.13 to 3.12.15, 3.13.13 to 3.13.16, 3.14.6 to 3.14.8 and 3.15.0 with its release candidates. There is no 3.8 or 3.9 build for 26.04 at all, and for each supported minor only the newest three or four patch releases exist.
The consequences follow directly. A workflow that requests python-version 3.9, an exact older patch such as 3.12.4, or a .python-version file holding 3.11.9 will fail at the setup step when the job lands on 26.04. A commenter on the issue reported the same result from container tests on 12 October; the manifest confirms it independently. The options are to pin those jobs to ubuntu-24.04, to run them in a container image that ships the interpreter, or to drop interpreters that are past their upstream end of life, which 3.8 and 3.9 are.
Illustrative Python that reproduces the check used for this article; run it with the versions your workflows request.
import json, urllib.request, collections
MANIFEST = "https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json"
def builds_per_ubuntu():
data = json.load(urllib.request.urlopen(MANIFEST, timeout=60))
by_release = collections.defaultdict(set)
for entry in data:
for f in entry.get("files", []):
if f.get("platform") == "linux":
by_release[f.get("platform_version")].add(entry["version"])
return by_release
def missing_on(release, wanted, by_release):
"""Versions your workflows request that have no build for the given Ubuntu release."""
have = by_release.get(release, set())
return sorted(v for v in wanted if not any(h == v or h.startswith(v + ".") for h in have))Other breakages to expect
- Java default moves to 25. Jobs that call java or mvn without setup-java compile with a different release; set the version explicitly with setup-java or JAVA_HOME_17_X64.
- Helm 4. A major version with changed behaviour; pin Helm with a setup action or install the version your charts were tested with.
- Docker Compose 5 and Docker 29. Test compose files and any scripts that parse docker output; pin with setup-docker actions where the version matters.
- PostgreSQL 18 and MySQL 8.4 services. Integration tests that start the on-image database now run against new majors; prefer service containers with an explicit image tag.
- Node 24 as the system node. Scripts that run node without setup-node change runtime; add setup-node with the version from your engines field.
- Removed tools. Pulumi, Mercurial, Miniconda and the others in the table must be installed by the job or replaced.
- Compiler majors. GCC 15 and Clang 22 add warnings and tighten defaults; builds with warnings as errors may break.
A pin, test and migrate procedure
Illustrative; keep the version-printing step, because it turns a silent label change into a visible line in every job log.
# 1. Inventory: every workflow that depends on the moving label.
grep -rn "runs-on:.*ubuntu-latest" .github/workflows/
# 2. Add a job on the new image now, next to the existing one, so the diff is visible in CI.
# (in a workflow file)
jobs:
test:
strategy:
fail-fast: false
matrix:
runner: [ubuntu-24.04, ubuntu-26.04]
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v4
- name: Record what this image really is
run: |
cat /etc/os-release | head -2
java -version 2>&1 | head -1; node -v; python3 -V; helm version --short; docker version --format '{{.Server.Version}}'
- uses: actions/setup-python@v5
with: {python-version: "3.12"} # a minor, not an exact old patch
- run: make test- Pin pipelines that must not change during the window to ubuntu-24.04 explicitly. Treat the pin as temporary and record the removal date.
- Run the matrix job for a week and fix every difference: toolchain versions, removed tools, compiler warnings, database majors.
- Switch the default to ubuntu-26.04 explicitly rather than waiting for the label, so the change happens on a day you chose.
- Remove the 24.04 pins before 19 November, except for jobs that need interpreters the new manifest does not provide, and give those jobs a container.
- Subscribe to the pinned issue; the image READMEs change with every image version and the migration issue is where regressions are reported.
What this article did not verify
The rollout had not begun when this was written, so the exact day a given repository flips is unknown; the issue says several weeks from 19 October. The comparison table reflects image version 20261012 and will drift as weekly images ship. Behaviour differences inside tools such as Helm 4 or Compose 5 were not tested here; the recommendation is to pin and test rather than to assume. And the Java default contradiction between the issue table and the README is reported as found; if it matters to you, the README of the image your job printed is the fact.
Practice and related reading
Further reading:
Sources and scope
Checked against these sources on 18 October 2026. Event: actions/runner-images issue 14748: ubuntu-latest label migrates to Ubuntu 26.04, rollout 19 October to 19 November 2026 (announced 17 September 2026), 19 October 2026; recheck it before acting. Examples and figures are illustrative, not measurements.
- actions/runner-images issue 14748: ubuntu-latest label will use Ubuntu 26.04 (opened 2026-09-17; rollout from 2026-10-19)
- actions/runner-images: Ubuntu 26.04 image README (installed software)
- actions/runner-images: Ubuntu 24.04 image README (installed software)
- actions/python-versions: versions-manifest.json (builds available to setup-python)
- GitHub Docs: GitHub-hosted runners reference and workflow labels