Optimizing Workflows
This guide provides practical strategies to reduce your GitHub Actions costs without sacrificing CI/CD quality.
Quick Wins
1. Right-Size Runners
Many jobs use larger runners than needed:
# Before: 8-core runner for simple tests
runs-on: ubuntu-latest-8-core # $0.032/min
# After: Default 2-core is often enough
runs-on: ubuntu-latest # $0.008/min
Savings: 75% on runner costs
Check if your jobs actually need larger runners by:
- Reviewing CPU/memory usage
- Testing on smaller runners
- Measuring build time difference
2. Cache Dependencies
Avoid downloading packages every run:
- uses: actions/cache@v4
with:
path: ~/.npm
key: npm-${{ hashFiles('package-lock.json') }}
restore-keys: npm-
Savings: 2-5 minutes per run
3. Skip Unnecessary Runs
Don't run CI when it's not needed:
on:
push:
paths-ignore:
- 'docs/**'
- '*.md'
- '.gitignore'
Savings: Entire runs for doc-only changes
4. Cancel Redundant Runs
When you push again, cancel the previous run:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Savings: Prevents paying for superseded builds
Optimize Matrix Builds
Reduce Matrix Size
Only test combinations that matter:
# Before: Full matrix (9 jobs)
strategy:
matrix:
os: [ubuntu, windows, macos]
node: [16, 18, 20]
# After: Smart selection (5 jobs)
strategy:
matrix:
os: [ubuntu]
node: [16, 18, 20]
include:
- os: windows
node: 18
- os: macos
node: 18
Savings: 44% fewer matrix jobs
Use Fail-Fast
Stop the entire matrix when one fails:
strategy:
fail-fast: true
matrix:
os: [ubuntu, windows, macos]
Savings: Avoid running all jobs when there's a known failure
Optimize Job Duration
Parallelize Tests
Run tests in parallel to reduce wall time:
- run: npm test -- --parallel --maxWorkers=4
Split Large Jobs
Break monolithic jobs into parallel smaller ones:
jobs:
test-unit:
runs-on: ubuntu-latest
steps:
- run: npm run test:unit
test-integration:
runs-on: ubuntu-latest
steps:
- run: npm run test:integration
Use Artifacts Strategically
Build once, use many times:
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: build
path: dist/
test:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: build
- run: npm run test
Reduce OS Costs
Minimize macOS Usage
macOS is 10x the cost of Linux:
# Only use macOS when necessary
jobs:
test:
runs-on: ubuntu-latest # Most tests
test-ios:
runs-on: macos-latest # Only iOS-specific
Minimize Windows Usage
Windows is 2x the cost of Linux:
# Use Linux with Docker for Windows testing
jobs:
test:
runs-on: ubuntu-latest
container: mcr.microsoft.com/windows/servercore
Schedule Optimization
Reduce Schedule Frequency
Run scheduled jobs less often:
# Before: Every hour
on:
schedule:
- cron: '0 * * * *'
# After: Every 6 hours
on:
schedule:
- cron: '0 */6 * * *'
Savings: 83% reduction in scheduled runs
Use Conditions
Only run expensive steps when needed:
- name: Deploy
if: github.ref == 'refs/heads/main'
run: ./deploy.sh
Workflow Organization
Combine Small Workflows
Merge workflows that always run together:
# Before: 3 separate workflows = 3× job startup overhead
# After: 1 workflow with 3 jobs
jobs:
lint:
...
test:
...
build:
...
Savings: Reduced job startup overhead
Use Reusable Workflows
Avoid duplicating workflow logic:
jobs:
call-workflow:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: 18
Measure Optimization Impact
Before/After Comparison
Use CICosts to measure:
- Note current metrics
- Implement optimization
- Wait for data (1-2 weeks)
- Compare in CICosts
Key Metrics to Track
| Metric | Target |
|---|---|
| Cost per run | Decrease |
| Duration | Decrease |
| Success rate | Maintain or improve |
| Cost per PR | Decrease |
Optimization Checklist
Run through this checklist for each expensive workflow:
- Is the runner size appropriate?
- Are dependencies cached?
- Can we skip runs for non-code changes?
- Can we reduce matrix size?
- Are there redundant steps?
- Can jobs run in parallel?
- Is macOS/Windows usage minimized?
- Are scheduled jobs necessary at current frequency?
Next: Setting Budgets →