Navigating the End of an Era: Managing the Ubuntu 20.04 Deprecation in GitHub Actions

The landscape of Continuous Integration and Continuous Deployment (CI/CD) is undergoing a significant shift as GitHub formally deprecates the ubuntu-20.04 runner image. This deprecation is not merely a routine software update but a critical infrastructure change that threatens to break existing workflows across the developer community. The transition involves scheduled "brownout" periods designed to force migration, followed by a complete removal of the 20.04 image from the GitHub-hosted runner fleet. While the standard remediation involves updating workflow files to target ubuntu-22.04, ubuntu-24.04, or ubuntu-latest, the reality is far more nuanced. For projects relying on legacy software stacks, specific library versions, or older Python and Ansible releases, a simple version bump in YAML files can introduce new compatibility nightmares, particularly regarding GLIBC versions and cgroup management. Understanding the timeline, the technical implications of these brownouts, and the strategic alternatives for legacy support is essential for maintaining robust CI/CD pipelines.

The Deprecation Timeline and Brownout Strategy

GitHub has implemented a structured deprecation plan for the ubuntu-20.04 runner image, beginning with active warnings and progressing to enforced failures. The process is designed to raise awareness and compel maintainers to update their workflows before the final removal. The deprecation process officially commenced on February 1, 2025, with the full removal of the ubuntu-20.04 image scheduled for April 1, 2025. However, some reports indicate a slightly later hard stop date of April 15, 2025, highlighting the variability in how different parts of the ecosystem communicate these deadlines. Regardless of the exact final day, the immediate impact on developers comes in the form of "brownouts."

Brownouts are scheduled periods during which GitHub intentionally fails jobs that are configured to use ubuntu-20.04. These are not random outages but targeted disruptions intended to flag workflows that have not been updated. During these windows, developers will encounter specific error messages, such as This is a scheduled Ubuntu 20.04 brownout. Ubuntu 20.04 LTS runner will be removed on 2025-04-15 and The remote provider was unable to process the request. These failures serve as a critical alert mechanism, ensuring that repository maintainers are aware of the impending obsolescence of their CI environment.

The scheduled brownout windows are precise and recurring, occurring weekly leading up to the final removal. Developers must ensure their pipelines are resilient or updated outside these specific UTC time frames to avoid false alarms or actual build failures during critical release cycles.

  • March 4, 2025: 14:00 UTC – 22:00 UTC
  • March 11, 2025: 13:00 UTC – 21:00 UTC
  • March 18, 2025: 13:00 UTC – 21:00 UTC
  • March 25, 2025: 13:00 UTC – 21:00 UTC

Beyond these specific brownout dates, workflows relying on ubuntu-20.04 will also experience longer queue times as GitHub deprioritizes these deprecated images in favor of supported versions. The ultimate goal is a clean migration, but the enforcement mechanism is strict. Once the brownout period ends and the final removal date passes, any workflow explicitly calling for runs-on: ubuntu-20.04 will fail indefinitely until updated.

Standard Migration Pathways

For the majority of modern software projects, the migration from ubuntu-20.04 is straightforward. The primary action required is to update the runs-on directive in GitHub Actions workflow YAML files. Developers have three primary options for replacement images: ubuntu-22.04, ubuntu-24.04, or ubuntu-latest.

Using ubuntu-22.04 offers a balanced approach, providing a newer environment with updated tools and libraries while maintaining a degree of stability and predictability. This version is often the preferred choice for projects that need to ensure compatibility with a broader range of downstream users, particularly those running on Debian Bullseye or similar intermediate distributions. In contrast, ubuntu-24.04 represents the cutting edge of the Ubuntu LTS releases, offering the latest kernels, compilers, and development tools. However, this newer version introduces a significant risk: GLIBC compatibility. Software compiled on ubuntu-24.04 may rely on newer versions of the GNU C Library (GLIBC) that are not present in older operating systems, such as Debian Bookworm. This can result in binary incompatibilities where precompiled libraries or binaries fail to run on user systems with older base distributions.

The option ubuntu-latest provides an unpinned label that automatically resolves to the newest supported Ubuntu version. While this saves maintainers from having to manually update workflows in the future, it introduces a different kind of risk: unpredictability. When GitHub updates the ubuntu-latest image to a new major version, existing workflows may break if they are not thoroughly tested against the new baseline. This "set it and forget it" approach can lead to sudden pipeline failures during unexpected image updates, making it less suitable for projects with strict stability requirements.

Runner Image Pros Cons Best For
ubuntu-22.04 Stable, good compatibility with Debian Bullseye, updated tools May require manual updates in the future Projects needing broader binary compatibility
ubuntu-24.04 Latest tools, newest kernel, best performance GLIBC issues with older distros (e.g., Debian Bookworm) Modern projects with no legacy compatibility requirements
ubuntu-latest Automatic updates, no manual YAML changes needed Unpredictable breaks when GitHub updates the image Projects with extensive testing and flexible pipelines

The Legacy Support Dilemma

While the migration path seems clear for modern applications, it poses a severe challenge for projects that explicitly require the ubuntu-20.04 environment. In many cases, the use of ubuntu-20.04 is not arbitrary but a technical necessity. Newer Ubuntu runners often remove or alter support for older software versions, making it impossible to test legacy codebases using the standard GitHub-hosted runners.

A prominent example is the testing of older Python versions. The actions/setup-python action on ubuntu-22.04 and newer images has broken support for Python 3.6. For organizations that still need to verify compatibility with Python 3.6, the ubuntu-20.04 runner was the only viable option within the standard GitHub Actions ecosystem. Similarly, the Ansible community faces significant hurdles. Testing against older Ansible versions, such as Ansible 2.9, ansible-base 2.10, and ansible-core 2.11, requires an environment that mimics the behavior of older operating systems. The ubuntu-20.04 runner provides the necessary underlying system libraries and init system behaviors that these older Ansible versions expect. Moving to ubuntu-latest or newer images breaks ansible-test --docker for many stable branches, rendering the CI pipeline useless for maintaining backward compatibility.

Furthermore, the transition to newer Ubuntu versions introduces complications with cgroup management. Ubuntu 22.04 and 24.04 utilize cgroups v2 by default, whereas older systems may rely on cgroups v1. Tools like ansible-core only introduced full support for cgroups v2 in version 2.12 and later. This means that testing older versions of Ansible on newer Ubuntu runners can lead to unexpected failures related to resource management and container isolation.

Strategic Alternatives for Legacy Workflows

For projects that cannot simply migrate to ubuntu-22.04 or ubuntu-24.04 due to legacy requirements, several strategic alternatives exist. The most robust solution is to abandon the GitHub-hosted runner labels entirely and utilize self-hosted runners. By deploying a self-hosted runner on a machine or virtual machine running Ubuntu 20.04, organizations can maintain precise control over their CI environment. This approach bypasses the brownouts and deprecation schedule, allowing continued testing of legacy Python and Ansible versions. Since Ubuntu 20.04 has standard support until April 2025, and professional (paid) support available until April 2030, self-hosted runners remain a viable long-term strategy for enterprises with strict compliance or compatibility needs.

Another effective approach is to use Docker containers within GitHub Actions. Instead of relying on the base OS image for the runner, workflows can specify a Docker container for the job execution. This isolates the build environment from the host runner's OS version. By defining a specific Docker image in the workflow, developers can create a reproducible environment that includes the exact versions of Python, GLIBC, or other dependencies required. This method decouples the build environment from the runner image, allowing the use of modern GitHub runners (like ubuntu-22.04) while executing jobs inside a container that mimics ubuntu-20.04.

For developers working with precompiled libraries, such as rustler-precompiled, the choice of runner image directly impacts the binary compatibility of the output. Compiling on ubuntu-24.04 may produce binaries that are incompatible with older distributions like Debian Bookworm due to GLIBC version differences. In such cases, switching the build runner to ubuntu-22.04 often resolves the issue, as it provides a middle ground that is compatible with a wider range of downstream Linux distributions. If even ubuntu-22.04 proves too new, the Docker container approach remains the definitive solution, allowing the build to occur in a precisely controlled legacy environment regardless of the underlying GitHub runner.

Conclusion

The deprecation of the ubuntu-20.04 runner in GitHub Actions represents a pivotal moment for CI/CD infrastructure. While the migration to ubuntu-22.04 or ubuntu-24.04 is the standard and recommended path for most projects, it is not a universal fix. The scheduled brownouts and eventual removal of the 20.04 image expose the hidden dependencies many legacy projects have on older system libraries and software versions. For modern applications, the upgrade brings access to newer tools and improved performance, albeit with the need to manage potential GLIBC compatibility issues. For legacy projects, particularly those maintaining older Python or Ansible versions, the deprecation necessitates a more sophisticated approach. Adopting self-hosted runners or leveraging Docker containers for job execution provides the necessary insulation from the underlying runner changes, ensuring that testing environments remain stable and compliant. Ultimately, the success of this transition depends on a thorough audit of workflow dependencies and a strategic choice between standard migration and isolated legacy support.

Sources

  1. youki-dev/youki #3095
  2. checkstyle/checkstyle #16793
  3. wazuh/wazuh #28282
  4. GitHub Actions still using ubuntu-20.04
  5. GitHub runners for Ubuntu 20.04 have been removed
  6. actions-runner-controller actions-runner.ubuntu-20.04.dockerfile

Related Posts