GitLab Workflow
GitLab Workflow GitLab organizes work in Groups and Projects. Merge Requests (the GitLab equivalent of Pull Requests) are the primary unit of code review. Under…
GitLab Workflow
GitLab organizes work in Groups and Projects. Merge Requests (the GitLab equivalent of Pull Requests) are the primary unit of code review. Understanding protected branches, approval rules, and CODEOWNERS is essential for team workflows.
Groups, Projects & Repository Structure
# Groups: namespace for projects (like GitHub organizations)
# Subgroups: nested groups (e.g. company/backend/payments)
# Projects: individual repositories
# Clone a project
git clone git@gitlab.com:mygroup/myproject.git
git clone https://gitlab.com/mygroup/myproject.git
# GitLab uses "main" as default branch (older projects use "master")
# Rename local default branch to match
git branch -m master main
git push -u origin main
# Then update default branch in Settings → Repository → Default branch
# Repository mirroring — keep a GitLab repo in sync with GitHub
# Settings → Repository → Mirroring repositories
# Pull mirroring: GitLab pulls from GitHub on schedule or via trigger
# Push mirroring: GitLab pushes to another remote on each push
# Set up push mirror via API
curl --request POST --header "PRIVATE-TOKEN: <token>" "https://gitlab.com/api/v4/projects/:id/remote_mirrors" --data "url=https://github.com/user/repo.git&enabled=true&keep_divergent_refs=true"
Merge Requests
# Create an MR from the CLI (glab tool — see CLI page)
glab mr create --title "feat: add payment gateway" --description "Closes #123" --assignee @me --label "feature,backend" --target-branch main
# Draft MR — not ready for review (prefixes title with "Draft:")
glab mr create --draft --title "WIP: refactor auth"
# MR merge strategies (set in project Settings → Merge Requests):
# Merge commit: always creates merge commit (full history)
# Squash and merge: squashes all MR commits into one
# Fast-forward: linear history, no merge commit (fails if not rebased)
# Allow squash on merge: let author choose per MR
# Approval rules (Settings → Merge Requests → Approval rules)
# Require N approvals before merge
# Set "Code owner approval" to require CODEOWNERS to approve
# CODEOWNERS file — auto-assigns reviewers based on changed paths
# Place at: CODEOWNERS, docs/CODEOWNERS, or .gitlab/CODEOWNERS
# Syntax matches .gitignore patterns
# .gitlab/CODEOWNERS
[Backend]
src/api/ @backend-team @alice
src/db/ @alice @bob
[Frontend]
src/components/ @frontend-team
[DevOps]
*.yml @devops-team
Dockerfile @devops-team
terraform/ @devops-team
# Protected branches (Settings → Repository → Protected branches)
# Restrict who can push/merge to main/release/* branches
# "Allowed to merge": Maintainers
# "Allowed to push": No one (force merge requests)
# "Allowed to force push": disabled
Issues & Milestones
# Create issue from CLI
glab issue create --title "Bug: login fails on Safari" --description "Steps to reproduce: ..." --label "bug,high-priority" --milestone "v2.0"
# List open issues
glab issue list
glab issue list --label "bug" --milestone "v2.0"
# Close issue via commit message or MR description
# Supported keywords: Closes, Fixes, Resolves
git commit -m "fix: handle null session token
Closes #42"
# Cross-project reference
# Closes mygroup/other-project#15
# Issue boards — Kanban-style board linked to labels
# Epics (GitLab Premium) — group issues across milestones
# Milestones — track progress toward a goal
# Project milestones: per-project
# Group milestones: span multiple projects in a group
# Quick actions in issue/MR descriptions (no UI needed):
# /assign @alice — assign to user
# /label ~bug — add label
# /milestone %v2.0 — set milestone
# /close — close issue
# /spend 2h — log time (if time tracking enabled)
# /due 2024-06-30 — set due date
CI/CD Pipelines
GitLab CI/CD Pipelines GitLab CI/CD is configured entirely in .gitlab-ci.yml at the repo root. Pipelines run on Runners — lightweight agents that execute jobs. …
GitLab CI/CD Pipelines
GitLab CI/CD is configured entirely in .gitlab-ci.yml at the repo root. Pipelines run on Runners — lightweight agents that execute jobs. The declarative YAML syntax covers stages, dependencies, caching, and advanced conditional logic.
.gitlab-ci.yml Fundamentals
# .gitlab-ci.yml — minimal full-stack pipeline example
default:
image: node:20-alpine # Default Docker image for all jobs
before_script:
- npm ci --cache .npm --prefer-offline
cache:
key:
files:
- package-lock.json
paths:
- .npm/
stages:
- test
- build
- deploy
variables:
NODE_ENV: production
ARTIFACT_PATH: dist/
# --- Test stage ---
lint:
stage: test
script:
- npm run lint
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event" # Only on MRs
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # And on main
unit-tests:
stage: test
script:
- npm test -- --coverage
coverage: /All files[^|]*|[^|]*s+([d.]+)/ # Parse coverage % from output
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
expire_in: 1 week
# --- Build stage ---
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 hour
needs: ["lint", "unit-tests"] # Run as soon as dependencies pass (DAG)
# --- Deploy stage ---
deploy-staging:
stage: deploy
image: alpine
script:
- apk add --no-cache curl
- curl -X POST $DEPLOY_WEBHOOK_URL
environment:
name: staging
url: https://staging.example.com
needs: ["build"]
rules:
- if: $CI_COMMIT_BRANCH == "main"
deploy-production:
stage: deploy
script:
- ./deploy.sh production
environment:
name: production
url: https://example.com
when: manual # Requires manual trigger in GitLab UI
rules:
- if: $CI_COMMIT_TAG =~ /^vd+.d+.d+$/ # Only on semver tags
Runners & Docker Executor
# Runner types:
# Shared runners: GitLab-hosted; available to all projects; limited free minutes
# Group runners: registered to a group; available to all projects in it
# Project runners: registered to a single project
# Register a self-hosted runner (Docker executor)
# 1. 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
# 2. Register (get token from Settings → CI/CD → Runners)
sudo gitlab-runner register --url https://gitlab.com --registration-token <TOKEN> --executor docker --docker-image alpine:latest --description "docker-runner-prod" --tag-list "docker,linux" --run-untagged true --locked false
# 3. Start the runner
sudo gitlab-runner start
sudo gitlab-runner status
# Target a specific runner with tags
job-on-gpu:
tags:
- gpu
- docker
script:
- nvidia-smi
# Services (sidecar containers — e.g. test database)
integration-tests:
stage: test
services:
- name: postgres:15
alias: postgres
variables:
POSTGRES_DB: testdb
POSTGRES_USER: test
POSTGRES_PASSWORD: test
DATABASE_URL: postgresql://test:test@postgres:5432/testdb
script:
- npm run test:integration
Pipeline Triggers & Multi-Project Pipelines
# Trigger a downstream project pipeline from upstream
trigger-deploy:
stage: deploy
trigger:
project: mygroup/infrastructure
branch: main
strategy: depend # Wait for downstream pipeline to succeed
# Pass variables to downstream pipeline
trigger-with-vars:
stage: deploy
variables:
DEPLOY_ENV: staging
APP_VERSION: $CI_COMMIT_SHA
trigger:
project: mygroup/infrastructure
# Parent-child pipelines (split large pipelines into separate YAML files)
generate-child:
stage: build
trigger:
include:
- local: ci/frontend.yml
- local: ci/backend.yml
strategy: depend
# Trigger pipeline via API (e.g. from a webhook)
# curl -X POST # --form "token=<trigger-token>" # --form "ref=main" # --form "variables[DEPLOY_ENV]=production" # "https://gitlab.com/api/v4/projects/:id/trigger/pipeline"
# Useful CI/CD predefined variables:
# $CI_COMMIT_SHA — full commit SHA
# $CI_COMMIT_SHORT_SHA — first 8 chars of SHA
# $CI_COMMIT_BRANCH — branch name
# $CI_COMMIT_TAG — tag name (if triggered by a tag)
# $CI_PIPELINE_SOURCE — push / merge_request_event / schedule / trigger / web
# $CI_PROJECT_PATH — mygroup/myproject
# $CI_REGISTRY_IMAGE — container registry image path for this project
# $CI_ENVIRONMENT_NAME — name of the current environment
GitLab CLI & API
GitLab CLI & REST API The glab CLI brings GitLab into the terminal. The REST API enables automation, scripting, and integrations. Both use personal access token…
GitLab CLI & REST API
The glab CLI brings GitLab into the terminal. The REST API enables automation, scripting, and integrations. Both use personal access tokens or project/group tokens for authentication.
glab CLI
# Install glab
brew install glab # macOS
curl -sL https://raw.githubusercontent.com/cli/cli/main/scripts/bootstrap.sh | bash # Linux
# Authenticate
glab auth login # Interactive login
glab auth login --token $GITLAB_TOKEN # With PAT
# Merge Requests
glab mr create # Interactive MR creation
glab mr list # List open MRs
glab mr list --state merged --author @me
glab mr view 42 # View MR #42
glab mr checkout 42 # Checkout MR branch locally
glab mr approve 42
glab mr merge 42 --squash --delete-source-branch
# Issues
glab issue create --title "Bug: ..." --label "bug"
glab issue list --label "high-priority"
glab issue close 99
glab issue view 99
# Pipelines
glab pipeline list # List recent pipelines
glab pipeline view # View pipeline for current branch
glab pipeline run --ref main # Trigger pipeline
glab pipeline status # Status of latest pipeline
glab pipeline cancel <id>
glab pipeline retry <id>
# Job logs
glab pipeline ci view # Interactive pipeline trace
glab pipeline job trace <job-name> # Stream job logs
# Repo
glab repo clone mygroup/myproject
glab repo create --name new-project --group mygroup --visibility private
glab repo fork mygroup/myproject
REST API with curl
# Authentication: pass token in header or query param
# Header (preferred): --header "PRIVATE-TOKEN: <token>"
# Query param: ?private_token=<token>
GITLAB_TOKEN="glpat-xxxxxxxxxxxxxxxxxxxx"
PROJECT_ID="mygroup%2Fmyproject" # URL-encode the / as %2F (or use numeric ID)
BASE="https://gitlab.com/api/v4"
# Get project info
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "$BASE/projects/$PROJECT_ID"
# List merge requests
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "$BASE/projects/$PROJECT_ID/merge_requests?state=opened&per_page=20"
# Create an issue
curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" --header "Content-Type: application/json" --data '{"title": "Fix login bug", "labels": "bug,high-priority"}' "$BASE/projects/$PROJECT_ID/issues"
# Pagination — GitLab returns X-Next-Page, X-Total-Pages headers
# Fetch all pages with a loop:
page=1
while true; do
response=$(curl -sI --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "$BASE/projects/$PROJECT_ID/issues?per_page=100&page=$page")
next=$(echo "$response" | grep -i "x-next-page:" | tr -d "[:space:]" | cut -d: -f2)
[ -z "$next" ] && break
page=$next
done
# Trigger pipeline
curl --request POST --form "token=<trigger-token>" --form "ref=main" "$BASE/projects/$PROJECT_ID/trigger/pipeline"
# Get pipeline jobs
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "$BASE/projects/$PROJECT_ID/pipelines/12345/jobs"
Access Tokens & Webhooks
# Token types:
# Personal Access Token (PAT): tied to a user; use for personal automation
# Project Access Token: scoped to a project; rotate without affecting user
# Group Access Token: scoped to a group and all subgroups/projects
# Create a project access token (API)
curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" --data "name=ci-bot&scopes[]=api&scopes[]=read_repository&expires_at=2025-01-01" "$BASE/projects/$PROJECT_ID/access_tokens"
# Webhooks — GitLab POSTs JSON to your URL on events
# Events: Push, Tag Push, Merge Request, Issue, Pipeline, Deployment, etc.
# Create webhook via API
curl --request POST --header "PRIVATE-TOKEN: $GITLAB_TOKEN" --header "Content-Type: application/json" --data '{
"url": "https://myapp.example.com/webhooks/gitlab",
"push_events": true,
"merge_requests_events": true,
"pipeline_events": true,
"token": "my-secret-webhook-token"
}' "$BASE/projects/$PROJECT_ID/hooks"
# Verify webhook authenticity (in your handler)
# GitLab sends X-Gitlab-Token header matching the token you set above
# List webhooks
curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" "$BASE/projects/$PROJECT_ID/hooks"
GitLab vs GitHub & DevSecOps
GitLab vs GitHub & DevSecOps GitLab is a complete DevSecOps platform — SCM, CI/CD, security scanning, package registry, and deployment environments are all buil…
GitLab vs GitHub & DevSecOps
GitLab is a complete DevSecOps platform — SCM, CI/CD, security scanning, package registry, and deployment environments are all built-in. GitHub relies on the marketplace and Actions integrations for the same coverage.
Feature Comparison
# Feature GitLab GitHub
# ─────────────────────────────────────────────────────────────────────
# CI/CD Built-in (.gitlab-ci.yml) Actions (.github/workflows)
# Container Registry Built-in (free) Packages (GHCR, free)
# Package Registry Built-in (npm/pip/maven) Packages
# SAST Built-in (free tier) CodeQL (Actions)
# DAST Built-in (Ultimate tier) 3rd party via Actions
# Dependency Scanning Built-in Dependabot
# Secret Detection Built-in Secret scanning
# Pages (static hosting) GitLab Pages GitHub Pages
# Environments Built-in deployments UI Environments API
# Self-hosting GitLab CE/EE GitHub Enterprise Server
# Issue boards Built-in Kanban Projects (beta)
# Epics / Roadmap Premium+ Projects
# Group-level CI variables Yes Organization secrets
# Merge trains Yes (Premium) No native equivalent
# GitLab unique strengths:
# - Everything in one platform (no marketplace required for basics)
# - Stronger self-hosting story (GitLab CE is fully open source)
# - Merge trains: queue MRs and test in combined state before merging
# - Built-in Kubernetes deployment / agent (GitLab Agent for Kubernetes)
# - Value stream analytics, DORA metrics dashboards
# GitHub unique strengths:
# - Largest developer community / open source ecosystem
# - GitHub Copilot (AI coding assistant, deeply integrated)
# - GitHub Actions marketplace (50k+ actions)
# - Codespaces (cloud dev environments)
Security Scanning & Container Registry
# Include GitLab security templates in .gitlab-ci.yml
include:
- template: Security/SAST.gitlab-ci.yml
- template: Security/Secret-Detection.gitlab-ci.yml
- template: Security/Dependency-Scanning.gitlab-ci.yml
- template: Security/Container-Scanning.gitlab-ci.yml # requires image artifact
# These templates auto-detect language and run appropriate scanners
# Results appear in the Security tab of the MR and pipeline
# Container Registry — build and push Docker image
build-image:
stage: build
image: docker:24
services:
- docker:24-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
before_script:
- docker login -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD" $CI_REGISTRY
script:
- docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA" .
- docker tag "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA" "$CI_REGISTRY_IMAGE:latest"
- docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
- docker push "$CI_REGISTRY_IMAGE:latest"
# Pull in deploy job
deploy:
image: "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"
script:
- ./run-deploy.sh
Environments, Releases & GitLab Pages
# Environments — track deployments per environment in the UI
# Deployments → Environments shows current version per env + history
deploy-staging:
script:
- ./deploy.sh staging
environment:
name: staging
url: https://staging.example.com
on_stop: stop-staging # Optional: job to tear down env
stop-staging:
script:
- ./teardown.sh staging
environment:
name: staging
action: stop
when: manual
# Create a Release (links Git tag to release notes + artifacts)
release-job:
stage: release
image: registry.gitlab.com/gitlab-org/release-cli:latest
rules:
- if: $CI_COMMIT_TAG
script:
- echo "Creating release for $CI_COMMIT_TAG"
release:
name: "Release $CI_COMMIT_TAG"
description: "./CHANGELOG.md"
tag_name: "$CI_COMMIT_TAG"
assets:
links:
- name: "Binary"
url: "https://example.com/releases/$CI_COMMIT_TAG/app"
# GitLab Pages — host static sites from CI artifacts
# Artifact must be in a job named "pages" and published to "public/" directory
pages:
stage: deploy
script:
- npm run build -- --outDir public
artifacts:
paths:
- public
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
# Site available at: https://<namespace>.gitlab.io/<project>/
Repositories, Branches & Merge Requests
GitLab: Repositories, Branches & Merge Requests GitLab is a complete DevSecOps platform — combining Git hosting, CI/CD, issue tracking, container registry, and …
GitLab: Repositories, Branches & Merge Requests
GitLab is a complete DevSecOps platform — combining Git hosting, CI/CD, issue tracking, container registry, and security scanning in one application. Available as SaaS (gitlab.com) or self-managed.
Repository Basics
# Clone via HTTPS or SSH
git clone https://gitlab.com/group/project.git
git clone git@gitlab.com:group/project.git
# GitLab CLI
glab auth login
glab repo clone group/project
# Protected branches — set in Settings → Repository → Protected Branches
# Main/master: typically locked to Maintainers for push, Developers for merge
Branch Strategy
GitLab Flow: feature branches → main → production (add environment branches like staging)
Trunk-based development: short-lived feature branches, merge daily, use feature flags
Protected branches: prevent force-push, require approvals before merge
Default branch: configurable per project (Settings → Repository → Default branch)
Branch naming convention: feature/, fix/, chore/ prefixes recommended
Merge Requests (MRs)
Merge Requests in GitLab are equivalent to GitHub Pull Requests. They are the primary unit of code review.
# Create MR via CLI
glab mr create --title "Add payment flow" --description "Implements Stripe checkout" --target-branch main
# List open MRs
glab mr list
# Check out MR locally
glab mr checkout 42
# Approve and merge
glab mr approve 42
glab mr merge 42
MR Settings & Best Practices
Draft MRs: prefix title with "Draft:" to prevent accidental merge while WIP
Assignee: the person responsible for merging; Reviewer: person doing the code review
Approvals: configure required approvals in Settings → Merge Requests → Approval rules
Squash commits: enabled per MR or as project default — keeps main branch history clean
Delete source branch: check "Delete source branch when merge request is accepted"
Rebase on merge: keeps linear history without merge commits
Merge trains: queue MRs that pipeline-test against each other before merging
Code Review Features
Inline comments: click the diff gutter to add a comment on a specific line
Suggestions: click the "Insert suggestion" icon to propose a code change inline — author can apply with one click
Threads: group related comments into a thread; resolve when addressed
Review summary: submit all comments at once instead of notifying per comment
MR dependencies: block an MR from merging until another MR is merged first
Reviewers can filter by file, diff type (inline/side-by-side), or whitespace
Forks & Contributions
Fork: copies the project to your namespace — use for open-source contributions
Fork sync: GitLab can sync forks automatically with the upstream project
Contribution limits: project owners can set who can create branches and MRs
Merge request from fork: create MR targeting upstream repository directly
GitLab CI/CD Pipelines
GitLab CI/CD Pipelines GitLab CI/CD is defined in .gitlab-ci.yml at the root of the repository. It is one of the most feature-complete CI/CD systems available, …
GitLab CI/CD Pipelines
GitLab CI/CD is defined in .gitlab-ci.yml at the root of the repository. It is one of the most feature-complete CI/CD systems available, with DAG pipelines, environments, review apps, and artifact management built in.
.gitlab-ci.yml Anatomy
stages:
- build
- test
- deploy
variables:
NODE_ENV: production
default:
image: node:20
before_script:
- npm ci
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 hour
test:
stage: test
script:
- npm test
coverage: '/Statements.*?([d.]+)%/'
deploy_staging:
stage: deploy
script:
- ./deploy.sh staging
environment:
name: staging
url: https://staging.example.com
only:
- main
Job Keywords
# Run on specific branches/tags
only:
- main
- /^release-.*/
# Modern alternative: rules (more flexible)
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: manual
# Dependencies between jobs
needs: # DAG — run as soon as dependency is done (skip stage order)
- job: build
dependencies: # Download artifacts from specific jobs
- build
# Retry on failure
retry:
max: 2
when: runner_system_failure
# Timeout per job
timeout: 30 minutes
Pipeline Types
Branch pipelines: run on every push to a branch
Merge request pipelines: run when MR is created/updated (source: merge_request_event)
Merged results pipelines: test MR as if already merged with target branch
Merge trains: pipeline tests queued MRs sequentially before merging
Scheduled pipelines: cron-based, configured in CI/CD → Schedules
Triggered pipelines: via API, web hook, or another pipeline (trigger keyword)
Parent-child pipelines: trigger: include — split huge .gitlab-ci.yml across files
Runners
# GitLab-hosted runners: available on gitlab.com (SaaS), use tags to target
# Self-managed: install runner on your own infrastructure
# Register a runner
gitlab-runner register --url https://gitlab.com --token <token> --executor docker --docker-image alpine
# Runner tags — match runner to jobs
build_app:
tags:
- docker
- linux
Artifacts & Caching
# Artifacts: pass files between jobs or download after pipeline
artifacts:
paths:
- build/
reports:
junit: test-results.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
expire_in: 7 days
# Cache: speed up jobs by reusing node_modules, pip packages, etc.
cache:
key:
files:
- package-lock.json
paths:
- node_modules/
Environments & Review Apps
Environments track deployments — visible in Deployments → Environments
Review Apps: auto-create a temporary environment for every MR (dynamic environments)
Stop review app: define a stop job with action: stop and trigger on MR close
Protected environments: only specific roles can deploy to production
Deployment freezes: CI/CD → Deployment freezes — block deploys during maintenance windows
Predefined CI Variables
$CI_COMMIT_SHA # full commit SHA
$CI_COMMIT_SHORT_SHA # first 8 chars
$CI_COMMIT_BRANCH # branch name
$CI_COMMIT_TAG # tag name (if pipeline is for a tag)
$CI_PIPELINE_ID # unique pipeline ID
$CI_JOB_NAME # current job name
$CI_PROJECT_ID # project numeric ID
$CI_REGISTRY # GitLab container registry URL
$CI_REGISTRY_IMAGE # full image path
$CI_ENVIRONMENT_NAME # set when job has environment:
Issues, Boards & Project Planning
GitLab: Issues, Boards & Project Planning Issues GitLab Issues are the primary unit of work. They support labels, milestones, weight, due dates, assignees, and …
GitLab: Issues, Boards & Project Planning
Issues
GitLab Issues are the primary unit of work. They support labels, milestones, weight, due dates, assignees, and time tracking.
# Create issue via CLI
glab issue create --title "Bug: login fails on Safari" --label bug --assignee @me
# List issues
glab issue list --label "backend" --assignee @me
# Close issue
glab issue close 42
Issue Features
Labels: colored tags for categorization (Priority::High, Type::Bug, Stage::In Review)
Milestones: group issues and MRs by sprint, version, or goal with a due date
Iterations: time-boxed development cycles (like sprints) — available in groups
Weight: numeric effort estimation (1-5 Fibonacci, or custom)
Time tracking: /spend 2h to log time, /estimate 5h to set estimate
Linked issues: mark as blocks / is blocked by / relates to / clones
Confidential issues: hide from non-members (for security vulnerabilities)
Service Desk: receive external emails as issues (support ticket system built in)
Issue Boards
Kanban-style boards that visualize issues by label or assignee. Multiple boards per project or group.
List types: label-based (most common), assignee-based, or milestone-based
Scoped labels: label::value where only one value per scope is allowed (Status::Open, Status::Closed)
Board filters: filter by milestone, label, assignee, weight — saved per board
Epic swimlanes: group issues by epic on the board (Premium/Ultimate)
Drag & drop: move issues between columns to apply/remove label
WIP limits: set maximum issues per column to prevent overload
Epics & Roadmaps (Premium)
Epics: group related issues across milestones — create at group level
Child epics: nest epics for hierarchical planning (Theme → Epic → Story → Task)
Roadmap: timeline view of all epics in a group, with start/end dates
Epic health status: On Track / Needs Attention / At Risk — set manually or inherit from issues
Burndown/burnup charts: visualize progress against milestone or iteration
Quick Actions
In issue/MR description or comments, use slash commands:
/assign @username assign to user
/assign me assign to yourself
/label ~bug ~"high priority"
/milestone %"v2.0"
/weight 3
/estimate 4h
/spend 2h 30m
/due 2026-06-01
/close
/reopen
/confidential
/copy_metadata #42 copy labels and milestone from another issue
/relate #56 link as "relates to"
/blocks #57
/todo add to your To-Do list
Notifications & To-Do List
To-Do list: auto-created when you are mentioned, assigned, or reviewed — top bar bell icon
Notification levels: Global, Group, Project — customize per project (Watch/Participate/Mention/Disabled)
Email notifications: sent for all activity matching your notification level
Todos cleared when: you take action (comment, approve, close issue)
Security, Container Registry & Environments
GitLab: Security, Container Registry & Environments GitLab Security Scanning GitLab Ultimate includes a full suite of security scanners that run as CI jobs and …
GitLab: Security, Container Registry & Environments
GitLab Security Scanning
GitLab Ultimate includes a full suite of security scanners that run as CI jobs and report findings in the MR Security tab.
# Enable all security scans by including GitLab templates
include:
- template: Security/SAST.gitlab-ci.yml
- template: Security/Dependency-Scanning.gitlab-ci.yml
- template: Security/Container-Scanning.gitlab-ci.yml
- template: Security/DAST.gitlab-ci.yml
- template: Security/Secret-Detection.gitlab-ci.yml
Security Scanner Types
SAST (Static Application Security Testing): analyze source code for vulnerabilities (SQL injection, XSS, etc.)
Dependency Scanning: detect vulnerable packages in package.json, Gemfile, requirements.txt, etc.
Container Scanning: scan Docker images for CVEs using Trivy or Grype
DAST (Dynamic Application Security Testing): run against a live URL — finds runtime vulnerabilities
Secret Detection: scan commits for accidentally committed secrets (API keys, tokens)
Coverage-guided fuzzing: run AI-powered fuzz testing against your functions
License Compliance: detect licenses in dependencies — block unapproved licenses
Vulnerability Dashboard
Security → Vulnerability Report: all detected vulnerabilities per project
Status: Detected → Confirmed → Resolved (or Dismissed with reason)
Dismiss: mark as "Not a problem", "Used in tests", "Mitigating control" with comment
Create issue from vulnerability: one-click issue creation with full context
Security policies: enforce scan thresholds — fail MR if new critical CVE detected
Container Registry
# GitLab provides a built-in container registry per project
# Login
docker login registry.gitlab.com
# Build and push
docker build -t registry.gitlab.com/group/project:latest .
docker push registry.gitlab.com/group/project:latest
# In CI/CD — use predefined variables
build_image:
image: docker:24
services:
- docker:24-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
Registry Management
Cleanup policies: auto-delete old images by tag pattern and age (Settings → Packages & Registries)
Protected tags: prevent deletion of specific tags (e.g., latest, v*)
Pull-through cache: proxy requests to Docker Hub or other registries through GitLab
Dependency proxy: cache frequently used upstream images (node:20, alpine) in your group
CI/CD Secrets & Variables
# Stored in Settings → CI/CD → Variables
# Types:
# Variable — plain text
# File — written to a temp file, $VAR_NAME = path to file
# Protect variables: only exposed to protected branches/tags
# Mask variables: hidden from job logs
# Access in .gitlab-ci.yml
deploy:
script:
- echo "$DATABASE_URL" # direct use
- cat "$SERVICE_ACCOUNT_JSON" # file-type variable
Access Tokens & OAuth
Personal Access Tokens: Settings → Access Tokens — scopes: api, read_api, read_user, write_repository
Project Access Tokens: scoped to a single project — use for CI bots
Group Access Tokens: scoped to a group and all its subgroups/projects
Deploy tokens: limited token for pulling container images or packages in deployment
OAuth applications: register an app to use GitLab as an OAuth provider
Token expiry: enforce expiry dates, rotate regularly
GitLab Pages, Packages & Interview Questions
GitLab Pages, Packages & Interview Questions GitLab Pages Deploy static websites directly from a GitLab project using CI/CD. Free for all plans on gitlab.com. #…
GitLab Pages, Packages & Interview Questions
GitLab Pages
Deploy static websites directly from a GitLab project using CI/CD. Free for all plans on gitlab.com.
# .gitlab-ci.yml for a static site (e.g., Hugo, Jekyll, plain HTML)
pages:
stage: deploy
script:
- apt-get install -y hugo
- hugo --minify
artifacts:
paths:
- public # must be named "public"
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
Published at: https://<namespace>.gitlab.io/<project> (or custom domain)
Custom domain: Settings → Pages → Add domain + DNS CNAME
HTTPS: auto-provisioned via Let's Encrypt
Access control: restrict Pages to authenticated GitLab members (Ultimate)
Redirect rules: _redirects file (Netlify-compatible format)
Package Registry
GitLab Package Registry supports npm, PyPI, Maven, Go modules, Composer, Conan, Helm charts, and more — all hosted inside your project or group.
# Publish npm package to GitLab registry
# .npmrc
@mygroup:registry=https://gitlab.com/api/v4/packages/npm/
//gitlab.com/api/v4/packages/npm/:_authToken=${CI_JOB_TOKEN}
# Publish in CI
npm publish --registry https://gitlab.com/api/v4/projects/${CI_PROJECT_ID}/packages/npm/
# Publish PyPI package
pip install twine
twine upload --repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi dist/*
GitLab CLI (glab)
# Install: brew install glab
glab auth login
# Repos
glab repo clone group/project
glab repo create
# MRs
glab mr create
glab mr list --state=opened
glab mr checkout 42
glab mr approve 42
glab mr merge 42 --squash
# Issues
glab issue create --title "..." --label bug
glab issue list
glab issue close 42
# Pipelines
glab pipeline list
glab pipeline status
glab pipeline retry 99
glab ci view # TUI pipeline viewer
GitLab vs GitHub — Key Differences
CI/CD: GitLab CI is built-in and more configurable; GitHub Actions requires YAML workflows but has larger marketplace
Merge Requests vs Pull Requests: functionally identical, different name
Self-managed: GitLab has a robust CE/EE self-hosted option; GitHub has GitHub Enterprise Server
Security: GitLab bundles SAST, DAST, dependency scanning at Ultimate; GitHub Advanced Security is add-on
Pipelines: GitLab has DAG pipelines (needs:), parent-child, merge trains natively; GitHub Actions has matrix but no native DAG
Package registry: both have one; GitLab supports more package formats natively
Project management: GitLab has epics, roadmaps, iterations built in; GitHub has Projects (kanban) + Milestones
Interview Questions
What is a merge train and when would you use one? — Queue of MRs tested together before merging, prevents broken builds on main
How do you prevent a secret from leaking in CI logs? — Use masked CI variables; never echo secrets; use file-type variables for certs
Difference between cache and artifacts in GitLab CI? — Cache speeds up jobs (node_modules); Artifacts pass files between jobs or download after pipeline
How would you implement branch-based environment deployments? — Dynamic environments with review apps, stop action on MR close
What is a protected branch vs protected environment? — Branch: controls who can push/merge; Environment: controls who can deploy there
How do you handle database migrations in GitLab CI? — Separate migration job in deploy stage, run before main app deploy, rollback strategy
What is the difference between only/except and rules? — rules is more flexible, supports complex conditions (if/when/changes/exists); only/except is older and simpler
How would you structure a monorepo CI pipeline? — parent-child pipelines with trigger: include, use changes: to only run affected child pipelines