> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Actions Cost Optimization

> Strategies to reduce GitHub Actions spend by gating macOS and Windows jobs, using Linux runners for cross platform tasks, and considering self hosted runners

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."](https://mintcdn.com/kodekloud-c4ac6d9a/IdpF6mT8Tun72cng/images/GitHub-Administration-Certification/GitHub-Actions-and-Runners/Actions-Cost-Optimization/macos-costs-github-actions-invoice.jpg?fit=max\&auto=format\&n=IdpF6mT8Tun72cng\&q=85\&s=425359492e0154090dcb740ae3e9d7eb)

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:

```yaml theme={null}
# .github/workflows/ios-ci.yml
on:
  push:
  pull_request:

jobs:
  linux-tests:
    name: "Linux unit & lint"
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Install dependencies
        run: npm ci
      - name: Run unit tests
        run: npm test

  macos-tests:
    name: "macOS integration & iOS tests"
    runs-on: macos-latest
    # Only run on pull requests targeting the main branch
    if: github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'main'
    steps:
      - uses: actions/checkout@v4
      - name: Install CocoaPods
        run: pod install
      - name: Run iOS integration tests
        run: xcodebuild test -workspace MyApp.xcworkspace -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 14'
```

2. 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.](https://mintcdn.com/kodekloud-c4ac6d9a/IdpF6mT8Tun72cng/images/GitHub-Administration-Certification/GitHub-Actions-and-Runners/Actions-Cost-Optimization/windows-runners-every-commit-progress.jpg?fit=max\&auto=format\&n=IdpF6mT8Tun72cng\&q=85\&s=a4f6ae17407781131cd1f3dbd84d65e8)

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."](https://mintcdn.com/kodekloud-c4ac6d9a/IdpF6mT8Tun72cng/images/GitHub-Administration-Certification/GitHub-Actions-and-Runners/Actions-Cost-Optimization/reducing-windows-runner-costs-linux-baseline.jpg?fit=max\&auto=format\&n=IdpF6mT8Tun72cng\&q=85\&s=e41915c27e364954a242fdd9698cd21d)

2. 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:

```yaml theme={null}
jobs:
  windows-build:
    name: "Windows build & tests"
    runs-on: windows-latest
    # Only run on the main branch
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4
      - name: Setup .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: '8.0.x'
      - name: Build and test
        run: dotnet test
```

![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.](https://mintcdn.com/kodekloud-c4ac6d9a/IdpF6mT8Tun72cng/images/GitHub-Administration-Certification/GitHub-Actions-and-Runners/Actions-Cost-Optimization/reducing-windows-runner-costs-main-branch.jpg?fit=max\&auto=format\&n=IdpF6mT8Tun72cng\&q=85\&s=91526b698dfb1cbb6f37ef2f4b925077)

## Quick reference: costs and actions

| Runner type | Typical GitHub-hosted multiplier | Recommended action |
| - | -: | - |
| macOS | \~10x Linux | Gate jobs to PRs/`main`; consider self-hosted macOS for heavy usage |
| Windows | \~2x Linux | Move cross-platform work to Linux; gate Windows-only jobs to `main` |
| Linux | Baseline | Default runner for cross-platform work and fast feedback |

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`.

## Links and further reading

* GitHub Actions: [https://docs.github.com/actions](https://docs.github.com/actions)
* Self-hosted runners: [https://docs.github.com/actions/hosting-your-own-runners](https://docs.github.com/actions/hosting-your-own-runners)
* Workflow syntax & `if` expressions: [https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions](https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions)

> [card] [Watch Video](https://learn.kodekloud.com/user/courses/github-administration-certification/module/3ba840c5-7afb-4da9-8395-a430a6354557/lesson/2bc37fd2-3093-4507-a4f2-cd2f3700f12e)

> [card] [Practice Lab](https://learn.kodekloud.com/user/courses/github-administration-certification/module/3ba840c5-7afb-4da9-8395-a430a6354557/lesson/5bbc67e7-699b-48c5-85e4-391ef73bab1d)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.