Everyone's doing the "GitHub Actions is free" victory lap, and CircleCI's sales team is no doubt pushing their "simple per-job pricing." Let's cut through the cloud-native fog for a minute. When your pipeline is essentially a glorified `kubectl` wrapper with a side of Helm, the cost levers aren't in the marketing copy.
I've been auditing our team's invoices for a pipeline that builds, containerizes, and deploys a dozen microservices to a staging cluster. The devil is in the compute minutes, and more specifically, in the *idle* compute minutes.
* **GitHub Actions:** Your "free" minutes vanish when you need macOS runners for iOS builds or larger runners for Docker layer caching. The real gotcha? The persistent workflow state you need for multi-stage K8s deployments. If you're using `kubectl wait` or a Helm hook that polls, your runner is sitting there, billing you, while your cluster decides if a Pod is ready. That's not building software; that's paying for a timer.
* **CircleCI:** Their credit system is just obfuscated compute-time pricing. The advertised "parallelism" is a trap for K8s work. Yes, you can fan out ten jobs to test ten services concurrently. But if each job spins up its own Docker-in-Docker container for building images, you're paying for the overhead ten times over. Their "machine executor" for heavier workloads? The credits burn faster than a misconfigured resource limit.
So, the question isn't which is cheaper on a per-job basis. It's which one lets you *avoid* paying for compute you aren't actively using. Can you realistically use their native caching to shave minutes off each build, or are you just moving the bottleneck to your own artifact repository? Does their "K8s integration" actually mean they schedule jobs *on* your cluster, or does it just mean they have a step to run `kubectl apply`?
I'd love to see real numbers from someone who has migrated between them for a similar stack. Not the "we saved 20%" blog post numbers, but the line items: what was the actual compute-minute burn rate per deployment, and how much of that was truly productive work?
Vendor claims are hypotheses, not facts.
I'm the security lead at a 75-person fintech. We run a dozen Java/Go services on GKE, with our pipeline doing container builds, vulnerability scans, and ArgoCD syncs.
1. **Real cost for K8s polling:** Your timer example is correct. For us, GitHub's hidden cost was in `kubectl wait` and security scanning. A 10-minute Snyk scan on a large image held a $0.008/s (8 cents/min) runner for 600 seconds. That's $4.80 per scan, per service. CircleCI's credit model obfuscates this, but the compute cost is still there. Our bill for ~50 daily builds was $300-400/month on GH, $450-550 on Circle.
2. **K8s auth and secret sprawl:** Both fail by default. GitHub's `${{ secrets }}` and Circle's contexts get baked into YAML. You must script secret injection into your k8s manifests or use something like Sealed Secrets. GitHub's OIDC integration for AWS/Azure is solid; Circle's is clunkier but works. The vulnerability is permanent env vars with cluster credentials.
3. **Self-hosted runner operational load:** We tried GitHub's self-hosted runners on a GKE node pool to cut costs. It saved ~30% but added 5-7 hours/week of maintenance (runner scaling, security patches, networking). Circle's equivalent (runner on your infra) has the same tax. Not worth it unless you have dedicated platform team hours.
4. **Security event visibility:** Neither gives you a real SIEM feed. You can ship audit logs to your cloud, but alerting on "runner executed `kubectl exec`" requires a custom pipeline. I built a Lambda that parsed GitHub's audit log to detect anomalous k8s commands. It was ~150 lines of Python. Circle's API was worse.
Pick GitHub Actions if you're already on GitHub and can accept the idle-time tax for simplicity. The OIDC integration is one less secret to rotate. Pick CircleCI only if your finance team requires per-job invoicing and you have the cycles to manage their context hierarchy for secrets.
If you want a clean call, tell us your team's weekly ops hours budget for pipeline maintenance and whether you need SOC2 evidence for pipeline access logs.
Least privilege is not a suggestion.
Exactly! That persistent state cost is brutal. It's not just `kubectl wait` either - think about running integration tests that need the deployed pods to be live. Your pipeline is burning cash just sitting there while your test suite pings a service.
A hack we've used is to split the workflow. The first job deploys with Helm and outputs the release name, then exits. A second, manually-triggered job handles the waiting and testing. Saves compute if a deployment is clearly broken early. But it's clunky.
Have you looked at using the K8s API directly from a self-hosted runner? The idle cost disappears if that runner's just a t3a.nano you're already paying for.
pipeline all the things