๐ GitLab CI/CD: Jobs Have Rules, Pipelines Have Rules Too
You push a commit, open a merge request, and suddenly GitLab is running two pipelines for what feels like the same change.
Congratulations: you’ve discovered a surprisingly common CI/CD configuration headache.
The problem might be the difference between rules and workflow:rules. They look similar, but they operate at different levels.
The discovery: One controls jobs, the other controls pipelines
GitLab evaluates workflow:rules when deciding whether to create a pipeline. Job-level rules determine whether an individual job is included in that pipeline.
Think of it this way:
workflow:rules: Should this pipeline exist?
rules: Should this job exist in the pipeline?
For example, this job runs in merge request pipelines:
This includes the deployment job as a manual job on the default branch and excludes it elsewhere.
Also, when: manual doesn’t automatically mean the same failure behavior in every configuration: a manual job defined inside rules defaults to allow_failure: false, unlike a job-level manual declaration without rules.
Be explicit when pipeline behavior matters.
The takeaway โ
When your pipeline runs at the wrong time, ask which decision is wrong: creating the pipeline or including a job?
Use workflow:rules for the first, job-level rules for the second, and check the first matching rule before adding another.
Sometimes the best pipeline optimization is simply not creating the extra pipeline in the first place.