Without rules every job runs on every push; with them you deploy only from main and skip work nobody needs.
rules decides whether a job is added to a pipeline. GitLab evaluates the list in order and the first matching rule wins; if none match, the job is not added. Each rule can test:
if: an expression over variables, such as$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCHor$CI_COMMIT_TAG =~ /^v\d+/.changes: file paths or globs changed by the push (src/**/*).exists: files present in the repository.
A matching rule can set when: on_success (default), manual (wait for a click), delayed (with start_in), always or never. It can also set allow_failure and variables.
when: never rules are used to exclude cases first, followed by a catch-all. workflow:rules uses the same syntax for the whole pipeline: if it doesn't match, no pipeline is created. Its most common job is avoiding duplicate pipelines when a branch has an open merge request.
rules replaces the older only and except keywords, which still work but can't be combined with rules in the same job. New configuration should use rules everywhere.
Example
workflow:
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
when: never # avoid branch + MR duplicate pipelines
- if: $CI_COMMIT_BRANCH || $CI_COMMIT_TAG
docs:
script: make docs
rules:
- changes: ["docs/**/*"] # only when docs changed
deploy:
script: ./deploy.sh
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual # needs a click on main
- when: never # nowhere elseRemember
- The first matching rule wins; no match means the job is not added
- when: manual, delayed, never, always tune the outcome
- workflow:rules decides whether a pipeline is created at all
Watch out for
- Mixing rules with only/except in the same job, which is a configuration error.