GitHub Actions: How I Cut Costs by 70%
💰 The Problem
My GitHub Actions bill was $200/month. For a personal project. That's insane.
I had to do something. So I analyzed my workflows, found the waste, and cut costs by 70%.
The result: From $200/month to $60/month. Same functionality. Better performance.
📊 Where the Money Was Going
I analyzed my GitHub Actions usage:
| Workflow | Minutes/Month | Cost |
|---|---|---|
| CI Tests (Linux) | 1,200 | $60 |
| CI Tests (Windows) | 800 | $80 |
| CI Tests (macOS) | 600 | $60 |
Total: 2,600 minutes/month = $200
❌ What Was Wasteful
1. Running Tests on Every Push
I was running full test suites on every push, even for documentation changes.
2. Testing on All Platforms
I was testing on Linux, Windows, and macOS for every change. Most of my code is platform-agnostic.
3. Long-Running Jobs
Some jobs took 20+ minutes when they should have taken 5.
4. No Caching
I wasn't caching dependencies, so every run downloaded everything from scratch.
✅ Optimizations That Worked
1. Conditional Workflows
Only run tests when code changes:
# .github/workflows/test.yml
on:
push:
paths:
- 'app/**'
- 'tests/**'
- 'composer.json'
pull_request:
paths:
- 'app/**'
- 'tests/**'
Impact: Reduced runs by 40%. Saved $80/month.
2. Matrix Strategy Optimization
Only test on Linux for most changes, full matrix for releases:
# Test on Linux only for PRs
strategy:
matrix:
os: [ubuntu-latest]
php: [8.2]
# Full matrix only for releases
if: github.event_name == 'release'
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
php: [8.1, 8.2, 8.3]
Impact: Reduced test runs by 60%. Saved $120/month.
3. Dependency Caching
Cache Composer and NPM dependencies:
- name: Cache Composer dependencies
uses: actions/cache@v3
with:
path: vendor
key: composer-${{ hashFiles('composer.lock') }}
- name: Cache NPM dependencies
uses: actions/cache@v3
with:
path: node_modules
key: npm-${{ hashFiles('package-lock.json') }}
Impact: Reduced job time by 50%. Saved $40/month.
4. Parallel Jobs
Run tests in parallel instead of sequentially:
jobs:
test-unit:
# Unit tests
test-integration:
# Integration tests
test-e2e:
# E2E tests
Impact: Faster feedback, same cost.
📊 Final Results
| Metric | Before | After | Savings |
|---|---|---|---|
| Minutes/Month | 2,600 | 780 | 70% |
| Cost/Month | $200 | $60 | $140 |
| Average Job Time | 12 min | 6 min | 50% faster |
💡 Key Optimizations
- Conditional workflows: Only run when needed
- Smart matrix strategy: Full testing only when necessary
- Dependency caching: Reuse dependencies between runs
- Parallel jobs: Run tests concurrently
- Job timeouts: Kill jobs that run too long
🎯 Key Takeaways
- GitHub Actions costs can add up quickly
- Most workflows are wasteful by default
- Simple optimizations can cut costs significantly
- Conditional workflows are your friend
- Caching is essential for cost savings
I went from $200/month to $60/month with these optimizations. The workflows are actually faster now, too. Win-win.