Copyright (c) 2026 MindMesh Academy. All rights reserved. This content is proprietary and may not be reproduced or distributed without permission.

3.2.3. Runner Software, Tooling, and Troubleshooting

💡 First Principle: A hosted runner image is a curated, versioned software bill of materials — knowing what's already there tells you which setup-* steps you can skip, and knowing that it changes over time tells you why a workflow that passed last month fails today.

GitHub publishes each image's full contents (the actions/runner-images repository, and a Set up job log section in every run linking to the exact image version). Ubuntu images ship with common language runtimes, Docker, build toolchains, cloud CLIs, and browsers. Two mechanisms let you take control of versions: the setup-* action family (setup-node, setup-python, setup-java, setup-go) installs and caches a specific version regardless of what's preinstalled, and the runner's tool cache holds several versions of common runtimes for instant switching. For anything not present, install at runtime — apt-get install, choco install, brew install, or a container image — accepting the per-job time cost.

Image mutability is the practical trap. Labels like ubuntu-latest migrate to a new major version on GitHub's schedule; a workflow that relied on a preinstalled tool version can break with no change to your code. Pinning runs-on: ubuntu-24.04 and pinning tool versions in setup-* steps is the reproducibility answer.

Two migrations are named in the objectives and worth carrying into the exam. ubuntu-20.04 was retired outright in April 2025 after a period of brownouts — a workflow that still names it fails to start, rather than quietly falling back to a newer image. And windows-latest moved from Windows Server 2022 to Windows Server 2025 across September 2025, so jobs depending on tooling that shipped only in the 2022 image began failing with no change to the workflow file; pinning runs-on: windows-2022 restores the old image and buys time to fix the real dependency. (windows-2019 was retired in June 2025.)

Self-hosted troubleshooting has its own checklist worth memorizing as symptom → cause. A runner showing offline means the service isn't running or lost egress to GitHub — check the service, the proxy settings, and the _diag log directory the runner writes locally. A runner stuck active with no progress usually means a hung process holding the job. Version mismatch matters because self-hosted runners auto-update by default; disabling auto-update requires you to update manually within GitHub's support window or jobs stop being assigned. And "runner exists but jobs queue" returns to 3.2.2: labels or group access.

⚠️ Exam Trap: setup-* actions do not merely select a preinstalled version — they download and cache the requested version when it isn't present. That's why pinning an old runtime works even on a brand-new image, and why "the image doesn't include Node 16 anymore" is not a blocker.

Reflection Question: Your workflow started failing after a runner image update, with no commit to your repository. Walk through the two log sources you'd read first, and name the two pinning changes that prevent a recurrence.

See how it connects
Alvin Varughese
Written byAlvin Varughese
Founder18 professional certifications