HomeClaude Code for TeamsGitLab CI/CD with Claude Code
intermediate12 min read· Module 12, Lesson 2

🦊GitLab CI/CD with Claude Code

Integrate Claude Code into GitLab merge request workflows

GitLab CI/CD with Claude Code

GitLab CI/CD is GitLab's built-in continuous integration and delivery platform. Unlike GitHub Actions which uses separate workflow files, GitLab uses a single .gitlab-ci.yml file at the root of your repository to define your entire pipeline. When you combine GitLab CI/CD with Claude Code, you can automate merge request reviews, code quality checks, security scanning, and more — all triggered automatically by pipeline events.

Why GitLab CI/CD + Claude Code?

Many organizations choose GitLab for its all-in-one DevOps platform. If your team is already on GitLab, integrating Claude Code directly into your CI/CD pipelines means:

  • Automated MR reviews — Claude reads diffs and posts review comments on every merge request
  • Pipeline-native integration — Claude runs as a pipeline job, not an external service
  • Docker runner support — Run Claude inside Docker containers for consistent, isolated environments
  • Self-hosted compatibility — Works with GitLab self-managed instances, not just GitLab.com
  • Fine-grained access control — Use GitLab's CI/CD variables, protected branches, and environments
  • Consistent quality — Every MR gets the same thorough automated review

Understanding GitLab CI/CD Concepts

Before diving into the configuration, let's clarify key GitLab CI/CD concepts:

ConceptDescription
PipelineA collection of jobs organized in stages
StageA group of jobs that run in parallel within the pipeline
JobA single task that runs a script (e.g., run Claude review)
RunnerThe machine (or container) that executes jobs
.gitlab-ci.ymlThe single configuration file defining the entire pipeline
CI/CD VariablesEnvironment variables stored securely in GitLab settings
ArtifactsFiles produced by jobs and passed between stages
Merge Request PipelineA pipeline triggered specifically by MR events

Pipeline Stages

A typical pipeline with Claude Code integration looks like this:

build -> test -> review -> deploy

Claude Code jobs typically run in the review stage, after your build and tests pass, so you only spend API tokens on code that already compiles and passes basic tests.


Setting Up Your First GitLab Pipeline

Step 1: Store Your API Key

Never hardcode your Anthropic API key. Store it as a CI/CD variable in GitLab:

  1. Go to your project in GitLab
  2. Navigate to Settings > CI/CD > Variables
  3. Click Add variable
  4. Key: ANTHROPIC_API_KEY
  5. Value: Your Anthropic API key (starts with sk-ant-)
  6. Check Mask variable to hide it in job logs
  7. Optionally check Protect variable to limit it to protected branches
  8. Click Add variable

Step 2: Create the Pipeline File

Create .gitlab-ci.yml at the root of your repository:

YAML
stages: - build - test - review variables: CLAUDE_MODEL: "claude-sonnet-4-20250514" claude-mr-review: stage: review image: node:20-slim only: - merge_requests script: - npm install -g @anthropic-ai/claude-code - | claude -p "Review the changes in this merge request. Focus on: 1. Code correctness and potential bugs 2. Security vulnerabilities 3. Performance issues 4. Code style and readability 5. Missing error handling Be specific and actionable in your feedback. For each issue found, suggest a concrete fix." --output-format json > review.json artifacts: paths: - review.json when: always

Understanding the Configuration

Let's break down each section:

stages — Defines the order in which stages run. Jobs within the same stage run in parallel.

variables — Pipeline-level environment variables. The ANTHROPIC_API_KEY is pulled automatically from CI/CD Variables.

only: merge_requests — This job runs only when a merge request is created or updated. Other options include:

  • only: [main, develop] — Run on specific branches
  • only: tags — Run on tag pushes
  • except: [main] — Run on everything except main

image — The Docker image used for the job. We use node:20-slim because Claude Code CLI requires Node.js.

artifacts — Files saved after the job finishes. The review output is saved as JSON for downstream consumption.


Docker Runner Configuration

GitLab runners execute your CI/CD jobs. For Claude Code integration, Docker runners are recommended because they provide isolated, reproducible environments.

Shared Runners (GitLab.com)

If you use GitLab.com, shared runners with Docker are available by default. No extra setup is needed.

Self-Hosted Runner with Docker

For self-managed GitLab instances, set up a runner with Docker executor:

Terminal
# Install GitLab Runner curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash sudo apt-get install gitlab-runner # Register the runner with Docker executor sudo gitlab-runner register \ --non-interactive \ --url "https://gitlab.example.com/" \ --registration-token "YOUR_REGISTRATION_TOKEN" \ --executor "docker" \ --docker-image "node:20-slim" \ --description "claude-code-runner" \ --tag-list "claude,review" \ --run-untagged="false"

Using Tags to Target Runners

Assign the job to specific runners using tags:

YAML
claude-mr-review: stage: review tags: - claude - review image: node:20-slim script: - npm install -g @anthropic-ai/claude-code - claude -p "Review this MR..."

Merge Request Review Automation

Here is a comprehensive MR review pipeline that checks multiple quality dimensions and posts results back as MR comments.

Posting Comments on Merge Requests

Use GitLab's API to post Claude's review as a comment on the MR:

YAML
stages: - build - test - review claude-comprehensive-review: stage: review image: node:20-slim only: - merge_requests variables: GIT_DEPTH: 0 before_script: - apt-get update && apt-get install -y curl jq git - npm install -g @anthropic-ai/claude-code script: - | # Get the MR diff git diff origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD > mr_diff.txt # Run Claude review REVIEW=$(claude -p "You are a senior code reviewer. Analyze this diff thoroughly. $(cat mr_diff.txt) ## Review Checklist - [ ] No obvious bugs or logic errors - [ ] Error handling is adequate - [ ] No security vulnerabilities - [ ] No hardcoded secrets or credentials - [ ] Performance is reasonable - [ ] Code follows project conventions - [ ] Tests are included or updated For each issue, provide: - File and line number - Severity: critical / warning / suggestion - Description of the issue - Suggested fix with code snippet If the code looks good, say so explicitly.") # Post review as MR comment using GitLab API curl --request POST \ --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \ --header "Content-Type: application/json" \ --data "$(jq -n --arg body "$REVIEW" '{body: $body}')" \ "$CI_API_V4_URL/projects/$CI_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes"

Setting Up the GitLab Token

To post comments, you need a GitLab access token:

  1. Go to User Settings > Access Tokens (or create a Project Access Token)
  2. Create a token with api scope
  3. Add it as a CI/CD variable named GITLAB_TOKEN
  4. Mark it as Masked and optionally Protected

Pipeline Stages: A Complete Example

Here is a full pipeline configuration with multiple Claude-powered stages:

YAML
stages: - build - test - security - review - deploy variables: CLAUDE_MODEL: "claude-sonnet-4-20250514" NODE_IMAGE: "node:20-slim" # --- Build Stage --- build: stage: build image: $NODE_IMAGE script: - npm ci - npm run build artifacts: paths: - dist/ # --- Test Stage --- test: stage: test image: $NODE_IMAGE script: - npm ci - npm run test artifacts: reports: junit: test-results.xml # --- Security Scan with Claude --- claude-security-scan: stage: security image: $NODE_IMAGE only: - merge_requests - main before_script: - apt-get update && apt-get install -y curl jq git - npm install -g @anthropic-ai/claude-code script: - | claude -p "Perform a security audit on the changed files. Check for: 1. Hardcoded secrets, API keys, or passwords 2. SQL injection vulnerabilities 3. Cross-site scripting (XSS) risks 4. Insecure deserialization 5. Path traversal vulnerabilities 6. Missing input validation 7. Insecure cryptographic practices 8. Missing authentication or authorization checks For each finding: - Severity: CRITICAL / HIGH / MEDIUM / LOW - CWE reference if applicable - Exact file and line - Remediation steps with code example" > security-report.txt artifacts: paths: - security-report.txt when: always # --- Code Review with Claude --- claude-code-review: stage: review image: $NODE_IMAGE only: - merge_requests before_script: - apt-get update && apt-get install -y curl jq git - npm install -g @anthropic-ai/claude-code script: - | git diff origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD > diff.txt REVIEW=$(claude -p "Review this code diff: $(cat diff.txt) Focus on correctness, performance, and maintainability. Be concise but specific.") # Post to MR curl --request POST \ --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \ --header "Content-Type: application/json" \ --data "$(jq -n --arg body "$REVIEW" '{body: $body}')" \ "$CI_API_V4_URL/projects/$CI_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes" # --- Deploy Stage --- deploy: stage: deploy image: $NODE_IMAGE only: - main script: - echo "Deploying to production..."

Environment Variables Reference

GitLab provides many predefined CI/CD variables. Here are the most useful ones for Claude integration:

VariableDescription
CI_MERGE_REQUEST_IIDThe internal ID of the merge request
CI_MERGE_REQUEST_TARGET_BRANCH_NAMEThe target branch of the MR
CI_MERGE_REQUEST_SOURCE_BRANCH_NAMEThe source branch of the MR
CI_PROJECT_IDThe numeric project ID
CI_API_V4_URLThe base URL for the GitLab API v4
CI_COMMIT_SHAThe full commit SHA
CI_COMMIT_BRANCHThe branch name
CI_PIPELINE_IDThe unique pipeline ID
CI_JOB_TOKENA temporary token for API access within the pipeline
ANTHROPIC_API_KEYYour API key (from CI/CD Variables)

Using Variables in Scripts

YAML
script: - echo "Reviewing MR #$CI_MERGE_REQUEST_IID" - echo "Target branch: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME" - echo "Source branch: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME"

Comparing GitLab CI/CD with GitHub Actions

If you have used GitHub Actions with Claude Code, here is how the two platforms compare:

FeatureGitHub ActionsGitLab CI/CD
Config file.github/workflows/*.yml (multiple).gitlab-ci.yml (single file)
Trigger syntaxon: pull_requestonly: merge_requests
SecretsRepository SecretsCI/CD Variables
RunnersGitHub-hosted or self-hostedShared or self-hosted
Docker supportVia container keyNative Docker executor
Official Claude actionanthropics/claude-code-action@v1No official action (use CLI directly)
Comment on PR/MRBuilt into the actionUse GitLab API via curl
Artifactsactions/upload-artifactBuilt-in artifacts keyword
Cachingactions/cacheBuilt-in cache keyword
Environmentsenvironment keyenvironment key (similar)
Parallel jobsMatrix strategyParallel keyword + matrix

Key Differences

Configuration style — GitHub uses multiple workflow files; GitLab uses a single file. For large projects, GitLab supports include to split configuration across files.

Claude integration — GitHub has an official Claude Code action that handles posting comments natively. In GitLab, you call the Claude CLI directly and use the GitLab API to post MR comments.

Runners — GitLab's Docker executor is more tightly integrated. Every job runs inside a fresh Docker container by default.

Migrating a GitHub Action to GitLab CI

Here is a side-by-side comparison:

GitHub Actions:

YAML
name: Claude PR Review on: pull_request: types: [opened, synchronize] jobs: review: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} prompt: "Review this PR for bugs and security issues."

GitLab CI/CD equivalent:

YAML
claude-mr-review: stage: review image: node:20-slim only: - merge_requests before_script: - apt-get update && apt-get install -y curl jq git - npm install -g @anthropic-ai/claude-code script: - | git diff origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME...HEAD > diff.txt REVIEW=$(claude -p "Review this MR for bugs and security issues. $(cat diff.txt)") curl --request POST \ --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \ --header "Content-Type: application/json" \ --data "$(jq -n --arg body "$REVIEW" '{body: $body}')" \ "$CI_API_V4_URL/projects/$CI_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes"

Advanced Configuration

Using rules Instead of only/except

GitLab recommends using the newer rules syntax for more flexible pipeline control:

YAML
claude-mr-review: stage: review image: node:20-slim rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: always - when: never script: - npm install -g @anthropic-ai/claude-code - claude -p "Review this merge request..."

Splitting Configuration with include

For large projects, split your Claude CI config into a separate file:

YAML
# .gitlab-ci.yml include: - local: '.gitlab/ci/claude-review.yml' - local: '.gitlab/ci/claude-security.yml' stages: - build - test - security - review - deploy
YAML
# .gitlab/ci/claude-review.yml claude-mr-review: stage: review image: node:20-slim rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" before_script: - apt-get update && apt-get install -y curl jq git - npm install -g @anthropic-ai/claude-code script: - claude -p "Review this MR..."

Caching Node Modules

Speed up your pipeline by caching npm dependencies:

YAML
claude-mr-review: stage: review image: node:20-slim cache: key: claude-cli paths: - /usr/local/lib/node_modules/ before_script: - npm install -g @anthropic-ai/claude-code script: - claude -p "Review this MR..."

Timeout and Retry Configuration

Protect against long-running Claude API calls:

YAML
claude-mr-review: stage: review image: node:20-slim timeout: 10 minutes retry: max: 2 when: - api_failure - runner_system_failure script: - npm install -g @anthropic-ai/claude-code - claude -p "Review this MR..."

Cost Optimization Tips

Running Claude in CI pipelines can accumulate costs. Here are strategies to keep spending under control:

1. Run Only on MR Events

YAML
rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: always - when: never

2. Filter by Changed File Paths

YAML
rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" changes: - "src/**/*" - "lib/**/*" when: always - when: never

3. Skip Draft MRs

YAML
rules: - if: $CI_MERGE_REQUEST_TITLE =~ /^Draft:/ when: never - if: $CI_PIPELINE_SOURCE == "merge_request_event" when: always

4. Use Cost-Effective Models for Simple Tasks

YAML
# Use Haiku for simple triage claude-triage: variables: CLAUDE_MODEL: "claude-haiku-4-20250514" script: - claude -p "Categorize this MR..." # Use Sonnet for deep security reviews claude-security: variables: CLAUDE_MODEL: "claude-sonnet-4-20250514" script: - claude -p "Perform security audit..."

5. Limit Output Tokens

YAML
script: - claude -p "Review this MR..." --max-tokens 2048

6. Set Monthly Budget Alerts

Monitor your Anthropic API usage dashboard and configure spending alerts. Set hard limits on your API key to prevent runaway costs from pipeline loops.

Cost Estimation Table

Job TypeRuns/MonthAvg TokensEst. Cost
MR review1003,000~$3-5
Security scan305,000~$3-4
Code triage501,000~$0.50
Test suggestions802,000~$2-3

Costs vary by model and token pricing. Check the Anthropic pricing page for current rates.


Debugging and Troubleshooting

Common Issues

ProblemCauseSolution
Job fails with 401Invalid API keyVerify ANTHROPIC_API_KEY in CI/CD Variables
No MR comment postedMissing GitLab tokenAdd GITLAB_TOKEN with api scope
Job stuck pendingNo available runnerCheck runner status and tags
Timeout errorsLarge diff or slow responseIncrease timeout or limit diff size
High costsNo filters on triggersAdd rules with path and draft filters
Permission deniedProtected variable on unprotected branchUnprotect the variable or protect the branch

Viewing Job Logs

  1. Go to CI/CD > Pipelines in your project
  2. Click on the pipeline
  3. Click on the specific job
  4. Review the log output for errors

Testing Locally

Use Docker to replicate the CI environment locally:

Terminal
# Run the same Docker image used in CI docker run -it --rm \ -e ANTHROPIC_API_KEY="your-key" \ -v $(pwd):/workspace \ -w /workspace \ node:20-slim bash # Inside the container apt-get update && apt-get install -y curl jq git npm install -g @anthropic-ai/claude-code claude -p "Review this code..."

Summary

ConceptWhat You Learned
.gitlab-ci.ymlSingle file defining the entire pipeline
StagesOrdered groups of parallel jobs
MR pipelinesPipelines triggered by merge request events
Docker runnersIsolated containers executing CI jobs
CI/CD VariablesSecure storage for API keys and tokens
MR commentsPosting Claude reviews via GitLab API
Rules syntaxFlexible pipeline control with conditions
Include filesSplitting configuration for large projects
Cost optimizationPath filters, draft skipping, model selection
GitHub comparisonKey differences in config, triggers, and integration

By integrating Claude Code into GitLab CI/CD, you bring intelligent automated code review directly into your merge request workflow. Every MR gets consistent, thorough feedback without waiting for human reviewers — while keeping costs under control through smart filtering and model selection.