GitHub-Hosted Runners, Images, Labels, Hardware, and Runtime Behavior: Guided Hands-On Workflow
Now you will build a controlled cross-OS probe. The workflow compares versioned Ubuntu, Windows and macOS labels, records image and tool metadata using each platform's native shell, and then installs an explicitly selected Python version with an immutable official action reference. The purpose is not to create a giant matrix; it is to prove which execution inputs differ and which you can control.
Learning objectives
- Run a small OS matrix with explicit versioned labels and platform-appropriate shell commands.
- Record safe runner/image metadata and selected preinstalled tool versions.
-
Use an immutable
actions/setup-pythonreference to make the Python toolchain explicit. -
Compare a
-latestlabel with a versioned label without treating either as a frozen image release. - Create a compact reproducibility manifest containing source/run/image/tool evidence.
1. Preflight and cost boundary
Use a learner-owned disposable repository. A public repository is the simplest free-compatible path for standard hosted runners; private repositories consume included/billable Actions minutes according to the account plan. The mandatory learning works with Ubuntu and Windows. macOS is optional if you want a third OS comparison.
The probe records environment identity and portability behavior. It is not a statistically controlled hardware benchmark and must not be used to rank operating systems or providers.
2. Versioned OS matrix
name: chapter08-hosted-runner-probe
on: workflow_dispatch
permissions: {}
jobs:
probe:
strategy:
fail-fast: false
matrix:
os: [ubuntu-24.04, windows-2025, macos-26]
runs-on: ${{ matrix.os }}
steps:
- name: Common context evidence
shell: bash
if: runner.os != 'Windows'
run: |
set -euo pipefail
printf 'label=%s
' '${{ matrix.os }}'
printf 'runner_os=%s
' '${{ runner.os }}'
printf 'runner_arch=%s
' '${{ runner.arch }}'
printf 'image_os=%s
' "${ImageOS:-unknown}"
printf 'image_version=%s
' "${ImageVersion:-unknown}"
printf 'run_id=%s
' "$GITHUB_RUN_ID"
printf 'attempt=%s
' "$GITHUB_RUN_ATTEMPT"
printf 'sha=%s
' "$GITHUB_SHA"
- name: Windows context evidence
if: runner.os == 'Windows'
shell: pwsh
run: |
"label=${{ matrix.os }}"
"runner_os=${{ runner.os }}"
"runner_arch=${{ runner.arch }}"
"image_os=$env:ImageOS"
"image_version=$env:ImageVersion"
"run_id=$env:GITHUB_RUN_ID"
"attempt=$env:GITHUB_RUN_ATTEMPT"
"sha=$env:GITHUB_SHA"
The platform split is intentional. Cross-platform automation often fails because authors assume Bash, GNU utilities, path separators, and quoting rules are universal.
3. Inspect preinstalled tools without depending on them
Add a step that records a few common versions:
- name: Preinstalled tool snapshot
shell: pwsh
run: |
git --version
python --version
node --version
"workspace=$env:GITHUB_WORKSPACE"
"temp=$env:RUNNER_TEMP"
PowerShell Core is available across the chosen hosted images, so this compact snapshot is convenient. The values are observations, not a promise that your project can rely on those versions next week.
4. Make Python an explicit dependency
The official setup action is executable code. Pin the exact reviewed commit and record the human-readable release in a comment:
- name: Set up Python 3.13
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.13'
check-latest: false
- name: Verify selected toolchain
shell: pwsh
run: |
python --version
python -c "import platform; print(platform.python_implementation(), platform.machine())"
The OS image may still change, but the workflow now expresses which Python line it requires. For even stricter reproducibility, pin a patch version that your support policy has tested.
5. Compare latest to a versioned label safely
Create a separate two-cell diagnostic matrix, not a production gate:
label_compare:
strategy:
matrix:
os: [ubuntu-latest, ubuntu-24.04]
runs-on: ${{ matrix.os }}
steps:
- shell: bash
run: |
set -euo pipefail
printf 'requested=%s
image_os=%s
image_version=%s
' \
'${{ matrix.os }}' "${ImageOS:-unknown}" "${ImageVersion:-unknown}"
Today both labels map to Ubuntu 24.04, but they communicate
different intent. ubuntu-latest opts into a future
migration; ubuntu-24.04 opts out of the OS-generation
migration while that image remains supported.
6. Create a non-sensitive reproducibility manifest
Do not upload the whole environment. Produce a small text or JSON file containing only the inputs needed for diagnosis:
$manifest = [ordered]@{
run_id = $env:GITHUB_RUN_ID
run_attempt = $env:GITHUB_RUN_ATTEMPT
source_sha = $env:GITHUB_SHA
runner_os = '${{ runner.os }}'
runner_arch = '${{ runner.arch }}'
requested_os = '${{ matrix.os }}'
image_os = $env:ImageOS
image_version = $env:ImageVersion
python = (python --version 2>&1 | Out-String).Trim()
git = (git --version | Out-String).Trim()
}
$manifest | ConvertTo-Json | Set-Content "$env:RUNNER_TEMP/runner-manifest.json"
Get-Content "$env:RUNNER_TEMP/runner-manifest.json"
Chapter 13 will teach retained artifact transport. In this chapter, the manifest is inspected in the job log/local temp area so the runner concept remains isolated.
7. Challenge: choose the correct control
A project passed yesterday on ubuntu-latest but fails
today because the preinstalled Python minor changed. Which
correction controls the actual dependency?
Answer after investigation: changing to
ubuntu-24.04 may avoid a future OS-generation
migration, but an explicit setup step is the direct control for
Python. Record both the image release and the selected Python
version.
Knowledge check
Why use versioned OS labels in the mandatory probe?
They hold the OS generation stable while still allowing us to observe normal image-release updates.
What does pinning setup-python accomplish that
ubuntu-24.04 does not?
It makes the Python dependency explicit instead of inheriting whatever Python version happens to be preinstalled on the current image release.
Why is the macOS cell optional?
The core learning only needs two OSes, and plan/minute economics vary; macOS adds useful portability evidence but is not required.
Should the manifest contain the entire process environment?
No. Record only non-sensitive runtime identity and tool versions; broad environment dumps become dangerous once credentials exist.
If ubuntu-latest and
ubuntu-24.04 resolve identically today, are they
semantically identical?
No. The latest label opts into GitHub’s future stable-image migration, while the versioned label fixes the OS generation until that label is retired.
Official references and version notes
- GitHub-hosted runners reference — current standard/larger runner behavior, filesystem, networking, IP and privilege details.
-
Choosing the runner for a job
— current
runs-onlabels, public/private standard runner specifications and architecture availability. - Using GitHub-hosted runners — current fresh-instance execution model and runner selection.
- GitHub Actions runner images — authoritative current image-label mappings, image releases, weekly update cadence and included-software manifests.
- Ubuntu 24.04 software manifest — current Ubuntu 24.04 image software inventory; verify the run-specific image release rather than assuming this file is frozen.
- Windows Server 2025 software manifest — current Windows Server 2025 image software inventory.
- macOS 26 software manifest — current macOS 26 Arm64 hosted-image inventory.
-
Runner context
— current
runner.os,runner.arch,runner.temp,runner.tool_cacheand related context fields. - Default environment variables — current workspace, temp, repository, run and runner-related default environment state.
- Larger runners concept — current larger-runner plan boundaries and features such as additional resources, groups, autoscaling, GPU and networking options.
- Larger runners reference — current larger-runner hardware/network restrictions, static-IP and macOS caveats.
- setup-python releases — current official action release history; Chapter 08 uses an immutable SHA corresponding to v7.0.0.
Version-sensitive runner behavior was rechecked against current
primary GitHub documentation on 2026-09-09. At
verification time, ubuntu-latest maps to Ubuntu 24.04
x64, windows-latest to Windows Server 2025 x64, and
macos-latest to macOS 26 Arm64; Ubuntu 26.04 and
selected Arm64 images are available in preview. GitHub states that
runner-image software is typically updated weekly and that
-latest migrations are gradual, so a successful run
must record the actual image version/toolchain rather than
treating a label as a frozen machine. For standard hosted runners,
every normal job receives a fresh hosted instance; steps inside
one job share that instance, while separate jobs do not share its
filesystem. The ubuntu-slim single-CPU option is a
special container-on-shared-VM case and is not used in mandatory
labs. Current public-repository standard Linux/Windows x64 runners
provide 4 CPU/16 GB RAM/14 GB SSD, while private-repository
standard Linux/Windows x64 runners provide 2 CPU/8 GB RAM/14 GB
SSD; macOS and Arm64 classes have different specifications.
Hardware figures are therefore plan/repository/image inputs, not
universal constants. Larger runners are optional
organization/enterprise features and are not required for course
completion. Official actions/setup-python v7.0.0 is
pinned in examples to
5fda3b95a4ea91299a34e894583c3862153e4b97. The guided
matrix uses versioned GA labels and an optional macOS cell;
preview images are discussed but not required.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.