Understanding GitLab CI
What Is GitLab CI in Simple Terms
GitLab CI is the CI/CD system built into GitLab. Like GitHub Actions for GitHub, it requires no external tools — define your pipeline in a YAML file, push it, and GitLab runs it. It is particularly strong at environment tracking (showing which version is deployed where across all your environments) and merge request integration (pipeline status shown inline on every MR).
Organisations running self-hosted GitLab for compliance — common in Indian financial services and regulated industries — use GitLab CI because it stays entirely within their infrastructure.
How It Works
+------------------------------------------+| .gitlab-ci.yml in repository root |+------------------------------------------+ | push or MR event | v+------------------------------------------+| GitLab pipeline created || Jobs assigned to stages || Dependency graph (DAG) resolved |+------------------------------------------+ | v+------------------------------------------+| Runner picks up job from queue || || Shared runner (GitLab SaaS) || OR || Specific runner (your infrastructure) |+------------------------------------------+ | v+------------------------------------------+| Job executes in Docker container || Results: artifacts, reports, logs || Environment deployment tracked |+------------------------------------------+Complete .gitlab-ci.yml:
## .gitlab-ci.yml ## Define the order of stagesstages: - build - test - scan - deploy ## Variables available to all jobsvariables: IMAGE_NAME: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA DOCKER_DRIVER: overlay2 ## Reusable configuration with YAML anchors.node-base: &node-base image: node:20-alpine cache: key: $CI_COMMIT_REF_SLUG paths: - node_modules/ build-image: stage: build image: docker:24 services: - docker:24-dind script: - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY - docker build -t $IMAGE_NAME . - docker push $IMAGE_NAME unit-tests: stage: test <<: *node-base ## merge node-base config script: - npm ci - npm test -- --coverage --reporter=junit ## Publish JUnit test results to GitLab artifacts: reports: junit: junit.xml paths: - coverage/ expire_in: 1 week security-scan: stage: scan image: name: aquasec/trivy:latest entrypoint: [""] script: - trivy image --exit-code 1 --severity HIGH,CRITICAL $IMAGE_NAME deploy-staging: stage: deploy script: - kubectl set image deployment/payment-api payment-api=$IMAGE_NAME -n staging environment: name: staging url: https://staging.payment.internal ## Rules control when this job runs rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH deploy-production: stage: deploy script: - kubectl set image deployment/payment-api payment-api=$IMAGE_NAME -n production environment: name: production url: https://api.payment.razorpay.com ## Manual trigger only on main branch rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when: manual needs: - deploy-staging ## must deploy to staging firstPractical Commands
## GitLab CLI -- view pipeline statusglab ci viewglab ci status ## View specific job logsglab ci trace JOB_ID ## Retry a failed jobglab ci retry JOB_ID ## Cancel a pipelineglab ci cancel PIPELINE_ID ## Trigger a pipeline via APIcurl -X POST \ -H "PRIVATE-TOKEN: $GITLAB_TOKEN" \ "https://gitlab.com/api/v4/projects/PROJECT_ID/pipeline" \ -d "ref=main"Troubleshooting
| Symptom | Check | What to Look For |
|---|---|---|
| Job stuck pending | Runner availability | No runners with matching tags |
| YAML syntax error | CI Lint tool in GitLab UI | Indentation and key names |
| Rules not matching | rules:if conditions | Variable names and values |
| Cache not working | Cache key and paths | Key must be consistent across runs |
PLACEMENT PRO TIP**Tip:** Use GitLab's built-in CI Lint tool (Settings > CI/CD > CI Lint) to validate your .gitlab-ci.yml before pushing. It catches YAML syntax errors, invalid keywords, and rule logic errors — much faster than push-and-see.
REMEMBER THIS**Remember:** GitLab CI predefined variables are your friends. `$CI_COMMIT_SHA` is the full commit hash. `$CI_COMMIT_SHORT_SHA` is the short version. `$CI_REGISTRY_IMAGE` is the full path to your project's container registry. `$CI_ENVIRONMENT_NAME` is the name of the environment being deployed to. Use these instead of hardcoding values.
COMMON MISTAKE / WARNING**Security:** Protect your production environment in GitLab (Settings > CI/CD > Environments > Edit > Protected). Protected environments restrict which branches can deploy and which users can manually trigger deployments — preventing a developer branch from accidentally deploying to production.
COMMON MISTAKE / WARNING**Common Mistake:** Using `when: always` on deployment jobs. This deploys on every pipeline run regardless of branch, test results, or manual conditions. Use `rules:` blocks to explicitly define when deployment jobs should run — they should only run on specific branches (main, release/*) and after required stages pass.