GitLab Tutorial: Learn DevOps Platform from Scratch (2026)
I joined a startup that was drowning in toolchain sprawl — GitHub for code, Jenkins for CI, a separate Docker registry, and a wiki nobody updated. GitLab replaced all of them. It is a single application covering the entire DevOps lifecycle: repository management, CI/CD, container registry, package registry, security scanning, and observability. This unified approach means every artifact, pipeline, and deployment is linked back to a merge request. In this tutorial, I will walk through GitLab from initial setup to advanced CI/CD pipelines, drawing from my experience running GitLab for teams of five to five hundred engineers.
GitLab Architecture and Installation Options
GitLab comes in two editions: Community Edition (CE) and Enterprise Edition (EE). CE includes core features like repositories, CI/CD, and container registry. EE adds advanced features like compliance pipelines, security dashboards, and multiple approval rules. You can self-host GitLab on Linux using the Omnibus package, deploy via Helm on Kubernetes, or use GitLab.com for a fully managed experience. The Omnibus installation packages everything — PostgreSQL, Redis, NGINX, Sidekiq, and the Rails application — into a single coherent system. I have managed both self-hosted instances and GitLab.com. Self-hosting gives you control over runners and data residency but requires operational attention. GitLab.com removes that burden and is suitable for most teams in 2026.
sudo apt update && sudo apt install -y curl openssh-server ca-certificates
curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.deb.sh | sudo bash
sudo EXTERNAL_URL="https://gitlab.example.com" apt install gitlab-ce
gitlab-ctl reconfigure
Repositories, Branches, and Merge Requests
GitLab repositories support all the standard Git workflows with added collaboration features. Merge requests are the centerpiece — they provide a discussion thread, pipeline status, code review, and approval workflow in one view. I enforce branch protection on main: no direct pushes, require merge request approvals, and require pipelines to pass before merging. The merge request page shows the diff, inline comments, resolved threads, and pipeline results. GitLab's merge methods include merge commit, fast-forward merge, and squash merge. I prefer squash merge for feature branches to keep history clean. The web IDE lets reviewers edit files directly from the merge request, which speeds up small fixes tremendously. Linked issues close automatically when the merge request merges with the appropriate commit message.
git clone git@gitlab.example.com:mygroup/myproject.git
git checkout -b feature/add-login
git push -u origin feature/add-login
# Create merge request via web UI or GitLab CLI
# glab mr create --title "Add login feature" --description "Closes #42"
GitLab CI/CD Pipeline Configuration
The .gitlab-ci.yml file defines your pipeline as YAML. Each job runs in a separate environment — a shell executor, a Docker container, or a Kubernetes pod. Jobs belong to stages that execute sequentially by default. I define stages like .pre, build, test, deploy, and .post. The rules keyword controls job execution conditions based on branch names, file changes, or variables. The cache keyword speeds up pipelines by preserving dependencies between runs. Artifacts pass build outputs between stages. GitLab CI has first-class support for parallel jobs with the parallel keyword, which splits test suites across multiple jobs. The pipeline graph in the GitLab UI shows the entire flow, including manual gates and downstream pipeline triggers.
stages:
- build
- test
- deploy
build-job:
stage: build
image: node:20-alpine
script:
- npm ci
- npm run build
artifacts:
paths:
- dist/
test-job:
stage: test
image: node:20-alpine
script:
- npm ci
- npm run test:ci
coverage: '/All files[|]\d+\.\d+/'
Runners: Where Pipelines Execute
GitLab runners are agents that pick up pipeline jobs and execute them. Shared runners are provided by GitLab.com or configured for your self-hosted instance. Specific runners are tied to a project or group. I maintain a fleet of auto-scaling Docker runners on AWS: when jobs queue, the runner creates EC2 instances, runs the jobs, and terminates them. The runner configuration in config.toml sets concurrent job limits, executor type, and environment variables. The Docker executor is the most versatile — each job gets a fresh container with the specified image. The Kubernetes executor runs each job in a Pod, which is ideal for dynamic scaling. Tags help route jobs to the right runner: a job with tags: [gpu] only runs on runners with that tag.
# Install and register a GitLab runner
docker run -d --name gitlab-runner --restart always \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
gitlab/gitlab-runner:latest
gitlab-runner register \
--url https://gitlab.com \
--token $GITLAB_TOKEN \
--executor docker \
--docker-image alpine:latest \
--tag-list "linux,amd64"
Container Registry and Package Management
GitLab includes a built-in container registry integrated with each project. When CI builds a Docker image, you push it to registry.gitlab.com/namespace/project/image:tag. The registry UI shows tags, size, and download count. Pipeline jobs can use images from the same project's registry, creating an efficient inner loop. GitLab also hosts package registries for Maven, npm, PyPI, NuGet, and others. I use the npm registry to publish internal libraries scoped to our organization. CI jobs authenticate with CI_JOB_TOKEN, so no external credentials are needed. The dependency proxy caches upstream images, reducing pull time and avoiding Docker Hub rate limits. This integrated registry means I never manage a separate artifact storage system.
docker-build:
stage: build
image: docker:27-cli
services:
- docker:27-dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
rules:
- if: $CI_COMMIT_BRANCH == "main"
Security Scanning and Compliance Features
GitLab includes security scanners that run automatically in CI. SAST scans source code for vulnerabilities, DAST tests running applications, secret detection finds leaked credentials, and dependency scanning checks for known CVEs in libraries. The results appear in the merge request widget and the security dashboard. I have caught several hardcoded API keys and vulnerable npm packages before they reached production. The compliance pipeline framework lets you define mandatory pipeline jobs across all projects in a group. Audit events track who changed what in the GitLab configuration. For regulated industries, GitLab EE provides evidence collection, separation of duties through multiple approval rules, and signed commits with GPG. These features make GitLab a complete DevSecOps platform.
include:
- template: Jobs/SAST.gitlab-ci.yml
- template: Jobs/Secret-Detection.gitlab-ci.yml
- template: Jobs/Dependency-Scanning.gitlab-ci.yml
sast:
stage: test
variables:
SAST_EXCLUDED_ANALYZERS: "eslint"
secret_detection:
stage: test
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
Frequently Asked Questions
What is the difference between GitLab CE and EE?
GitLab CE is free with core features: repository management, CI/CD, container registry, and basic security scanning. GitLab EE adds compliance pipelines, multiple approval rules, security dashboards, vulnerability management, and advanced authentication like SAML and LDAP group sync. EE requires a paid subscription for production use.
How does GitLab CI compare to GitHub Actions?
Both are excellent. GitLab CI has been a built-in feature since 2012 and has a mature YAML syntax with include, rules, and parallel. GitHub Actions launched later and uses a broader ecosystem of reusable actions. GitLab excels when you want a single application for the entire DevOps lifecycle rather than integrating separate tools.
Can I migrate from GitHub to GitLab?
Yes. GitLab provides a project import page that accepts GitHub repositories, pull requests, issues, and wiki pages. The migration tool handles most data, though some advanced GitHub-specific features like Actions secrets and environments may need manual reconfiguration. GitLab also has a GitHub API compatibility endpoint to ease the transition.
What is a GitLab Runner and do I need it?
A GitLab Runner is an agent that executes CI/CD jobs. GitLab.com provides shared runners free of charge with limited minutes. For self-hosted GitLab, you must install your own runners. Even on GitLab.com, you might want a specific runner if you need custom hardware, on-premises deployment access, or GPU support. Runners can run on Linux, Windows, or macOS.
Originally published on Ayodhyyya. Last updated June 1, 2026.