What are GitLab CI/CD variables and how do you use them in pipelines?
Quick Answer
GitLab CI/CD variables are key-value pairs that inject configuration, secrets, and dynamic values into pipeline jobs. They can be defined at the instance, group, project, or pipeline level, and GitLab also provides dozens of predefined variables like CI_COMMIT_SHA and CI_PIPELINE_ID that give jobs context about the current pipeline execution.
Detailed Answer
Think of CI/CD variables like the settings panel on a dishwasher. Instead of hardcoding the water temperature and cycle length into the machine's circuit board, the manufacturer exposes dials and buttons (variables) that let you adjust behavior without rewiring anything. You can set defaults (project-level variables), override them for a special load (pipeline-level variables), and some settings come preset from the factory (predefined variables like CI_COMMIT_SHA). Secrets like the rinse-aid dosage are hidden behind a cover (masked variables) so guests cannot see them.
CI/CD variables in GitLab are key-value pairs available as environment variables inside every job's execution environment. They are defined at multiple levels with a clear precedence hierarchy: pipeline-level variables (set when manually triggering a pipeline) override project-level variables, which override group-level variables, which override instance-level variables. Within the .gitlab-ci.yml file, you can define variables at the global level (applying to all jobs) or at the job level (applying only to that job). Job-level variables override global-level variables of the same name. Variables defined in the GitLab UI under Settings > CI/CD > Variables are injected into every pipeline run and are the recommended location for secrets because they are stored encrypted in the database and never appear in the .gitlab-ci.yml file, which is committed to the repository.
GitLab provides a rich set of predefined variables that are automatically available in every job. CI_COMMIT_SHA contains the full 40-character commit hash, CI_COMMIT_SHORT_SHA is the first eight characters, CI_COMMIT_BRANCH holds the branch name, CI_PIPELINE_ID is the unique pipeline identifier, CI_PROJECT_NAME is the project name, CI_REGISTRY_IMAGE is the path to the project's container registry, and CI_MERGE_REQUEST_IID is the merge request number (available in merge request pipelines). These predefined variables eliminate the need for custom scripts to extract Git information and ensure consistency across all jobs. Variables can also be referenced within other variable definitions using the $VARIABLE_NAME syntax, enabling composition like IMAGE_TAG set to $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA.
In production, variables are critical for managing environment-specific configuration and secrets. A deployment pipeline for logistics-tracker might use project-level variables like KUBE_CONFIG (the base64-encoded kubeconfig for the target cluster), DOCKER_REGISTRY_PASSWORD (credentials for pushing images), and SENTRY_DSN (the error tracking endpoint). These are marked as masked (so their values never appear in job logs) and optionally protected (so they are only available in pipelines running on protected branches like main, preventing feature branch pipelines from accessing production secrets). Environment-scoped variables take this further: you can set DATABASE_URL to different values for the staging and production environments, and GitLab automatically injects the correct value based on which environment the job is deploying to. This eliminates the need for environment-specific configuration files in the repository.
A critical gotcha is accidentally exposing secrets in job logs. Even if a variable is marked as masked, GitLab can only mask exact matches of the variable value in the log output. If a script transforms the variable (for example, base64-encoding it or splitting it into parts), the masked value will not match the transformed output and the secret will appear in the logs. Always audit your pipeline logs after adding new secret variables. Another common mistake is defining secrets directly in .gitlab-ci.yml instead of using the GitLab UI, which means the secrets are committed to the repository history and visible to anyone with read access. Never put actual credentials in your pipeline file; use variable references instead.