Skip to main content

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

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:

  1. Review usage weekly
  2. Identify expensive jobs (see CI/CD Analytics)
  3. Calculate burn rate: (current_usage / days_elapsed) * days_in_month
  4. 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 started
  • interruptible: 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:

  1. Start small: Default to small runners
  2. Measure: Use CI/CD analytics to identify bottlenecks
  3. Scale selectively: Only increase size for jobs that need it
  4. Parallelize smartly: Sometimes 4x small runners < 1x large runner

Best Practices Summary

Immediate Cost-Saving Actions

  1. Enable interruptible jobs on all MR pipelines

    • Expected savings: 30-50%
  2. Skip pipelines for draft MRs or limit to lint-only

    • Expected savings: 20-30% on development workflows
  3. Add pre-flight validation stage to fail fast

    • Expected savings: 50-80% on failed pipelines
  4. Use path-based rules to skip irrelevant jobs

    • Expected savings: 40-60% in monorepos
  5. Avoid redundant pipelines (branch + MR)

    • Expected savings: 50% of MR pipeline costs
  6. Right-size runners - start with small, scale only when needed

    • Expected savings: 30-50% from over-provisioned runners
  7. 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


Last Updated: 2026-01-08 Priority: CRITICAL - Review and implement immediately