All topics
General · Learning hub

GitLab notes for developers

Master GitLab with a curated set of 9 developer notes — core concepts, patterns, and interview prep. Maintained by the DevRecall team.

Save this stack to your DevRecallMore General notes
GitLab

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
GitLab

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

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

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>/
GitLab

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

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:
GitLab

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)

GitLab

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

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

Keep your GitLab knowledge sharp.

Save this stack to your personal DevRecall — add your own notes, track what you're learning, and share what you know with the community.

Get started — free forever