← Back 04

Results, Costs & Lessons Learned

May 2026 · 5-part series: migrating CI/CD from GitHub to Codeberg

Pipeline Performance

Our CI pipeline runs ~12-15 minutes end-to-end — the same as on GitHub Actions. But the details matter more than the headline number.

PhaseStepDuration
1lint-backend~3 min
1test-backend (8 workers)~7 min
1test-frontend~2 min
2build-backend~3 min
2build-frontend-staging~2 min
3deploy-staging~3 min

Key difference: Backend tests run faster. On GitHub-hosted runners (2 vCPU), pytest's -n auto used 2 workers. On our spot pool (8 vCPU e2-highcpu-8), -n auto detects 8 CPUs and uses 8 workers. Test time dropped from ~20 minutes to ~7 minutes.

Cost Breakdown

ComponentGitHub ActionsForgejo on GKE
CI runnersBilled per minute~$3/month (spot, scale-to-zero)
Container registryGHCR (free, bundled)GAR (~$0.10/GB stored)
Runner compute2 vCPU per runner8 vCPU spot (~$0.01/hr)
Cold startNone (queues anyway)~2-3 min (pool scaled to zero)
Total monthly$$$ (minutes add up)~$3/month, $0 when idle

Scale-to-zero is the game changer. Our CI runner costs exactly $0 when nobody is pushing code. For a small team pushing 10-20 times per day, total CI compute is ~$3/month.

Reliability

MetricGitHub ActionsForgejo on GKE
CI availability99.5-99.9% (reported)99.95% GKE, 99.99% Cloud SQL
Status accuracy"Operational" while brokenWe own the runner — it works or we see why
Debug loopFile ticket, waitkubectl logs, done
QueueingShared runners, minutesDedicated spot pool
Webhook reliabilityDropped, no ackCodeberg or manual

The key advantage isn't the SLA number — it's the debug loop. On two occasions during our GitHub era, deploys were blocked for hours by platform incidents the status page reported as "operational." Each time, we wasted hours debugging our own config. On our self-hosted runner, those hours would have been minutes.

What We'd Do Differently

  1. Start with a canary repo — test the full pipeline before touching main
  2. Test secret encoding early — deploy a test secret with postgresql:// on day one
  3. Keep GitHub as a read-only mirror from day one
  4. Budget more time for gotchas — the secret encoding bug alone cost 4+ hours
  5. Set up monitoring earlykubectl logs and Grafana on day one

Advice for Teams Considering This

← What Broke Next: The Dagger.io Plan →