Skip to main content
Two common, high-impact scenarios arise for GitHub administrators responsible for controlling GitHub Actions spend. These changes move you from being a mere setup technician to a strategic cost partner for your organization.

Scenario 1 — macOS cost problem

The iOS team runs a workflow on every push and pull request. While many steps are cheap on Linux runners, the core iOS integration tests must run on macOS. GitHub-hosted macOS runners carry a much higher per-minute cost (roughly 10x a Linux runner), and frequent commits on personal feature branches can rapidly inflate the Actions invoice.
A slide titled "The macOS Cost Problem" showing developers pushing commits to feature branches that trigger GitHub Actions. On the right is a chart of increasing "Actions Minutes Usage" labeled "Invoice: Skyrocketing."
Recommended changes (two-pronged):
  1. Gate expensive macOS jobs so they only run when necessary
    • Keep fast, cross-platform checks (lint, unit tests) running on every push to give developers quick feedback.
    • Restrict the macOS job to run only for pull requests targeting main (or only on main itself), using a job-level if: condition.
Example workflow using this pattern:
  1. Consider self-hosted macOS runners for heavy, repeatable workloads
    • Migrate frequent macOS work to self-hosted machines (on-prem Mac minis or cloud macOS instances) to eliminate per-minute GitHub billing for those jobs.
    • Self-hosted runners can dramatically flatten your Actions bill for macOS-heavy teams, but they introduce operational responsibilities.
Self-hosted runners reduce GitHub billing but increase your operational responsibility: OS updates, security hardening, monitoring, and capacity planning become your team’s responsibility.

Scenario 2 — Windows runner costs

Windows runners are more expensive than Linux runners (roughly 2x per minute) and can be another unnecessary cost driver if Windows jobs run on every branch and commit.
A slide titled "Scenario 2: Windows Runners" showing a one-workflow diagram with buttons labeled "Every branch" and "Every commit." Below it is a Windows logo next to a partially filled progress bar.
Two-step recommendation:
  1. Audit and move cross-platform steps to Linux
    • If the tasks are language/runtime-agnostic (Node.js, Python, standard Docker containers, etc.), run them on ubuntu-latest instead of Windows. Linux runners are the cost baseline and usually faster to provision.
A presentation slide titled "Reducing Windows Runner Costs" with the step "Adjust the Steps." It shows a cross-platform list (Node.js, Python, Docker) pointing to a single Linux runner (Tux) and a note "Linux is always the baseline cost."
  1. Gate Windows-only jobs to main or PRs targeting main
    • For steps that truly require Windows (e.g., .NET Framework-only tests, Windows-specific tooling), restrict their execution using an if: condition so they do not run on every feature-branch push.
Example job-level gating:
A presentation slide titled "Reducing Windows Runner Costs" showing step 2: "Restrict the Execution Strictly to the Main Branch" with a UI card that allows Windows-only steps only on the main branch.

Quick reference: costs and actions

Best-practice checklist:
  • Run cheap, cross-platform steps on ubuntu-latest.
  • Use on: triggers and job-level if: conditions (github.event_name, github.ref, github.event.pull_request.base.ref) to limit expensive jobs to relevant branches or PRs.
  • Prefer fast feedback for developers by keeping lint/unit checks on every push and gating slow/integration tests.
  • Evaluate self-hosted runners when usage is predictable and heavy; plan for maintenance and security.
Small workflow changes—branch filters, workflow triggers, and job-level if: conditions—often yield large savings. Regularly audit workflows to move cross-platform work to Linux and to gate expensive OS-specific jobs to PRs or main.

Watch Video

Practice Lab