🦊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:
| Concept | Description |
|---|---|
| Pipeline | A collection of jobs organized in stages |
| Stage | A group of jobs that run in parallel within the pipeline |
| Job | A single task that runs a script (e.g., run Claude review) |
| Runner | The machine (or container) that executes jobs |
.gitlab-ci.yml | The single configuration file defining the entire pipeline |
| CI/CD Variables | Environment variables stored securely in GitLab settings |
| Artifacts | Files produced by jobs and passed between stages |
| Merge Request Pipeline | A 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:
- Go to your project in GitLab
- Navigate to Settings > CI/CD > Variables
- Click Add variable
- Key:
ANTHROPIC_API_KEY - Value: Your Anthropic API key (starts with
sk-ant-) - Check Mask variable to hide it in job logs
- Optionally check Protect variable to limit it to protected branches
- Click Add variable
Step 2: Create the Pipeline File
Create .gitlab-ci.yml at the root of your repository:
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: alwaysUnderstanding 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 branchesonly: tags— Run on tag pushesexcept: [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:
# 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:
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:
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:
- Go to User Settings > Access Tokens (or create a Project Access Token)
- Create a token with
apiscope - Add it as a CI/CD variable named
GITLAB_TOKEN - Mark it as Masked and optionally Protected
Pipeline Stages: A Complete Example
Here is a full pipeline configuration with multiple Claude-powered stages:
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:
| Variable | Description |
|---|---|
CI_MERGE_REQUEST_IID | The internal ID of the merge request |
CI_MERGE_REQUEST_TARGET_BRANCH_NAME | The target branch of the MR |
CI_MERGE_REQUEST_SOURCE_BRANCH_NAME | The source branch of the MR |
CI_PROJECT_ID | The numeric project ID |
CI_API_V4_URL | The base URL for the GitLab API v4 |
CI_COMMIT_SHA | The full commit SHA |
CI_COMMIT_BRANCH | The branch name |
CI_PIPELINE_ID | The unique pipeline ID |
CI_JOB_TOKEN | A temporary token for API access within the pipeline |
ANTHROPIC_API_KEY | Your API key (from CI/CD Variables) |
Using Variables in Scripts
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:
| Feature | GitHub Actions | GitLab CI/CD |
|---|---|---|
| Config file | .github/workflows/*.yml (multiple) | .gitlab-ci.yml (single file) |
| Trigger syntax | on: pull_request | only: merge_requests |
| Secrets | Repository Secrets | CI/CD Variables |
| Runners | GitHub-hosted or self-hosted | Shared or self-hosted |
| Docker support | Via container key | Native Docker executor |
| Official Claude action | anthropics/claude-code-action@v1 | No official action (use CLI directly) |
| Comment on PR/MR | Built into the action | Use GitLab API via curl |
| Artifacts | actions/upload-artifact | Built-in artifacts keyword |
| Caching | actions/cache | Built-in cache keyword |
| Environments | environment key | environment key (similar) |
| Parallel jobs | Matrix strategy | Parallel 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:
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:
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:
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:
# .gitlab-ci.yml
include:
- local: '.gitlab/ci/claude-review.yml'
- local: '.gitlab/ci/claude-security.yml'
stages:
- build
- test
- security
- review
- deploy# .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:
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:
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
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: always
- when: never2. Filter by Changed File Paths
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
changes:
- "src/**/*"
- "lib/**/*"
when: always
- when: never3. Skip Draft MRs
rules:
- if: $CI_MERGE_REQUEST_TITLE =~ /^Draft:/
when: never
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
when: always4. Use Cost-Effective Models for Simple Tasks
# 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
script:
- claude -p "Review this MR..." --max-tokens 20486. 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 Type | Runs/Month | Avg Tokens | Est. Cost |
|---|---|---|---|
| MR review | 100 | 3,000 | ~$3-5 |
| Security scan | 30 | 5,000 | ~$3-4 |
| Code triage | 50 | 1,000 | ~$0.50 |
| Test suggestions | 80 | 2,000 | ~$2-3 |
Costs vary by model and token pricing. Check the Anthropic pricing page for current rates.
Debugging and Troubleshooting
Common Issues
| Problem | Cause | Solution |
|---|---|---|
| Job fails with 401 | Invalid API key | Verify ANTHROPIC_API_KEY in CI/CD Variables |
| No MR comment posted | Missing GitLab token | Add GITLAB_TOKEN with api scope |
| Job stuck pending | No available runner | Check runner status and tags |
| Timeout errors | Large diff or slow response | Increase timeout or limit diff size |
| High costs | No filters on triggers | Add rules with path and draft filters |
| Permission denied | Protected variable on unprotected branch | Unprotect the variable or protect the branch |
Viewing Job Logs
- Go to CI/CD > Pipelines in your project
- Click on the pipeline
- Click on the specific job
- Review the log output for errors
Testing Locally
Use Docker to replicate the CI environment locally:
# 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
| Concept | What You Learned |
|---|---|
.gitlab-ci.yml | Single file defining the entire pipeline |
| Stages | Ordered groups of parallel jobs |
| MR pipelines | Pipelines triggered by merge request events |
| Docker runners | Isolated containers executing CI jobs |
| CI/CD Variables | Secure storage for API keys and tokens |
| MR comments | Posting Claude reviews via GitLab API |
| Rules syntax | Flexible pipeline control with conditions |
| Include files | Splitting configuration for large projects |
| Cost optimization | Path filters, draft skipping, model selection |
| GitHub comparison | Key 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.