GitLab CI/CD Associate study materials
GitLab CI/CD Associate study resources

GitLab CI/CD Associate: Variables, Secrets, and Protected Configuration

GitLab CI/CD Associate · Work through your exam, one topic at a time.

All guide sections

Variables, Secrets, and Protected Configuration

This domain covers where variable values come from, which one wins when names collide, and how secrets are restricted to trusted refs.

Precedence

Highest to lowest: trigger, manual, and scheduled pipeline variables → project variables → group variables → instance variables → job-level variables: → global variables: → deployment variables → predefined variables. Because predefined variables come last, a job-level variables: CI_COMMIT_BRANCH: main overrides the real branch - a classic self-inflicted bug.

Values are strings; quote booleans and nulls (VAR: "false"). $VAR expansion happens in variables: values and in script, but a variable cannot reference itself. Inside script, use $$ to emit a literal $ that GitLab would otherwise try to expand.

Protected, masked, and scoped

  • Protected variables are injected only into pipelines on protected branches or tags, and only when the job runs on a protected runner. On a feature branch the variable is simply absent (empty string), which surfaces as an auth failure rather than a clear error.
  • Masked variables keep their value out of job logs. Requirements: at least 8 characters, one line, no whitespace, and a base64/hex-compatible character set.
  • File variables are written to a temporary file; the variable holds the path, which is what tools expecting a credential file need.
  • artifacts:reports:dotenv passes variables from one job to downstream jobs in the same pipeline.

Worked example

Project variable API_TOKEN is protected and set to abc123. A merge request pipeline runs on a feature branch and executes curl -H "PRIVATE-TOKEN: $API_TOKEN". The request returns 401. Reasoning: the branch is not protected, so GitLab never injected the variable; $API_TOKEN expanded to an empty string and an empty header was sent. Fix it by protecting the branch, or by avoiding protected variables in MR pipelines - for example exchanging an OIDC id_tokens JWT for a short-lived secret.

Secrets

secrets: with vault: retrieves secrets at job runtime, authenticated by a JWT from id_tokens rather than a stored credential. CI_JOB_TOKEN is scoped to the job and pipeline and is the correct credential for the container and package registries.

Traps

  • Assuming a protected variable is "empty by mistake"; check the protection status of both the branch and the runner.
  • Trying to mask a short or multi-line secret.
  • Expecting export FOO=bar inside one job's script to reach another job.