Setting Up GitLab CI/CD
Installation
Configuration
First Pipeline
A complete guide to setting up GitLab CI/CD. Learn installation, configuration, project setup, and creating your first pipeline with detailed explanations.
GitLab
Installation
Configuration
Before You Begin
Setting up GitLab CI/CD involves several steps, depending on whether you're using GitLab.com (SaaS) or a self-hosted GitLab instance. This guide covers both scenarios.
Prerequisites:
- GitLab Account: GitLab.com account or self-hosted GitLab instance
- GitLab Repository: A project with code to build
- Docker: For container-based builds (recommended)
- Kubernetes: For container orchestration (optional)
- GitLab Runner: For self-hosted runners (optional)
Two Paths:
- GitLab.com (SaaS): Use shared runners. Just create
.gitlab-ci.yml and go. Fastest way to start.
- Self-Hosted GitLab: Install GitLab Runner, register it, and configure. More control but more setup.
Quick Start with GitLab.com
If you're using GitLab.com, setup is minimal. You only need to create a .gitlab-ci.yml file in your repository.
Create a GitLab Account
Sign up at
gitlab.com if you don't have an account. Verify your email.
Create a New Project
Click "New Project" → "Create blank project". Name your project and set visibility (public/private).
Clone the Repository
Clone the repository to your local machine and add your application code.
Create .gitlab-ci.yml
Add a .gitlab-ci.yml file at the root of your repository with a basic pipeline configuration.
Commit and Push
Commit the changes and push to GitLab. The pipeline will automatically trigger.
# .gitlab-ci.yml - Minimal Pipeline
stages:
- build
- test
build:
stage: build
script:
- echo "Building the application..."
- echo "Build complete!"
test:
stage: test
script:
- echo "Running tests..."
- echo "Tests passed!"
That's it! With GitLab.com shared runners, you now have a working CI/CD pipeline. Navigate to CI/CD → Pipelines to see it running.
Self-Hosted GitLab Runner Installation
For self-hosted GitLab or custom requirements, you need to install and register GitLab Runner.
Install GitLab Runner (Linux)
# Install GitLab Runner on Linux
# For Debian/Ubuntu
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
sudo apt-get install gitlab-runner
# For RHEL/CentOS
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh" | sudo bash
sudo yum install gitlab-runner
# For Binary Install (any Linux)
sudo curl -L --output /usr/local/bin/gitlab-runner "https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64"
sudo chmod +x /usr/local/bin/gitlab-runner
# Create a GitLab CI user
sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
# Install and run as service
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
sudo gitlab-runner start
# Verify installation
gitlab-runner --version
sudo systemctl status gitlab-runner
Install GitLab Runner (macOS)
# Install GitLab Runner on macOS
brew install gitlab-runner
# Start as service
brew services start gitlab-runner
# Verify installation
gitlab-runner --version
Install GitLab Runner (Docker)
# Run GitLab Runner in Docker
docker run -d --name gitlab-runner --restart always \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
# Register the runner
docker exec -it gitlab-runner gitlab-runner register
# Verify the runner
docker exec -it gitlab-runner gitlab-runner list
Registering a GitLab Runner
After installing GitLab Runner, you need to register it with your GitLab instance.
Get Registration Token
Go to your project → Settings → CI/CD → Runners. Copy the registration token.
Register the Runner
Run gitlab-runner register and provide the required information.
Choose an Executor
Select the executor type: shell, docker, docker-machine, kubernetes, etc.
Configure Docker Image
If using Docker executor, specify the default Docker image for jobs.
Verify Registration
Check that the runner appears in GitLab's CI/CD settings.
# Register a GitLab Runner (Interactive)
sudo gitlab-runner register
# Prompts:
# Enter the GitLab instance URL: https://gitlab.com/
# Enter the registration token: YOUR_REGISTRATION_TOKEN
# Enter a description for the runner: My Docker Runner
# Enter tags for the runner: docker,aws
# Enter optional maintenance note: Docker runner for builds
# Enter an executor: docker
# Enter the default Docker image: alpine:latest
# Register a GitLab Runner (Non-Interactive)
sudo gitlab-runner register \
--non-interactive \
--url "https://gitlab.com/" \
--registration-token "YOUR_REGISTRATION_TOKEN" \
--executor "docker" \
--docker-image "alpine:latest" \
--description "Docker Runner" \
--tag-list "docker,aws" \
--run-untagged="true" \
--locked="false" \
--access-level="not_protected"
# List registered runners
sudo gitlab-runner list
# Verify runner connection
sudo gitlab-runner verify
# Check runner status
sudo systemctl status gitlab-runner
Registration Token Security: Keep your registration token secure. It allows anyone to register a runner for your project. Rotate it if compromised.
Configuring the Runner
# GitLab Runner Configuration
# File: /etc/gitlab-runner/config.toml
concurrent = 4
check_interval = 0
[session_server]
session_timeout = 1800
[[runners]]
name = "Docker Runner"
url = "https://gitlab.com/"
token = "YOUR_RUNNER_TOKEN"
executor = "docker"
[runners.custom_build_dir]
[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]
[runners.cache.azure]
[runners.docker]
tls_verify = false
image = "alpine:latest"
privileged = true
disable_entrypoint_overwrite = false
oom_kill_disable = false
disable_cache = false
volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]
shm_size = 0
# Kubernetes Runner Configuration
[[runners]]
name = "Kubernetes Runner"
url = "https://gitlab.com/"
token = "YOUR_RUNNER_TOKEN"
executor = "kubernetes"
[runners.kubernetes]
namespace = "gitlab-runner"
image = "alpine:latest"
privileged = true
cpu_limit = "1"
memory_limit = "2Gi"
service_cpu_limit = "1"
service_memory_limit = "1Gi"
helper_cpu_limit = "500m"
helper_memory_limit = "512Mi"
poll_timeout = 600
[runners.kubernetes.node_selector]
"kubernetes.io/arch" = "amd64"
[runners.kubernetes.pod_labels]
"app" = "gitlab-runner"
# Reload configuration
sudo gitlab-runner restart
# Verify configuration
sudo gitlab-runner verify
Key Configuration Options:
- concurrent: Maximum number of jobs to run concurrently
- executor: Docker, shell, kubernetes, virtualbox, etc.
- docker.privileged: Allow privileged containers (needed for Docker-in-Docker)
- docker.volumes: Mount volumes like Docker socket or cache
- kubernetes.namespace: Namespace for runner pods
- kubernetes.cpu_limit: CPU limit for job pods
Creating Your First Pipeline
# .gitlab-ci.yml - Complete First Pipeline
# This pipeline demonstrates the core concepts of GitLab CI/CD
# Pipeline stages run sequentially
stages:
- build
- test
- deploy
# Global variables
variables:
NODE_VERSION: "18"
DOCKER_REGISTRY: $CI_REGISTRY
IMAGE_NAME: $CI_REGISTRY_IMAGE
# Default settings for all jobs
default:
image: node:$NODE_VERSION
before_script:
- echo "Starting job $CI_JOB_NAME"
- echo "Pipeline ID: $CI_PIPELINE_ID"
- echo "Commit: $CI_COMMIT_SHORT_SHA"
# Build Stage
build:
stage: build
script:
- echo "Installing dependencies..."
- npm install
- echo "Building the application..."
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 week
# Test Stage
test:
stage: test
script:
- echo "Running tests..."
- npm test
artifacts:
reports:
junit: test-results.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
# Deploy Stage (Development - Automatic)
deploy_dev:
stage: deploy
script:
- echo "Deploying to development..."
- npm run deploy:dev
environment:
name: development
url: https://dev.example.com
only:
- develop
# Deploy Stage (Production - Manual)
deploy_prod:
stage: deploy
script:
- echo "Deploying to production..."
- npm run deploy:prod
environment:
name: production
url: https://example.com
when: manual
only:
- main
First Pipeline Complete: This pipeline:
- Builds the application on every push
- Tests the application and reports coverage
- Deploys to development automatically
- Deploys to production manually with approval
- Uses artifacts and environments
Configuring CI/CD Variables
# Adding variables in GitLab UI
# Project → Settings → CI/CD → Variables
# Add variable: DOCKER_USERNAME
# Type: Variable
# Key: DOCKER_USERNAME
# Value: myusername
# Protected: ✓
# Masked: ✓
# Add variable: DOCKER_PASSWORD
# Type: Variable
# Key: DOCKER_PASSWORD
# Value: mypassword
# Protected: ✓
# Masked: ✓
# Using variables in .gitlab-ci.yml
build:
stage: build
script:
- echo "Logging into Docker registry..."
- docker login -u $DOCKER_USERNAME -p $DOCKER_PASSWORD
- docker build -t myapp:$CI_COMMIT_SHORT_SHA .
# Predefined CI/CD Variables
# $CI_COMMIT_SHA - Commit SHA
# $CI_COMMIT_SHORT_SHA - Short commit SHA
# $CI_COMMIT_BRANCH - Branch name
# $CI_COMMIT_TAG - Tag name (if tag)
# $CI_PIPELINE_ID - Pipeline ID
# $CI_JOB_ID - Job ID
# $CI_PROJECT_NAME - Project name
# $CI_PROJECT_NAMESPACE - Project namespace
# $CI_REGISTRY - Container registry URL
# $CI_REGISTRY_IMAGE - Registry image path
# $CI_REGISTRY_USER - Registry username
# $CI_REGISTRY_PASSWORD - Registry password
# Using predefined variables
build:
stage: build
script:
- echo "Building commit $CI_COMMIT_SHORT_SHA"
- echo "On branch $CI_COMMIT_BRANCH"
- echo "Project $CI_PROJECT_NAME"
Variable Security:
- Masked: Hide variable values in job logs
- Protected: Only available on protected branches/tags
- File: Store as file (for certificates, etc.)
- Never commit secrets to the repository
- Use masked variables for all sensitive data
Triggering the Pipeline
# Pipelines are triggered automatically on:
# 1. Push to any branch
# 2. Merge request creation
# 3. Tag creation
# 4. Scheduled runs (via UI)
# 5. Manual trigger (via UI or API)
# 6. Webhook events
# Commit and push to trigger
git add .
git commit -m "Add CI/CD pipeline"
git push origin main
# View pipeline in GitLab UI
# CI/CD → Pipelines
# Trigger via API
curl -X POST \
-F "token=YOUR_TRIGGER_TOKEN" \
-F "ref=main" \
"https://gitlab.com/api/v4/projects/PROJECT_ID/trigger/pipeline"
# Schedule a pipeline
# Project → CI/CD → Schedules → New Schedule
# Manual trigger via UI
# CI/CD → Pipelines → Run Pipeline
# View pipeline status
# CI/CD → Pipelines → Click on pipeline
# Check job logs
# CI/CD → Pipelines → Click pipeline → Click job
# Pipeline statuses:
# - pending: Waiting for runner
# - running: Executing
# - success: Completed successfully
# - failed: Completed with errors
# - canceled: Manually canceled
# - skipped: Skipped (rules)
Frequently Asked Questions
Do I need a GitLab Runner for GitLab.com?
No, GitLab.com provides shared runners that work out of the box. You only need to install a runner for self-hosted GitLab or if you need custom capabilities.
How do I choose an executor for my runner?
Use Docker executor for most cases (isolation, consistency). Use Kubernetes executor for auto-scaling in Kubernetes. Use Shell executor for simple setups. Choose based on your infrastructure.
Where do I put the .gitlab-ci.yml file?
Place it at the root of your repository. GitLab automatically detects and executes it. You can also use include to reference other files.
How do I use Docker-in-Docker (DinD)?
Use the Docker executor with privileged = true and add the Docker daemon as a service in your job. Use docker:dind as the service image.
How do I pass secrets to my pipeline?
Use CI/CD variables in GitLab UI (Settings → CI/CD → Variables). Mark them as masked and protected. Never commit secrets to the repository.
How do I debug a failed pipeline?
Check the job logs for error messages. Use CI_DEBUG_TRACE=true for verbose output. Check runner logs with journalctl -u gitlab-runner.
Can I run pipelines on specific branches only?
Yes, use only, except, or rules to control which branches trigger jobs. Use rules for more complex conditions.
How do I speed up pipelines?
Use caching for dependencies, use needs for DAG (parallel execution), use smaller Docker images, and use parallel jobs where possible.
Setting up GitLab CI/CD is straightforward. With GitLab.com shared runners, you can have a working pipeline in minutes. For custom requirements, self-hosted runners provide full control.