cost optimization
GitLab CI/CD Cost Optimization
CRITICAL PRIORITY - This guide focuses on preventing wasted CI minutes and reducing pipeline costs.
Table of Contents
- Understanding Compute Minutes
- Cost Monitoring
- Interruptible Jobs
- Draft MR Optimization
- Conditional Execution
- Fail Fast Patterns
- Resource Class Selection
- Best Practices Summary
Understanding Compute Minutes
Monthly Allocations (2026)
GitLab measures instance runner usage in compute minutes:
- Free tier: 400 minutes/month
- Premium: 10,000 minutes/month
- Ultimate: 50,000 minutes/month
Cost for Additional Minutes
- $10 per 1,000 minutes (valid for 12 months)
- Used only after monthly quota runs out
- Carried over month-to-month until expiration
- Applied per top-level group or personal namespace
Source: GitLab Compute Minutes Documentation
Cost Monitoring
Track Usage in GitLab
Location: Settings Usage Quotas Pipelines
Monitor:
- Current month usage
- Historical trends
- Per-project breakdown
- Minute multipliers (larger runners = more minutes consumed)
Set Up Budget Alerts
Manual Monitoring:
- Review usage weekly
- Identify expensive jobs (see CI/CD Analytics)
- Calculate burn rate:
(current_usage / days_elapsed) * days_in_month - Project end-of-month usage
External Monitoring:
- Use SaaS Price Pulse for pricing alerts
- Set up custom alerts using GitLab API
- Track minute consumption per project
Identify Expensive Jobs
GitLab UI: Analytics CI/CD Analytics Pipeline charts
Look for:
- Long-running jobs (duration > 5 minutes)
- Frequently triggered jobs
- Jobs with high failure rates (waste minutes on retries)
- Jobs using larger runners (minute multipliers)
Source: GitLab CI/CD Analytics
Interruptible Jobs
What Are Interruptible Jobs?
Jobs that can be automatically canceled when a newer pipeline starts on the same branch.
Key Benefits:
- Faster feedback: Developers get results for latest code immediately
- Resource optimization: Stop wasting minutes on outdated commits
- Cost reduction: Can save 30-50% on MR pipeline costs
Implementation
# Make jobs interruptible by default default: interruptible: true # Override for critical jobs deploy-production: script: ./deploy.sh interruptible: false # Never cancel production deploys only: - main # Conditional interruptibility test: script: npm test interruptible: true rules: - if: $CI_COMMIT_BRANCH == "main" interruptible: false # Don't interrupt on main - when: always
Workflow Auto-Cancel Strategies
workflow: auto_cancel: on_new_commit: interruptible # Only cancel interruptible jobs
Strategies:
conservative: Cancel entire pipeline only if no non-interruptible jobs startedinterruptible: Cancel only jobs marked as interruptible (recommended)
Source: GitLab Interruptible Jobs
Draft MR Optimization
Skip Expensive Jobs on Draft MRs
Draft MRs don't need full test suites or deployments.
workflow: rules: # Skip pipeline entirely for draft MRs - if: $CI_MERGE_REQUEST_TITLE =~ /-draft$/ when: never - if: $CI_MERGE_REQUEST_TITLE =~ /^Draft:/ when: never - when: always # Or skip only expensive jobs e2e-tests: script: npm run test:e2e rules: - if: $CI_MERGE_REQUEST_TITLE =~ /Draft:/ when: never - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: on_success security-scan: script: trivy scan rules: - if: $CI_MERGE_REQUEST_TITLE =~ /-draft$/ when: never - when: on_success
Cost Savings: Skip expensive jobs (e2e tests, security scans, builds) during draft phase. Run them when MR is ready for review.
Conditional Execution
Run Jobs Only When Needed
1. Path-based Execution (most effective for cost savings):
frontend-tests: script: npm run test rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" changes: - "frontend/**/*" - "package.json" - "package-lock.json" when: on_success - when: never backend-tests: script: go test ./... rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" changes: - "backend/**/*" - "go.mod" - "go.sum" when: on_success - when: never
2. Branch-based Execution:
deploy-staging: script: ./deploy-staging.sh rules: - if: $CI_COMMIT_BRANCH == "development" when: on_success - when: never deploy-production: script: ./deploy-production.sh rules: - if: $CI_COMMIT_BRANCH == "main" when: manual - when: never
3. Skip Redundant Jobs:
# Don't run MR pipelines if branch pipeline already ran workflow: rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS when: never - if: $CI_COMMIT_BRANCH
Cost Savings: Can reduce pipeline runs by 40-60% by avoiding redundant branch+MR pipelines.
Fail Fast Patterns
Validate Early, Fail Fast
1. Pre-flight Checks (run first, exit early):
stages: - validate - test - build - deploy check-variables: stage: validate script: - | if [ -z "$DEPLOY_TOKEN" ]; then echo "ERROR: DEPLOY_TOKEN not set" exit 1 fi - echo "All required variables present" lint: stage: validate script: npm run lint # Fast linting before expensive tests unit-tests: stage: test needs: [check-variables, lint] # Wait for validations script: npm test integration-tests: stage: test needs: [check-variables, lint] script: npm run test:integration
2. Use allow_failure Strategically:
# DON'T allow critical jobs to fail silently test: script: npm test allow_failure: false # Explicit - must pass # DO allow non-critical jobs to fail security-scan: script: trivy scan allow_failure: true # Don't block pipeline, but report issues
3. Parallel Jobs with Fail-Fast:
# If any parallel job fails, stop others test-matrix: parallel: matrix: - PLATFORM: [linux, macos, windows] script: npm test interruptible: true # Cancel remaining jobs on first failure
Cost Savings: Exiting early on validation failures can save 80%+ of pipeline costs for failed runs.
Source: GitLab Job Control
Resource Class Selection
Choose Appropriate Runner Sizes
Minute Multipliers:
- Small (1 CPU, 2GB RAM): 1x multiplier
- Medium (2 CPU, 4GB RAM): 2x multiplier
- Large (4 CPU, 8GB RAM): 4x multiplier
- X-Large (8 CPU, 16GB RAM): 8x multiplier
Cost Impact:
Small runner: 10 minutes = 10 compute minutes
Large runner: 10 minutes = 40 compute minutes
X-Large runner: 10 minutes = 80 compute minutes
Right-Sizing Guidelines
# Lightweight jobs - use small runners lint: script: npm run lint tags: - saas-linux-small-amd64 # Fast, low CPU - don't waste money on large runners # CPU-intensive jobs - use larger runners (but measure!) build: script: npm run build tags: - saas-linux-medium-amd64 # Start here # Profile first, then scale up if needed # Tests - batch appropriately unit-tests: script: npm test tags: - saas-linux-small-amd64 # Most unit tests are fine on small e2e-tests: parallel: 4 script: npm run test:e2e tags: - saas-linux-medium-amd64 # Parallelism needs resources
Cost Optimization:
- Start small: Default to small runners
- Measure: Use CI/CD analytics to identify bottlenecks
- Scale selectively: Only increase size for jobs that need it
- Parallelize smartly: Sometimes 4x small runners < 1x large runner
Best Practices Summary
Immediate Cost-Saving Actions
-
Enable interruptible jobs on all MR pipelines
- Expected savings: 30-50%
-
Skip pipelines for draft MRs or limit to lint-only
- Expected savings: 20-30% on development workflows
-
Add pre-flight validation stage to fail fast
- Expected savings: 50-80% on failed pipelines
-
Use path-based rules to skip irrelevant jobs
- Expected savings: 40-60% in monorepos
-
Avoid redundant pipelines (branch + MR)
- Expected savings: 50% of MR pipeline costs
-
Right-size runners - start with small, scale only when needed
- Expected savings: 30-50% from over-provisioned runners
-
Set artifact expiration to 1-7 days (see caching.md)
- Expected savings: Reduces storage costs, not minutes directly
Weekly Monitoring Checklist
- Review CI/CD minute usage (Settings Usage Quotas)
- Identify top 5 most expensive jobs
- Check pipeline failure rates (high = wasted minutes)
- Review interruptible job cancellations
- Validate path-based rules are working
- Audit runner sizes vs actual CPU usage
Red Flags (High Cost Indicators)
- Pipeline runs > 10 minutes on every commit
- Same job runs multiple times in single pipeline
- Large runners for simple tasks (lint, validation)
- No interruptible jobs configured
- Branch + MR pipelines both running
- Draft MRs triggering full test suites
- Jobs with > 30% failure rates
Optimization Workflow
1. Enable monitoring Identify expensive patterns
2. Apply quick wins Interruptible, draft MR skip, fail-fast
3. Deep optimization Path-based rules, runner sizing
4. Measure impact Track minute usage trends
5. Iterate Continuously optimize based on data
Additional Resources
- Pipeline Efficiency Guide - Performance optimization
- Caching Strategies - Speed up jobs to reduce duration
- Validation - Test pipelines locally before pushing
- GitLab CI/CD Analytics
- Compute Minutes FAQ
Last Updated: 2026-01-08 Priority: CRITICAL - Review and implement immediately