GitLab CI was originally created as a standalone open-source companion project to GitLab, developed by Dmitriy Zaporozhets and Kamil Trzciński in 2012.
In 2015 (GitLab 8.0), GitLab CI was integrated directly into the core GitLab product, meaning developers no longer needed to maintain a separate CI server.
Key innovations:
Introduced GitLab Runner written in Go, allowing developers to execute builds in lightweight containerized environments.
Promoted the use of .gitlab-ci.yml placed in the root directory to define pipeline pipelines-as-code.
Native support for Container Registries, Auto DevOps, and Kubernetes clusters.
Who:
Created by Dmitriy Zaporozhets, Kamil Trzciński, and the GitLab Inc. team.
Why:
Designed to provide a single, unified application interface for the entire software development lifecycle (devops toolchain), eliminating the coordination friction of using separate VCS (like GitHub) and CI servers (like Jenkins).
Introduction
Advantages
Single Interface Integration — Code hosting, merge request reviews, issue tracking, CI pipelines, container registries, and deployments are housed in the same UI.
Lightweight Runner Executables — The Go-based GitLab Runner is simple to install on VMs, Raspberry Pis, or Kubernetes nodes.
First-Class Container Support — Built-in support to run jobs inside custom Docker images, compile containers, and push them to the integrated registry.
Directed Acyclic Graph (DAG) — Jobs can bypass standard stage sequencing using the needs tag, allowing fast-running steps to execute immediately.
SaaS or Self-Hosted — Run pipelines using free SaaS runtime hours or connect private runners to handle heavy compute tasks for free.
Disadvantages
Monolithic UI Loading — The single interface can become sluggish when navigating large configurations, pipelines, and logs.
Proprietary YAML Extensions — GitLab-specific syntax keywords (like rules, extends, trigger) are unique to the platform and non-portable.
Configuration Bloat — Large enterprise projects frequently end up with thousand-line .gitlab-ci.yml files that are hard to debug locally.
Remember Points
Default Stage Sequencing — Jobs in the same stage run in parallel. The next stage only starts if all jobs in the current stage succeed.
Pre-execution Container Checks — Every job in GitLab CI runs in its own shell or isolated container. Any state changes (like temporary files) are lost between jobs unless declared as cache or artifacts.
Runners & Executors
Execution Environments
GitLab Runner is the agent application that executes pipeline jobs.
Executors define the technical environment where the script is executed:
Shell Executor: Runs scripts directly on the host machine shell (bash/powershell). Fast but lacks isolation; build tools must be installed on the host.
Docker Executor: Downloads specified container images and executes scripts in isolated containers. Clean state for every job run.
Kubernetes Executor: Spins up dynamic pods in a Kubernetes cluster for each job, scaling automatically based on pipeline loads.
YAML Pipeline Architecture
.gitlab-ci.yml Basic Structure
# Define pipeline execution phases in orderstages: - prepare - build - test - deploy# Global configuration parametersdefault: image: node:18-alpine before_script: - echo "Starting job execution steps..."# Define environment variablesvariables: GLOBAL_VAR: "SharedConfigValue"# Job 1: Build Applicationbuild_job: stage: build script: - npm install - npm run build artifacts: paths: - dist/ expire_in: 1 week# Job 2: Run Unit Teststest_job: stage: test script: - npm install - npm run test:unit variables: LOCAL_VAR: "JobSpecificOverride"
Directed Acyclic Graph & Rules
DAG (needs) and Conditional Execution (rules)
stages: - build - test - deploybuild_backend: stage: build script: - echo "Building backend app..."build_frontend: stage: build script: - echo "Building frontend app..."# 'needs' bypasses the build stage boundary: test_frontend runs immediately# as soon as build_frontend completes, without waiting for build_backend.test_frontend: stage: test script: - echo "Testing frontend..." needs: - build_frontend# 'rules' evaluates conditions dynamically (replacing legacy only/except tags)deploy_staging: stage: deploy script: - echo "Deploying to staging environment..." rules: # Execute only if push is on the main branch - if: '$CI_COMMIT_BRANCH == "main"' when: on_success # Allow manual trigger on staging branches - if: '$CI_COMMIT_BRANCH =~ /^stage-/' when: manual # Do not execute in other scenarios - when: never
Cache vs. Artifacts
Context Sharing Management
Cache: Designed to speed up builds by reusing dependencies (like npm modules or composer libraries). Not guaranteed to be present and should not contain build results.
Artifacts: Designed to store outputs (like bin files or zip bundles) that are passed to downstream jobs or made available for download in the UI.
stages: - build - testjob_with_both: stage: build script: - npm install - npm run compile # Cache npm modules folder across pipeline runs (uses branch key) cache: key: ${CI_COMMIT_REF_SLUG} paths: - node_modules/ # Save compile output directories for testing jobs artifacts: name: "build-output-${CI_COMMIT_SHA}" paths: - build/ expire_in: 2 days
Reusable Templates & Child Pipelines
DRY Patterns and Includes
Avoid code duplication using YAML anchors &, job extensions extends, and files configuration inclusion.
# 1. Base template structure.test_template: stage: test image: python:3.9 before_script: - pip install -r requirements.txt rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'# 2. Extend base template in concrete jobsunit_tests: extends: .test_template script: - pytest tests/unit/integration_tests: extends: .test_template script: - pytest tests/integration/# 3. Include external templates (from other repos or default templates)include: - template: Security/SAST.gitlab-ci.yml # Built-in Static Application Security Testing - project: 'my-org/shared-ci-templates' file: '/templates/docker-build.yml'# 4. Triggering a Child Pipeline (runs a sub-pipeline yaml)trigger_frontend_pipeline: stage: build trigger: include: frontend/.gitlab-ci.yml strategy: depend # Parent pipeline succeeds only if child succeeds
Deployments & Environments
Multi-Stage Gates and Rollbacks
Manage production endpoints, deployments tracking, and automatic manual rollback options in the GitLab UI:
deploy_production: stage: deploy script: - echo "Deploying build artifacts to production servers..." - ./deploy.sh environment: name: production url: https://my-app.company.com on_stop: stop_production_environment # Specify teardown script job rules: - if: '$CI_COMMIT_BRANCH == "main"' when: manual # Requires explicit user approval click in GitLab dashboardstop_production_environment: stage: deploy script: - echo "Tearing down production resources..." environment: name: production action: stop rules: - if: '$CI_COMMIT_BRANCH == "main"' when: manual