Chapter 13Lesson 01~220 minutes

CI/CD Variables, Inputs, Secrets, File Variables, Masking, Protection, and Scope: Concepts, Architecture, and Mental Model

Build a safe mental model for GitLab CI/CD variables, typed inputs, precedence, inheritance, masking, hidden/protected/file variables, expansion, external secret retrieval, and the runner trust boundary.

CI/CD variablesInputsPrecedenceMaskingProtectionFile variables

Learning objectives

  • Distinguish runtime CI/CD variables from typed configuration inputs and explain when each is appropriate.
  • Reason about project, group, instance, YAML, pipeline, dotenv, deployment, and predefined variable precedence without guessing.
  • Explain masked, hidden, protected, environment-scoped, and file variables as separate controls with separate failure modes.
  • Explain why masking is a log-redaction aid rather than a complete secret-management boundary.
  • Describe how OIDC/ID-token based retrieval can reduce dependence on long-lived stored credentials.
Availability baseline (verified 2026-08-21; GitLab 19.3 is the latest monthly release, released 2026-08-20). Project and group CI/CD variables, masking, hiding, protection, file variables, pipeline inputs, predefined variables, and OIDC ID tokens are available on Free/Premium/Ultimate across GitLab.com, Self-Managed, and Dedicated unless a narrower note is stated. Instance variables are Self-Managed/Dedicated administrator scope. Group-variable environment scoping is Premium/Ultimate. GitLab's built-in external-secret provider integrations are Premium/Ultimate, while the underlying ID-token/OIDC mechanism is Free. The mandatory chapter path uses only a disposable project, synthetic values, and a no-paid validation/fixture fallback.

1. The problem: a correct job can still receive the wrong data

Chapters 10–12 established when a pipeline exists, which jobs exist, and how those jobs execute. Chapter 13 asks the next security question: what configuration and credentials does each job receive, from where, and under whose authority?

A variable name such as DEPLOY_TARGET looks simple, but its effective value can come from several layers. A credential can be masked yet still be available to malicious pipeline code. A protected value can disappear on an unprotected ref by design. A file variable is not the secret text itself—it is a path to a temporary file. These distinctions are part of the execution model.

2. Mental model: configuration contract → resolved values → runner environment

Data and trust flow
flowchart TD
  I[Pipeline/component inputs] --> C[Configuration creation]
  Y[.gitlab-ci.yml variables] --> R[Variable resolution]
  G[Group / subgroup variables] --> R
  P[Project variables] --> R
  X[Pipeline variables / policy variables] --> R
  R --> A{Ref / environment / protection filters}
  A --> J[Job variable set]
  J --> U[Runner execution environment]
  S[External secret provider] -->|explicit job request / OIDC| U
  U --> L[Logs / commands / artifacts]

Inputs primarily shape configuration at pipeline creation. Variables become runtime values for jobs. GitLab resolves competing variable definitions according to precedence, then applies scoping/protection rules. The runner finally exposes the permitted values to the job. External secrets are different again: the job explicitly requests them, ideally through a short-lived identity token.

3. Inputs and variables solve different problems

Mechanism Primary purpose When evaluated / used Security posture
spec:inputs / pipeline inputs Typed, validated configuration parameters. Interpolated when GitLab creates/fetches configuration. Prefer for user-selectable pipeline configuration; options/regex/type create an explicit contract.
YAML variables Non-sensitive runtime configuration declared with code. Made available to jobs, subject to precedence. Repository-readable; do not store secrets here.
Project/group/instance variables Runtime configuration or, when unavoidable, protected secret-like values. Resolved into eligible jobs. Can be masked/hidden/protected/file type; still exposed to pipeline code that receives them.
External secret retrieval Fetch sensitive data only when a job needs it. Explicit job request, often after OIDC authentication. Better separation and shorter-lived identity; provider policy remains critical.

GitLab recommends pipeline inputs over ad-hoc pipeline variables for parameters supplied when a pipeline starts. Inputs support types, defaults, allowed options, regex validation, and a visible contract. They are not a vault and must not be used as a place to type secrets into source-controlled configuration.

4. Variable precedence: “closest in YAML” is not the rule

When the same key exists in multiple places, GitLab uses a defined precedence order. The important operational habit is to trace the key through every possible source before editing anything.

Higher → lower precedence Typical source Governance implication
1 Pipeline execution policy variables Security policy can intentionally dominate ordinary project configuration.
2 Scan execution policy variables Security orchestration may override lower sources.
3 Pipeline variables: manual/API/trigger/schedule/downstream/manual-job Powerful because they override project/group/YAML values; restrict who may set them.
4 Project variables Override inherited group and YAML job/default variables.
5 Group/subgroup variables Closest subgroup wins among group levels; still overrides YAML job/default values.
6 Instance variables Self-Managed/Dedicated administrator-defined defaults.
7 Dotenv report variables Values imported from a prior job report.
8 Job variables in YAML Override top-level YAML defaults, but not project/group variables.
9 Top-level YAML default variables Repository-defined defaults for jobs.
10 Deployment variables Environment/deployment-provided values.
11 Predefined variables GitLab-provided context such as project, SHA, pipeline, and job identifiers.

Example: if CH13_MODE is job in a job, group at the parent group, and project in project settings, the effective value is project. If the project value is removed, the group value beats the YAML job value.

5. Inheritance is convenient—and expands blast radius

Project variables affect one project. Group variables are inherited by projects in the group, and subgroup variables recursively participate in the same model. If the same key exists at multiple group depths, the closest subgroup value wins. This makes centralized configuration easy, but a broad group secret can become available to far more pipeline code than its owner intended.

Instance variables are an administrator surface for Self-Managed/Dedicated and can affect all projects/groups on that instance. Treat them as platform configuration, not as a substitute for project-specific secret governance.

6. Masked, hidden, protected, environment-scoped, and file are orthogonal properties

Property What it controls What it does NOT guarantee
Masked Redacts exact matching values in ordinary job trace output as [MASKED]. Does not stop malicious code from transmitting the value elsewhere; transformed output can defeat masking.
Hidden Prevents the saved value from being revealed again in the settings UI. Current hidden values must satisfy masking rules. Does not stop eligible jobs from receiving the value.
Protected Restricts availability to pipelines on protected branches/tags, with MR nuances controlled by project settings. Does not make the value safe if protected pipeline code or runner is compromised.
Environment scope Restricts a variable to jobs targeting matching environments. Does not replace protected refs or runner isolation; and group-variable environment scope is Premium/Ultimate.
File Writes the value to a temporary file and makes the environment variable contain the file path. Does not make the file automatically non-readable by the job; job code that receives the path can read it.
Security rule: masking is a logging convenience, not a secret-management guarantee. GitLab explicitly warns that malicious pipeline code can exfiltrate values even when they are masked or protected. Review CI changes before running them with sensitive variables.

7. File variables: the variable contains a path

For a normal variable CH13_CONFIG, the environment variable contains the configured value. For a file variable, GitLab Runner writes the configured value into a temporary file and sets CH13_CONFIG to the path of that file.

# Safe pattern: inspect file semantics without printing its content.
test -f "$CH13_CONFIG"
printf 'file_present=yes\n'
printf 'file_bytes=%s\n' "$(wc -c < "$CH13_CONFIG")"
# A tool that expects a path can consume "$CH13_CONFIG" directly.

This is useful for synthetic certificates, configuration files, and tools that require --certificate-authority=/path-style arguments. The temporary file exists within the job execution context; do not copy it into artifacts or caches unless that is an explicit, safe design.

8. Expansion and predefined variables: syntax is not a security boundary

GitLab makes predefined variables such as CI_COMMIT_SHA, CI_PIPELINE_ID, and CI_PROJECT_ID available automatically. Avoid overriding predefined keys: higher-precedence pipeline variables can technically replace many lower-precedence values and make pipeline behavior difficult to reason about.

UI-defined variable expansion is disabled by default in current GitLab. Expansion must be intentionally enabled, and masked/hidden values cannot use variable-reference expansion. Keep secret values raw whenever possible and let the application consume them directly.

9. Pipeline variables are powerful; inputs are the safer parameter surface

Pipeline variables supplied through manual runs, schedules, APIs, triggers, or downstream forwarding have high precedence. GitLab lets projects restrict the minimum role allowed to supply them, including no_one_allowed. For new GitLab.com projects in new namespaces, the secure default may prohibit pipeline variables entirely.

When a pipeline needs operator-selectable parameters, define typed inputs instead. Use pipeline variables only when their runtime mutability and precedence are actually required.

10. External secrets and identity-based retrieval

Long-lived credentials stored in GitLab variables can be acceptable for small labs, but production designs should prefer a secret manager when the environment supports one. GitLab supports OIDC ID tokens on Free across offerings. A job can present a narrowly scoped ID token to a cloud or secret provider and receive a short-lived credential without storing that credential in GitLab.

GitLab's built-in secrets integrations for providers such as HashiCorp Vault, Google Cloud Secret Manager, Azure Key Vault, and AWS Secrets Manager are Premium/Ultimate. The mandatory course path therefore teaches the architecture and claims/policy model without requiring a paid provider integration.

# Architecture fixture only — no real external provider is required.
job:
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.invalid
  script:
    - echo "OIDC token exists for provider authentication; do not print it"

11. Read-only inspection before changing anything

  • Record GitLab offering/version and your project role.
  • Inspect Settings → CI/CD → Variables for keys, type, protected/visibility status, and environment scope. Do not reveal values.
  • Inspect group inheritance indicators before adding a same-name project key.
  • Search .gitlab-ci.yml and included configuration for the variable key, input definition, and any variables: block.
  • Inspect the target ref's protection state before diagnosing a missing protected variable.
  • Inspect runner trust and pipeline source before assuming a variable should be exposed.
  • Never run env, printenv, export, or debug trace in a job that might receive real secrets.

12. DevOps connection: variable delivery is authorization

Secret safety is determined by the intersection of repository write permission, ref protection, pipeline source, variable scope, runner isolation, and external-provider policy. A masked variable available to unreviewed code is still a credential available to that code. A well-designed pipeline minimizes not only who can view a value in the UI, but also which jobs ever receive it.

Knowledge check

A project variable and a YAML job variable share the same key. Which one wins?

Does masking prevent malicious pipeline code from using a secret?

What does a file-type variable contain inside the job environment?

Why prefer pipeline inputs for operator-selected configuration?

What is the key difference between protected and environment-scoped variables?

Why are OIDC ID tokens useful for secrets?

Summary

GitLab variable safety is a resolution-and-authorization problem: choose the right mechanism, understand precedence, scope each value, protect the refs that receive it, isolate the runner, and prefer explicit short-lived secret retrieval when possible. Masking and hiding improve exposure resistance, but neither substitutes for trusted pipeline code.

Official references

Next lesson

Prove the model with only synthetic values

Lesson 2 creates a disposable variable matrix, demonstrates project-over-YAML precedence, validates a file variable without printing it, and compares protected and unprotected refs using presence-only evidence.

Keep the academy open

Support free, practical DevOps education.

Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.