CircleCI Automation: 7 Ways Your Pipeline Burns Credits (and How to Catch Them)
The monthly credit report landed and the number had roughly doubled. Nobody could explain it. The team shipped about the same features as the quarter before, merged about the same number of PRs, ran about the same test suite. And yet the credit meter said otherwise, and no one in the room could point to the workflow, the project, or the resource class that ate the difference.
That gap is the normal failure mode of CircleCI automation. Credits are consumed per second, per resource class, across dozens of workflows, and the pipeline that quietly costs the most is almost never the one anyone is watching. A flaky test auto-reruns three times before it goes green; an oversized resource class runs 40 jobs a day at four times the CPU it needs; a workflow rebuilds node_modules from scratch on every commit because its cache key never hits. None of it shows up as a broken build. It shows up as a bigger invoice.
The good news: on teams running roughly 50,000 to 2,000,000 credits a month, waste is predictable. It concentrates in the same seven patterns nearly every time. This guide walks through each one — what it is, why it happens, how to detect it with a real dashboard path, config.yml pattern, or API call, and what it typically costs. You can run these checks in an afternoon.
This is part one of a three-part series. Part two is a hands-on CircleCI build triage with native tools; part three covers automating the whole loop with a CloudThinker agent.
1. Flaky tests auto-rerunning and burning credits
What it is. A test that passes and fails nondeterministically. The pipeline goes red, someone clicks "Rerun workflow from failed," it goes green, and everyone moves on. Every rerun re-executes the job — the same setup, the same checkout, the same test run — and every second of it is billed again.
Why it happens. Fixing a flaky test is real work; clicking rerun is free. So reruns become the coping mechanism, and the flake never gets fixed. Over months, a handful of chronically flaky tests can double the effective cost of the jobs that contain them.
How to detect it. CircleCI's Insights surfaces this directly if you upload test results with store_test_results. In the app: Project → Insights → Tests, sorted by "Most failed" and flakiness. Via the API, list recent flaky tests for a project:
curl -s --header "Circle-Token: $CIRCLE_TOKEN" \
"https://circleci.com/api/v2/insights/gh/your-org/your-repo/flaky-tests"
Typical impact. On suites with known flakes, reruns commonly add 10–25% to the credit cost of the affected jobs — and far more developer-hours lost to waiting.
2. Resource classes oversized for the job
What it is. Each job declares a resource_class (for example medium, large, xlarge), and CircleCI bills credits per minute at a rate that climbs with the class. A lint job that needs half a CPU running on large pays the large rate for the whole run. Multiply by every commit, every day.
Why it happens. Someone bumped the class once to fix an out-of-memory error or speed up a slow job, it worked, and it was never revisited. Resource class is set-and-forget, and nobody profiles a job that already passes.
How to detect it. Check the actual CPU and memory a job uses against the class it's on. In the app, open a recent run of the job and read the Resources tab on the job page — it graphs CPU and RAM utilization for the run. If CPU peaks under 40% and memory sits well below the class ceiling, the job is oversized. The class itself is declared in config.yml:
jobs:
lint:
docker:
- image: cimg/node:20.11
resource_class: large # profile before trusting this
steps:
- checkout
- run: npm run lint
Typical impact. Dropping one class (say xlarge to large) roughly halves that job's per-minute credit rate. Across a fleet of oversized jobs this is frequently 15–30% of compute credits.
3. Workflows without caching — dependencies rebuilt every run
What it is. A job that reinstalls dependencies from scratch on every single commit because it has no dependency cache, or a cache key that never matches. The install step runs full-length every time, billing credits for work the previous run already did.
Why it happens. Caching is opt-in and easy to get subtly wrong. A key like deps-{{ checksum "package-lock.json" }} only hits when that file is byte-identical; a key with no checksum never invalidates; a save_cache step that was never added means nothing is ever stored.
How to detect it. Read the job's timing: Project → Insights → Workflows, open a workflow, and look at the per-step durations across recent runs. If the dependency-install step takes the same long time on every run, the cache is missing. Confirm the pattern in config.yml — a correct restore/save pair looks like this:
steps:
- checkout
- restore_cache:
keys:
- deps-v1-{{ checksum "package-lock.json" }}
- deps-v1-
- run: npm ci
- save_cache:
key: deps-v1-{{ checksum "package-lock.json" }}
paths:
- ~/.npm
If there's a restore_cache with no matching save_cache, or no checksum in the key, that's your miss.
Typical impact. A cold install every run commonly adds 30–90 seconds per job; on high-frequency pipelines that's easily 10–20% of a workflow's credits, recovered almost entirely by one correct cache key.
4. Fan-out that queues on concurrency limits
What it is. A workflow fans out into many parallel jobs — or uses high parallelism for test splitting — beyond the concurrency your plan allows. The excess jobs queue. Queue time isn't billed as compute, but it stretches every pipeline's wall-clock time, and teams respond by buying more concurrency or bigger classes rather than fixing the fan-out.
Why it happens. Parallelism looks free and always makes a single run faster in isolation. Nobody accounts for the whole org contending for the same concurrency ceiling at 10 a.m. when everyone pushes at once.
How to detect it. Look for a gap between when a job is created and when it starts. Project → Insights → Workflows shows duration; the per-job view shows queued time. Via the API, pull recent workflow runs and inspect timing:
curl -s --header "Circle-Token: $CIRCLE_TOKEN" \
"https://circleci.com/api/v2/insights/gh/your-org/your-repo/workflows/build-test?branch=main"
The response includes duration percentiles per workflow; rising p95 with flat compute is the queueing signature.
Typical impact. Rarely direct credit waste, but chronic queueing pushes teams into concurrency and resource-class upgrades that add 10–20% to spend without making anything genuinely faster.
5. Failed workflows nobody re-examines after the rerun goes green
What it is. A workflow fails, gets rerun, passes, and the original failure is never looked at again. Sometimes the failure was a real regression that the rerun masked because the flake happened to pass; sometimes it was infrastructure; sometimes it was a genuine bug that a retry hid. The signal is discarded the moment the badge turns green.
Why it happens. Green is the goal, and a green rerun feels like resolution. The failed run's logs require opening the job, reading output, and thinking — the rerun button requires none of that.
How to detect it. Find workflows that needed a rerun to succeed. Pull recent runs and look for the same pipeline appearing as both a failed and a later successful workflow. Via the API:
curl -s --header "Circle-Token: $CIRCLE_TOKEN" \
"https://circleci.com/api/v2/insights/gh/your-org/your-repo/workflows/build-test/jobs"
The success-rate and per-job failure counts tell you which jobs pass only after retries — those are the ones hiding something.
Typical impact. Hard to price in credits, but every masked regression that reaches production costs far more than the rerun did. Success rate under roughly 90% on a job that "always eventually passes" is the tell.
6. Credit spend nobody attributes per project
What it is. A single CircleCI org runs many projects, and the invoice is one number. Nobody knows which project, which branch, or which workflow drives the spend, so nobody can target a fix. The team optimizing a 2%-of-credits pipeline while a 40% one runs untouched is the normal outcome.
Why it happens. CircleCI bills at the org level, and attribution requires deliberately pulling per-project data and summing it. No one owns that report until the invoice doubles.
How to detect it. The org-level breakdown lives in the app under Plan → Plan Usage, which shows credit consumption over time and lets you filter by project. For a programmatic view, list your org's projects and pull each one's workflow Insights to rank them:
curl -s --header "Circle-Token: $CIRCLE_TOKEN" \
"https://circleci.com/api/v2/insights/gh/your-org/your-repo/workflows"
Iterate that across projects to build the attribution table CircleCI doesn't hand you directly.
Typical impact. Attribution rarely saves credits on its own, but it's the prerequisite for every other fix in this list — without it you optimize blind, and the biggest line item stays invisible.
7. Long-running jobs with no timeout or step-level budget
What it is. A job that occasionally hangs — a stuck test process, a network wait, a deadlocked container — and runs to CircleCI's default job timeout (10 minutes of no output, or up to the max job runtime) before failing. Every one of those minutes is billed, for a job that was never going to succeed.
Why it happens. Timeouts are optional, so most jobs inherit generous defaults. A job that hangs once a week goes unnoticed because it's rare — but each occurrence bills the full runaway duration.
How to detect it. Look for jobs whose duration distribution has a long tail: a p50 of two minutes and a p95 of fifteen means something occasionally hangs. The Workflows Insights view exposes these percentiles. Cap the runaway in config.yml with a step-level no_output_timeout:
- run:
name: integration tests
command: npm run test:integration
no_output_timeout: 5m
Typical impact. Usually small in aggregate — a few percent — but pure waste, and the fix is a single line that fails the hung job in minutes instead of letting it burn to the ceiling.
Why the monthly credit report keeps surprising you
Here's the uncomfortable part, and the reason "we'll look at credits when the invoice comes" is not a CircleCI automation strategy: waste regenerates on every push.
Every new flaky test starts getting auto-rerun. Every copy-pasted job config inherits the oversized resource class. Every new service ships without a cache key. Every fan-out contends for the same concurrency ceiling. The seven patterns above aren't a list you clear once — they're steady-state processes that produce credit waste continuously, and a monthly report catches them 30 days after the meter started running.
Do the math on a pipeline burning $8,000/month in credits with 25% addressable waste: each month of detection latency is roughly $2,000 gone, and the report that would reveal it takes an engineer half a day to assemble — so it gets skipped the month you ship a big release, which is exactly the month that creates the most waste.
The fix isn't reading the invoice harder. It's making detection continuous. Start manual — the next article in this series walks through triaging all of this with CircleCI's own Insights, config patterns, and the v2 API — but know where it ends up.
Run these checks continuously
Everything above can run on autopilot. A CloudThinker agent connects to CircleCI through a read-only API token and watches your workflows continuously — flagging flaky tests before reruns pile up, resource classes that are oversized for their jobs, cache keys that never hit, and per-project credit drift — with any change gated behind your approval and approval jobs left entirely to humans.
Try CloudThinker free — 100 premium credits, no card required — or continue the series with the hands-on native-tools triage guide.
