GitLab CI Compute Minutes: How They Are Calculated and How to Spend Fewer
How GitLab CI compute minutes are calculated — cost factors, tier quotas, what happens at zero — and the pipeline-data levers that cut usage without touching a YAML file.
A GitLab CI compute minute is the billing unit for pipelines that run on GitLab.com's hosted runners. The formula is simple: take a job's duration in seconds, divide by 60, and multiply by the cost factor of the runner size it ran on — 1 for a small Linux machine, up to 12 for the largest sizes, per GitLab's compute minutes documentation. Every job in every pipeline in a top-level namespace draws from one shared monthly pool: as of 2026-07-28, 400 minutes on the Free tier, 10,000 on Premium, and 50,000 on Ultimate, per GitLab's pricing page.
Spending fewer compute minutes therefore comes down to three variables — how long jobs run, how often they run, and what machine multiplier they run on — plus one structural escape hatch: self-hosted runners consume no compute minutes at all. This article walks through the calculation, the quotas, what actually happens when the pool hits zero, and the optimization levers that pipeline data reliably surfaces.
What a compute minute is
Compute minutes meter usage of instance runners — the shared runner fleet GitLab operates for GitLab.com projects. When a job runs on one of these machines, GitLab computes:
compute minutes = (job duration in seconds / 60) × cost factor
Three details in that formula matter more than they look:
- Only execution time counts. Job duration excludes time spent in the
createdorpendingstates, so queue time — however painful for feedback loops — does not consume compute minutes. - The metering is fractional. A 90-second job on a small Linux runner costs 1.5 compute minutes, not 2. This is a real difference from GitHub Actions, which — as of 2026-07-28 — rounds each job's duration up to the next whole minute — a subtle but compounding surcharge explained in CI minute rounding. If your jobs are short and numerous, GitLab's per-second metering is structurally kinder to you than GitHub's per-job round-up.
- The pool is per namespace, not per user. All projects under one top-level group share a single monthly quota. A 5-person team and a 50-person team on the same tier start each month from the same allocation, and one runaway repository can drain the pool for every other project in the namespace.
Usage accumulates across the calendar month and resets to zero on the first of the next month. Included minutes do not roll over.
Cost factors by runner type
The cost factor is the price multiplier for machine size and platform. One real minute on a small Linux runner costs one compute minute; the same real minute on larger or more specialized hardware costs more. As of 2026-07-28, GitLab documents these factors for its hosted runners:
| Runner type | Cost factor | 10 real minutes consume |
|---|---|---|
| Linux x86-64, small | 1 | 10 compute minutes |
| Linux x86-64, medium | 2 | 20 compute minutes |
| Linux x86-64, large | 3 | 30 compute minutes |
| Linux x86-64, xlarge | 6 | 60 compute minutes |
| Linux x86-64, 2xlarge | 12 | 120 compute minutes |
| Linux + GPU (medium, standard) | 7 | 70 compute minutes |
| Linux Arm64 (small / medium / large) | 1 / 2 / 3 | 10 / 20 / 30 compute minutes |
| macOS M1 (medium, Beta) | 6 | 60 compute minutes |
| macOS M2 Pro (large, Beta) | 12 | 120 compute minutes |
| Windows (medium, Beta) | 1 | 10 compute minutes |
Two discounts exist outside this table: public projects in GitLab's open-source program run at a 0.5 cost factor, and public forks of those projects at 0.008 — but private, commercial work always pays the full factor for its machine size.
The practical reading of the table: runner size upgrades are multiplicative, not additive. A team that moves its test suite from small to large Linux runners to shave wall-clock time has tripled the compute-minute price of every one of those job-minutes. That trade is often worth it — a suite that finishes in a third of the time on a 3× machine costs the same and returns feedback faster — but only if the job actually scales with the extra cores. A job that is I/O-bound or serial by nature pays 3× for nearly the same duration. This is exactly the kind of claim pipeline timing data can settle, and gut feeling cannot: compare the job's duration distribution before and after the size change, and check whether the duration drop matches the factor increase.
Quotas by tier
As of 2026-07-28, GitLab's pricing page lists these monthly allocations and prices for GitLab.com:
| Tier | Included compute minutes / month | Price |
|---|---|---|
| Free | 400 | $0 |
| Premium | 10,000 | $29 per user/month, billed annually |
| Ultimate | 50,000 | Custom (contact sales) |
When the pool runs dry, additional compute minutes cost $10 per 1,000 minutes as a one-time purchase (as of 2026-07-28, per GitLab's pricing page). Per GitLab's subscription documentation, purchased minutes are consumed only after the included monthly quota is exhausted, and — unlike the included allocation — unused purchased minutes roll over month to month (nominally valid for 12 months from purchase, though GitLab notes expiry is not yet enforced).
To put the quotas in perspective: 400 Free-tier minutes is one daily pipeline totalling 15 job-minutes on a small Linux runner — roughly 330 minutes over a 22-workday month, and the month is nearly spent. A 10-person team on Premium running a pipeline that totals 12 job-minutes on medium runners (factor 2) burns 24 compute minutes per pipeline — about 415 pipelines per month before hitting the 10,000 cap, or roughly 20 pipelines per workday. Active teams with merge request pipelines, scheduled jobs, and retries reach that faster than the headline number suggests.
When minutes run out
GitLab does not silently degrade — it stops. Per the instance runner compute minutes documentation, when a namespace exhausts its quota:
- Instance runners stop picking up new jobs. Pending jobs and retried jobs that require instance runners are dropped.
- Running jobs get a small buffer. Jobs already executing continue until the namespace exceeds its quota by 1,000 compute minutes, then they are dropped too.
- Your own runners keep working. Project and group runners you host yourself are unaffected — the outage applies only to GitLab's shared fleet.
GitLab warns ahead of the cliff: per its instance runner compute minutes documentation (as of 2026-07-28), in-app and email notifications go to namespace owners when remaining minutes fall below 25% of the quota, again below 5%, and at zero. (GitLab's pricing FAQ states 30% rather than 25% for the first banner.) The remedies are buying additional minutes, waiting for the monthly reset on the first, upgrading the tier, or moving work to your own runners.
The operational risk is less the hard stop itself than when it lands. Quota exhaustion is a function of cumulative monthly usage, so it clusters at month-end — which is also when release pipelines tend to run. A team that treats the quota as a soft budget discovers it is a hard one at the worst possible moment. If your usage crosses 75% before the 20th of the month, the math says you will hit the wall; that is the moment to act, not at the zero notification.
Self-hosted runners
The single largest structural lever in GitLab CI cost is that self-hosted runners — project or group runners you register on your own hardware or cloud instances — consume no compute minutes. The quota meters GitLab's machines, not yours. A namespace can route its heaviest jobs (long integration suites, container builds, GPU workloads) to its own runners and reserve the hosted pool for light, bursty work, or skip the hosted fleet entirely.
This changes the cost question rather than eliminating it. Self-hosted runners trade a metered, visible line item for infrastructure you provision, patch, and scale yourself — and their cost becomes invisible in exactly the way hosted minutes are not. An idle runner fleet sized for peak load burns cloud spend around the clock; nobody gets a quota warning about it. Teams that make this move need their own answer to "what does a pipeline cost us now," because GitLab's usage screens no longer provide one.
The same applies, more strongly, to self-hosted GitLab itself. Teams running GitLab CE or EE on their own infrastructure have no GitLab-hosted runner fleet in the picture at all — every runner is one they operate, every minute is unmetered, and the compute-minute quota system simply is not the lens through which their CI cost is visible. Their cost story is written entirely in pipeline durations, failure rates, and rerun patterns — data that exists in their GitLab instance but that GitLab's own UI does little to aggregate into money.
Where minutes actually go
Before optimizing, decompose. A monthly compute-minute bill is the sum of every job's duration × factor, which means it can be reconstructed — and attributed — entirely from pipeline metadata: which repository, which job, which runner size, how long, how often, and critically, how much of it bought nothing. The recurring sinks, in rough order of how often they dominate:
Failed runs that get re-run. A failed pipeline consumes its full compute minutes and then consumes them again on the retry. The minutes spent on the failure bought no merge, no deploy, no information beyond "it failed" — and the cost of failed builds compounds when the failure rate is concentrated in the longest pipelines. A repository with a 15% failure rate on a 30-minute pipeline wastes more minutes than one with a 40% failure rate on a 3-minute lint job.
The retry habit. When re-running is the standard response to a red pipeline, a
retry tax accrues quietly: compute spent re-executing work that already
ran. One honest caveat about measuring this on GitLab — retrying re-runs failed jobs inside the
same pipeline, so the pipeline id does not change, and superseded attempts are hidden from the
jobs list unless include_retried=true is requested
(GitLab API docs, as of 2026-07-28). There is no
per-run attempt counter on the pipeline record the way there is on GitHub Actions. The pattern
still shows in the data, as fail-then-pass
alternation on the same branch within a short window, and it almost always traces back to
flaky tests rather than genuinely broken code.
Duplicate pipelines. A classic GitLab-specific leak: a push to a branch with an open merge
request can trigger both a branch pipeline and a merge request pipeline for the same commit —
double the minutes for identical work. In pipeline data this is unmistakable: two runs, same
commit, same jobs, minutes apart. The fix lives in workflow:rules in .gitlab-ci.yml; the
detection requires nothing but run metadata.
Stale work that runs to completion. When a developer pushes three times in ten minutes, the
first two pipelines are answering a question nobody is still asking. Jobs not marked
interruptible run to completion anyway and bill every minute. Cancelled runs, for their part,
still consume everything up to the cancellation point — a pipeline cancelled at minute 25 of 30
saved almost nothing.
Hung jobs and generous timeouts. A job that hangs runs until its timeout — and bills the
entire wait. With GitLab's default job timeout at 60
minutes — and jobs on GitLab.com hosted runners
capped at 3 hours regardless of the project
setting, both as of 2026-07-28 — one hung integration test per day can quietly outspend the actual
test suite. One measurement caveat here: GitLab reports a
timed-out job with the plain failed status — the timeout is only visible in the job's
failure_reason field (e.g. stuck_or_timeout_failure), not as a distinct status the way GitHub
Actions returns timed_out — so hunting these means looking for failures whose duration sits
exactly at the timeout ceiling.
Scheduled pipelines on quiet repositories. Nightly builds keep running whether or not anyone pushed. On an active repository they earn their minutes; on a dormant one they are a subscription to re-testing yesterday's answer.
Optimization levers
Every lever below is a change to .gitlab-ci.yml or runner configuration — but which lever to
pull, and in which repository, is a question pipeline data answers far more reliably than
intuition. (For the record, this data-first perspective is also the one TrimCI takes: it analyzes
run and job metadata and never reads your .gitlab-ci.yml, source, or any repository file — the
observability data alone identifies the target; the YAML edit stays entirely in your hands.)
Cut the failure re-run loop first
Minutes spent on failures and their retries are the purest waste in the bill, and unlike duration optimizations, eliminating them requires no infrastructure work — it requires knowing which jobs fail repeatedly with the same failure fingerprint and fixing the top three. Rank candidates by minutes wasted, not failure count: frequency × duration × cost factor, not frequency alone.
Deduplicate branch and MR pipelines
If run data shows paired pipelines per commit, a workflow:rules block that suppresses branch
pipelines when a merge request is open cuts the duplicated share of the bill in one edit. The size
of the win is arithmetic, not guesswork: it equals the share of your pipelines that currently run
twice for one commit — run data gives you that number before you touch the YAML.
Make superseded work cancellable
Marking jobs interruptible and enabling auto-cancel for redundant pipelines stops billing for
answers nobody needs. The before/after is directly measurable: the share of minutes attributed to
runs on commits that were superseded within minutes.
Fail fast and order jobs by cost
Put cheap, high-signal jobs (lint, typecheck, fast unit tests) in the earliest stage so a doomed pipeline dies in minute 2 instead of minute 25. Time-to-failure in job data tells you whether your pipeline currently discovers failures early or late.
Right-size runner factors with evidence
For each job on a medium/large/xlarge runner, ask whether its duration actually improved proportionally to the cost factor when it was upgraded. If a factor-3 machine runs the job only 20% faster than a factor-1 machine, the upgrade multiplied cost by 2.4 for a marginal win. Job duration percentiles across runner sizes make this an arithmetic exercise.
Tighten timeouts to the evidence
Set job timeouts just above the observed p99 duration instead of the default. This converts hung jobs from hour-scale losses into minute-scale ones and makes them visible as a spike of failures at the ceiling.
Cache and split the proven offenders
Dependency caching, Docker layer caching, and test parallelization (parallel, DAG via needs)
are the classic duration levers — and the ones most often applied to the wrong job. The p95
duration ranking, not the loudest complaint, should pick the target; a
slow pipeline is a cost problem and a feedback problem at
once, and both diagnoses start from the same duration data.
Route heavy work to your own runners
Once the metered levers are exhausted, the structural one remains: jobs whose duration × factor × frequency dominates the bill are candidates for self-hosted runners, where the quota does not apply. Make the move with the per-job numbers in hand so the hosted savings can be weighed against real infrastructure cost rather than estimated against a feeling.
The analytics gap
Here is the odd part of the GitLab CI cost story: the data needed for every decision above already exists in GitLab's API — durations, statuses, runner details, per-pipeline job breakdowns — yet the CI analytics tooling ecosystem is overwhelmingly GitHub-centric. Most CI observability and cost products launched on GitHub Actions and treat GitLab as a later addition or a roadmap item (Datadog is a notable exception — GitLab ships a built-in Datadog integration, as of 2026-07-28); support for self-hosted GitLab CE/EE, where the compute-minute lens does not exist and the cost question is hardest to see, is rarer still. GitLab ships pipeline duration and success-rate charts in its built-in CI/CD analytics, and usage screens showing minutes consumed per project — but the two views never meet: nothing connects minutes to failures, retries, or dollars. (For a sense of how the larger observability platforms cover — and price — this space, see TrimCI vs Datadog CI Visibility.)
TrimCI treats GitLab as a first-class provider on every plan, including Free: gitlab.com and
self-hosted GitLab CE/EE (14 or newer, reachable over HTTPS) alike, with the
same dashboards, PDF reports, and AI recommendations as GitHub Actions. Access is deliberately
narrow — a read_api-scoped token constrained by an HTTP-layer allowlist to project, pipeline and
job endpoints (plus the token and version checks needed to connect), never repository contents, so
TrimCI structurally cannot read your source; the enforcement details are public on
/security/. Fair caveats apply: TrimCI syncs by polling (hourly on paid plans, daily
on Free) rather than webhooks, works at run/job granularity rather than per-test, ships its AI
recommendations as a beta-labelled, quota-limited feature running on a 7B-class local model, and
keeps at most a 60-day history window — it is a cost and failure analysis
tool, not a real-time monitor.
From synced pipeline data, TrimCI computes the numbers this article keeps pointing at: per-repository failure rates over decisive runs, duration percentiles, flaky-pipeline alternation on a branch (the GitLab-visible form of the retry pattern described above — the per-attempt re-run tax stays GitHub-only), and an estimated monthly cost of failed and repeated work in dollars — the calculator gives a quick first estimate of that loss, and plan details live on /pricing/. The compute-minute quota tells you when you have spent too much; pipeline data tells you where, and what to change so next month's bill answers to you.
See what your CI failures actually cost.
Connect GitHub Actions or GitLab CI with read-only access — never your source code — and get a ranked, dollar-costed fix list from your own pipeline data.