Setting Up GitLab CI/CD

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.
Previous: GitLab vs Jenkins vs GitHub Actions Next: .gitlab-ci.yml Explained

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.