How We Reduced Our Open Source Project’s GitHub Actions Bill by 70% (Without Sacrificing Speed)
Let me be blunt: our open source project was bleeding money on CI/CD.
We had a 10K+ star repo with 40+ contributors. Every PR triggered a full matrix build across 6 OSes, 4 Python versions, and 3 Node versions. It was overkill. But we were afraid to touch it. “If it ain’t broke, don’t fix it,” right?
How We Helped an EdTech Startup Survive a 10x Traffic Spike Without Burning Cash
How We Helped an EdTech Startup Survive a 10x Traffic Spike Without Burning Cash It’s a Thursday afternoon.… ...
Wrong.
Our monthly GitHub Actions bill hit $800. That’s not sustainable for a project with zero revenue. We needed to cut costs—fast.
Build a Custom AI-Powered Lint Fixer with Python and GitHub Actions: A Step-by-Step Developer Tutorial
Build a Custom AI-Powered Lint Fixer with Python and GitHub Actions: A Step-by-Step Developer Tutorial You’ve seen the… ...
I brought in a team from ECOA AI in Can Tho, Vietnam. They’re not just cheap labor; they’re engineers who live and breathe CI/CD optimization. Together, we slashed the bill to $240/month—a 70% reduction—without increasing merge times.
Here’s the exact playbook.
The Problem: Bloated Matrix Builds
Our workflow looked like this:
yaml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ['3.9', '3.10', '3.11', '3.12']
node-version: ['16', '18', '20']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- run: pip install -e .[dev]
- run: pytest
That’s 36 parallel jobs per push. Most of them tested the same code in slightly different environments. We didn’t need all combinations—just the critical ones.
Step 1: Identify Redundant Combinations
The Vietnamese team analyzed our test failure history. Here’s what they found:
- Python 3.9 and 3.10 had identical failure rates on Ubuntu and macOS.
- Windows builds accounted for only 2% of our user base but 25% of our CI minutes.
- Node 16 was EOL and irrelevant.
We cut the matrix to:
| OS | Python | Node | When to run |
|---|---|---|---|
| ubuntu-latest | 3.11, 3.12 | 20 | Always |
| macos-latest | 3.11 | 20 | Only on PRs with macOS-specific changes |
| windows-latest | 3.11 | 20 | Only on scheduled weekly runs |
Result: 12 jobs instead of 36. Cost dropped by 60% immediately.
Step 2: Cache Everything That Moves
Dependency installation was eating 40% of our build time. We added caching for pip and npm:
yaml
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
This cut install time from 3 minutes to 15 seconds. Combined with the reduced matrix, our average build time went from 12 minutes to 4.2 minutes.
Step 3: Use Self-Hosted Runners for Heavy Jobs
The biggest savings came from moving CPU-intensive integration tests to self-hosted runners. We set up two cheap VMs in Ho Chi Minh City (cost: $40/month total) and configured GitHub Actions to route specific jobs there.
yaml
jobs:
integration-tests:
runs-on: [self-hosted, linux, x64]
steps:
- run: docker-compose up -d
- run: pytest tests/integration/
Self-hosted runners cost $0 per minute. That single change saved us $180/month in runner minutes.
Step 4: Conditional Triggers
We stopped running CI on pushes to `docs/` and `*.md` files. Simple `paths-ignore` saved 5% more.
yaml
on:
push:
paths-ignore:
- 'docs/**'
- '*.md'
The Final Result
| Metric | Before | After | Improvement |
|---|---|---|---|
| Monthly cost | $800 | $240 | 70% reduction |
| Average build time | 12 min | 4.2 min | 65% faster |
| Failed builds | 8% | 2% | 75% fewer failures |
| Contributor satisfaction | Low | High | No more waiting |
Honestly, we should have done this a year ago. But we were scared to touch the workflow. The Vietnamese team brought fresh eyes and deep GitHub Actions expertise. They didn’t just cut costs—they made our CI faster and more reliable.
Why This Matters for Open Source
Open source projects run on goodwill and limited budgets. Every dollar saved on CI is a dollar you can reinvest into community events, documentation, or paying maintainers. And speed matters: slow CI drives contributors away.
Here’s the kicker: we didn’t sacrifice quality. Our test coverage stayed the same. We just stopped testing things that never broke.
Can You Do This Too?
Yes. Start by auditing your workflow. Look for:
- Massive matrix builds with redundant combinations
- Long dependency installs without caching
- Jobs that run on every push but shouldn’t
- Expensive macOS/Windows runners for trivial tasks
But if you’re busy shipping features (like you should be), consider bringing in experts. The ECOA AI team in Vietnam did this for us in two weeks for less than what we saved in the first month.
Frequently Asked Questions
Q: How do I calculate my current GitHub Actions cost?
Go to your repo’s Settings → Billing → GitHub Actions. You’ll see minutes used per month. Multiply by the runner type cost ($0.008/min for Linux, $0.016 for Windows, $0.08 for macOS). That’s your bill.
Q: Is it safe to use self-hosted runners for open source projects?
Yes, but isolate them. Use dedicated VMs that can’t access your production secrets. We use a separate GitHub environment for self-hosted runners with restricted permissions.
Q: What’s the biggest mistake teams make when optimizing CI costs?
They optimize for speed first, cost second. That’s backwards. Start by removing unnecessary jobs, then add caching, then consider self-hosted runners. Speed is a side effect of efficiency, not the primary goal.
Q: How do I convince my team to change the CI workflow?
Show them the numbers. Our $800/month bill was a wake-up call. Run a cost report, present it at a team meeting, and propose a two-week optimization sprint. The savings speak for themselves.
Related reading: Why Outsourcing Software Development to Vietnam Beats India and the Philippines in 2025