workflow:rules, Job rules, if Expressions, changes, exists, Pipeline Sources, and Conditional Execution: Configuration, Design Choices, and Tradeoffs
Rules are executable policy, so readability and scope matter as much as syntax. This lesson compares workflow:rules with job rules, positive allowlists with catch-all fallthrough, changes with exists, explicit source guards with implicit assumptions, and simple ordered rules with clever expressions that are difficult to audit.
Learning objectives
- Choose workflow:rules or job rules according to whether the requirement controls pipeline existence or only job membership.
- Prefer explicit source allowlists and readable ordered rules over broad catch-all behavior when pipeline classes have different trust/cost.
- Choose changes for diff-sensitive work and exists for repository-capability detection, with source/compare-to semantics made explicit.
- Evaluate rule order, expression complexity, duplicate-pipeline prevention, and version-sensitive current GitLab behavior.
- Document tier/offering/trust prerequisites and the observable evidence that proves each design choice.
1. Rules are policy code, not convenience syntax
A rule set should answer three audit questions quickly: which pipeline classes can exist, which jobs can exist inside each class, and what trusted evidence causes each decision? Designs that are technically compact but force reviewers to mentally simulate many overlapping fallthroughs are expensive to maintain and easy to mis-secure.
2. workflow:rules versus job rules
| Requirement | Best layer | Why | Evidence |
|---|---|---|---|
| Do not create pipelines for unsupported sources | workflow:rules | Stops work before any job graph exists | Absence/presence of pipeline record by source |
| Avoid redundant branch pipeline when MR pipeline exists | workflow:rules | The duplication is pipeline-level | One pipeline class for same push/SHA |
| Run integration tests only on MRs/default branch | Job rules | Other jobs can still use the pipeline | Job graph membership |
| Make deployment manual on default branch | Job rules | Changes one job’s when/authorization path | Job present as manual only in allowed context |
| Choose an include before full config exists | include:rules + pre-pipeline variables | Configuration composition decision | Merged configuration |
3. Positive allowlist versus broad fallthrough
A final when: always is easy to write and hard to
bound. If new pipeline sources appear in your architecture
later—API, child pipeline, multi-project pipeline, security policy
pipeline—the catch-all can admit them without a deliberate review. A
source allowlist is noisier but makes expansion an explicit code
change.
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH'
- if: '$CI_PIPELINE_SOURCE == "schedule"'
- when: never
The tradeoff is operational: strict allowlists can block a new legitimate integration until configuration is updated. That is often preferable for privileged/expensive repositories because failure is visible and reviewable rather than silently widening execution.
4. changes versus exists: diff intent
versus repository capability
| Question | Use | Example | Failure mode if misused |
|---|---|---|---|
| Did relevant content change relative to a comparison base? | changes |
Run docs build when docs changed | Unexpected true in no-push contexts; wrong comparison base |
| Does this project/ref contain a capability/config file? | exists |
Run Node job if package.json exists | Cannot see generated artifacts; include lookup context may differ |
| Should an optional service job run if service exists and changed? |
Combine exists + changes in one
rule
|
Monorepo service | Reviewer may miss AND semantics if split across rules |
Keywords inside a single rule are combined: all conditions must satisfy that rule. Separate rule entries are alternatives evaluated in sequence. That structural distinction is often clearer than a long Boolean expression.
5. Rule order is precedence: narrow exceptions first
First-match behavior means ordering is not formatting. A broad
source rule placed before a narrow
when: never exception makes the exception unreachable.
Review rule lists top-to-bottom as decision tables, not as unordered
sets.
# Risky: broad MR match makes the draft exception unreachable.
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_MERGE_REQUEST_DRAFT == "true"'
when: never
# Better: reject the narrow case first.
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event" && $CI_MERGE_REQUEST_DRAFT == "true"'
when: never
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
6. Duplicate-pipeline avoidance is a cost and correctness control
When a push updates a branch that has an open merge request, GitLab can have reasons to create both branch and MR pipelines. If job rules also end in broad catch-all behavior, the same SHA can consume twice the runners, publish duplicate reports, race external statuses, or make governance systems pick the wrong pipeline. Prevent duplicates at the pipeline boundary when your delivery model wants only one class.
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
- if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS && $CI_PIPELINE_SOURCE == "push"'
when: never
- if: '$CI_COMMIT_BRANCH'
CI_PIPELINE_SOURCE == "push" guard on the
suppression rule matters. Triggered/downstream pipelines can also
have a branch; a branch-only suppression could block them
unintentionally.
7. compare_to trades implicit context for explicit
baseline
rules:changes:compare_to makes the comparison ref
reviewable and can stabilize behavior for empty branches or
nonstandard contexts. The cost is that the baseline itself becomes
configuration state: branch renames, fork behavior, and
merged-results pipelines can change what the comparison means.
Record the baseline ref and, for release-critical decisions,
consider whether a moving branch is sufficient evidence or whether
another design is safer.
8. Expression readability and trust
Prefer several small, named pipeline classes to one dense
expression. Regex is useful for trusted ref naming conventions, but
it is not an authorization system. A branch name matching
release/* does not prove the actor is authorized to
deploy. Pair conditional inclusion with protected refs/environments,
scoped identities, and runner trust where the job has privileged
side effects.
Current GitLab rules:if regular expressions use RE2
semantics. Newer GitLab documentation also includes regexp forms for
rules:changes/exists; those have different
semantics/version history. This chapter’s runnable path uses stable
path/glob forms so self-managed learners are not forced onto the
newest syntax.
9. Worked scenario: monorepo delivery policy
| Decision | Choice | Prerequisites | Affected state | Proof |
|---|---|---|---|---|
| Which pipeline classes exist? | MR, default-branch push, nightly schedule | Free-compatible workflow rules | Pipeline records / compute cost | Pipeline source + ID count per SHA |
| API service tests | MR only when services/api/** changes |
MR diff available | Job graph | Job present/absent + diff paths |
| Docs lint | Any admitted pipeline when docs tree exists | Repository path | Job graph | exists match + job membership |
| Nightly dependency audit | Schedule only | Configured schedule | Job graph / runner use | source=schedule + job ID |
| Production deployment | Default branch plus separate protected deployment control | Protected ref/environment policy where used | Privileged side effect | Job inclusion + authorization + deployment evidence |
10. Availability, portability, and rollback
Core workflow:rules, job rules,
if, changes, and exists are
part of the ordinary CI/CD configuration surface across GitLab
offerings. Advanced surrounding controls—protected environments,
policy-injected jobs, merged results/merge trains, enterprise
governance—can be tier/offering sensitive. Keep the core rule model
free-compatible and label optional controls separately.
Rollback is code rollback: preserve the prior CI configuration SHA, revert the rule change, and verify the resulting pipeline class/job graph. Do not “fix” a rule mistake by manually rerunning arbitrary old jobs if side effects or source context differ.
Knowledge check
You need to stop API pipelines from existing at all. Workflow rule or job rule?
Workflow rule, because the policy concerns pipeline creation rather than one job.
You need tests only when src/ changes in an MR. changes or exists?
changes, because the question is about the diff. exists only proves the path is present.
Why prefer an explicit final when: never in strict workflow allowlists?
It makes unsupported pipeline sources visibly denied rather than accidentally admitted by fallthrough.
Can a matching release branch regex replace protected deployment authorization?
No. Naming is input classification, not actor/environment authorization.
What is the rollback artifact for a rule design?
The prior reviewed CI configuration at a known source SHA/ref, plus evidence that pipeline/job behavior returned to the expected state.
Version and compatibility note
GitLab and GitLab Runner evolve continuously. Treat version-sensitive YAML, runner/executor behavior, APIs, security features, policy controls, tiers, and deprecations as assumptions to verify against the current official GitLab documentation before production use. Preserve the exact project/ref/SHA, compiled configuration, pipeline/job IDs, runner/tool versions, and external target evidence used for any reproducible lab or incident record.
Official references and version notes
Documentation verification date: 2026-09-11. GitLab CI/CD rule semantics evolve; self-managed installations should confirm their deployed GitLab version before relying on newer syntax.
-
workflowkeyword — pipeline-creation rules, duplicate-pipeline avoidance, branch-to-MR switching, and current source examples. -
Specify when jobs run with
rules— first-match evaluation,CI_PIPELINE_SOURCEvalues, expressions, schedules,changes, and duplicate-pipeline guidance. -
CI/CD YAML syntax reference
— authoritative
rules,rules:changes,compare_to,rules:exists,when, and workflow syntax. - Predefined CI/CD variables — variable availability phases and safe source/ref/SHA metadata.
-
Debugging CI/CD pipelines
— current guidance for unexpected
changesbehavior and duplicate pipelines. - Troubleshooting merge request pipelines — evidence and repair guidance for branch + MR duplicates.
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.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this
address.