CI/CD Variables, Inputs, Secrets, File Variables, Masking, Protection, and Scope: Configuration, Design Choices, and Tradeoffs
Choose between variables, typed inputs, project/group scope, protected and environment-scoped values, external secret managers, and broad inheritance using least-privilege and maintainability criteria.
Learning objectives
- Choose between typed inputs and runtime variables based on mutability, validation, and trust.
- Choose project, group, or instance scope without creating unnecessary inheritance blast radius.
- Combine protected-ref and environment scoping deliberately rather than treating them as substitutes.
- Compare stored CI/CD variables with OIDC-backed external secret retrieval.
- Use pipeline-variable restrictions and current expansion behavior as part of a production governance model.
1. Design principle: choose the narrowest mechanism that matches the data
The easiest pipeline to write is often the hardest to govern: put everything in variables, let every job inherit it, and override values when necessary. Production CI should do the opposite. First classify the data as configuration parameter, runtime setting, or secret; then choose the smallest scope and shortest lifetime that works.
2. Typed input versus variable
| Question | Choose input when… | Choose variable when… |
|---|---|---|
| Who changes it? | A pipeline/configuration consumer selects from a documented contract. | The job needs runtime data supplied by settings/policy/system context. |
| Validation | Type/options/regex should reject invalid configuration before jobs exist. | Validation happens in application/job logic or is not required. |
| Mutability | Value should be fixed for configuration creation. | Value may be resolved/overridden according to GitLab precedence. |
| Sensitivity | Value is non-secret configuration. | A settings variable may hold sensitive data only if no better secret system exists; mask/hide/protect it. |
| Auditability | You want an explicit reusable-template interface. | You need standard CI environment-variable behavior. |
Pipeline inputs are recommended over pipeline variables for run-time parameters. If inputs meet the need, restrict or disable pipeline variables so a high-precedence untyped value cannot silently override the pipeline contract.
3. Project versus group versus instance scope
| Scope | Good fit | Risk |
|---|---|---|
| Project | One application or repository owns the value. | Duplication across many projects if the value is truly shared. |
| Group/subgroup | Shared non-secret configuration or carefully governed shared credential used by multiple projects. | Every inheriting project’s CI becomes part of the trust boundary; subgroup/project overrides can change the effective value. |
| Instance | Self-Managed/Dedicated administrator default across the instance. | Largest blast radius and administrative coupling; unavailable as a user-managed GitLab.com instance setting. |
Centralization reduces duplication only when the trust domain is actually shared. Do not put a production credential at group scope merely because several projects happen to deploy to the same platform.
4. Protected ref and environment scope answer different questions
Protected asks: “Is this pipeline running on an approved protected branch or tag?” Environment scope asks: “Is this job targeting an environment whose name matches the scope?” A production deployment may need both controls, plus a protected environment and appropriate runner isolation in later chapters.
| Control | Protects against | Example |
|---|---|---|
| Protected variable | Untrusted/unprotected refs receiving the value. |
Deploy credential available only on protected
main or release tags.
|
| Environment-scoped variable | Unrelated jobs/environments receiving the value. |
Staging endpoint available only to
staging environment jobs.
|
| Both | Ref and environment must both be appropriate. |
Production value only in a protected-ref job targeting
production.
|
Current GitLab supports environment scope on project variables.
Environment scope for group variables is
Premium/Ultimate. Do not use environment-scoped variables inside
rules or include logic because those
values may not be available when pipeline configuration is
validated.
5. Broad inheritance versus explicit duplication
A shared group variable can simplify fleet-wide configuration, but it also makes every inheriting project's pipeline code a potential consumer. Prefer group scope for values that genuinely belong to the group trust domain. Prefer project scope when ownership, rotation cadence, or incident response differs by application.
If the same key exists at group and project levels, project wins. This override ability is useful for migration, but can also hide stale group configuration. Inventory all same-name definitions during incidents and deprecations.
6. Variable expansion: opt in only when it buys something
UI variable expansion is disabled by default in GitLab 18.6 and
later. This is a safer default because values containing
$ are treated literally unless expansion is
intentionally enabled. Masked/hidden variables cannot use
variable-reference expansion.
Prefer explicit composition in application configuration or scripts with well-quoted variables rather than nested secret expansion. Complex chains make precedence and incident response harder to reason about.
7. Pipeline variables: treat high precedence as delegated authority
Allowing a user to set pipeline variables is allowing that user to
override project, group, instance, and YAML values. GitLab therefore
provides a project setting for the minimum role allowed to use
pipeline variables, including no_one_allowed. Use the
narrowest role compatible with your workflow, and prefer inputs for
ordinary operator parameters.
8. Stored variable versus external secret manager
| Criterion | Stored GitLab variable | External secret manager / federated retrieval |
|---|---|---|
| Setup | Simple; built into project/group settings. | Requires provider and trust-policy configuration. |
| Lifetime | Typically long-lived until rotated/deleted. | Can return short-lived credentials per job/session. |
| Access model | Eligible pipeline code receives the value automatically. | Job explicitly authenticates/requests the secret. |
| Rotation | Manual/API process around stored value. | Provider can centralize rotation and policy. |
| Audit boundary | GitLab settings + pipeline/job access. | GitLab identity plus provider-side policy/audit. |
| Best fit | Small synthetic labs; low-risk settings; transitional secrets with masking/hiding/protection. | Production credentials and cross-system access when supported. |
9. OIDC separates identity from a long-lived credential
OIDC ID tokens are Free across GitLab.com, Self-Managed, and Dedicated. GitLab signs a short-lived token for the job. The external provider validates its audience and claims, applies its own trust policy, and returns temporary access.
Provider policy should bind stable and narrow claims such as project/namespace identity and allowed ref/environment context. Treat the ID token itself as a credential: never print it, add it to artifacts, or pass it to services outside its intended audience.
10. Built-in external-secret integrations and GitLab Secrets Manager
GitLab's secrets integrations for Vault, Google Cloud
Secret Manager, Azure Key Vault, and AWS Secrets Manager are
Premium/Ultimate. They use ID-token-based authentication and
explicitly requested secrets. The course does not require them.
GitLab Secrets Manager is fast-moving and remains optional here. GitLab's 19.3 release announcement moved the GitLab.com offering into Limited Availability as a paid add-on through GitLab Credits, while the current Self-Managed OpenBao administration documentation still labels that offering Beta and requires GitLab Runner 19.0+ for CI/CD consumption. Re-check offering, status, billing, and Runner requirements before production adoption. The stable design lesson is broader: secret retrieval should be explicit, least-privilege, and preferably short-lived.
11. Worked decision table
| Scenario | Choice | Why |
|---|---|---|
User selects lab/audit for a
manual diagnostic
|
Pipeline input | Typed, validated, non-secret configuration contract. |
| One project needs a benign API base URL | Project variable or YAML variable | No cross-project trust needed; value is non-secret. |
| Twenty projects share a non-secret artifact mirror URL | Group variable | Central ownership is useful and exposure is low-risk. |
| Production database credential for one app | External secret manager if available; otherwise masked+hidden+protected project variable as transition | Minimize inherited blast radius and long-lived storage. |
| Certificate content required by a tool | File variable or external secret returned as file | Tool consumes a path; avoid printing/copying content. |
| Release credential only for protected release tags | Protected scope plus external provider/ref policy | Ref trust is part of authorization. |
12. Governance checklist before adding a value
- What classification is the data: public configuration, sensitive configuration, or secret?
- Which jobs need it? Which jobs must never receive it?
- Can a typed input replace a high-precedence pipeline variable?
- Can OIDC/federation replace a stored long-lived credential?
- What is the narrowest project/group/environment/ref scope?
- Who can change pipeline code that receives the value?
- Which runners execute those jobs and what else can they access?
- How will the value be rotated, revoked, and proven removed?
Knowledge check
Why can a group variable be more dangerous than duplicating a project variable?
Its trust boundary includes every inheriting project and the pipeline code in those projects; centralization can enlarge blast radius.
Does environment scope replace protected variables?
No. Environment scope filters by environment; protected variables filter by protected refs. They are complementary.
Why disable pipeline variables after migrating to inputs?
Pipeline variables are high-precedence, loosely typed overrides. Disabling an unused override channel reduces unexpected behavior and privilege.
What is the main security advantage of OIDC federation?
A job can exchange short-lived identity proof for temporary provider access instead of storing a long-lived cloud/provider credential.
What tier constraint matters for group-variable environment scope?
It is Premium/Ultimate; the Free path should use project scope or a documented simulation.
Why is GitLab Secrets Manager not mandatory in this chapter?
It is tier/status/offering-sensitive and fast-moving. The mandatory learning goal is secret lifecycle and trust-boundary design, which can be taught without requiring it.
Summary
A production variable model begins with classification and least privilege, not with a UI field. Inputs are contracts, variables are runtime data, protected/environment scopes are separate filters, group inheritance expands trust, and OIDC-backed retrieval can replace long-lived stored secrets. The safest design is the one that sends the fewest sensitive values to the fewest jobs for the shortest time.
Official references
- GitLab Docs — CI/CD variables
- GitLab Docs — CI/CD inputs
- GitLab Docs — Pipeline security
- GitLab Docs — External secrets in CI/CD
- GitLab Docs — OIDC authentication using ID tokens
- GitLab Docs — Connect to cloud services with OIDC
- GitLab Docs — Project-level CI/CD Variables API
- GitLab Docs — Group-level Variables API
- GitLab Docs — Predefined CI/CD variables
- GitLab Docs — Where variables can be used
- GitLab Docs — CI/CD YAML syntax reference
- GitLab CLI — variable commands
- GitLab CLI — variable set
- GitLab CLI — variable delete
- GitLab 19.3 — latest monthly release
- GitLab Docs — GitLab Secrets Manager (Self-Managed)
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.