
You pushed code but the pipeline does not appear or is marked "skipped"? This guide helps you understand why and fix the problem.
What you will learn
By the end of this guide, you will be able to explain why your pipeline is skipped or not created, and to fix it:
- Tell three symptoms apart: skipped pipeline, job missing from the pipeline, missing pipeline
- Diagnose a
workflow:rulesthat does not cover the case of a plain push - Spot jobs for which no rule matches and an invalid
.gitlab-ci.yml, with the CI Lint and its simulation - Understand the missing pipeline of a branch that has an open merge request (MR), and the effect of
[skip ci] - Add a debug job that prints
$CI_PIPELINE_SOURCEand the useful variables - Write correct
workflow:rules: push and MR, without duplicates, on tags, outside WIP branches
Understanding the difference
Before touching the .gitlab-ci.yml, identify which of the three symptoms you are looking at: they look alike in the interface but are not fixed in the same place. The sorting criterion is simple: go to Build > Pipelines and see whether a pipeline row exists. If a row exists, GitLab did create the pipeline: look for a [skip ci] in the commit or for jobs whose rules: do not match. If no row appears, no pipeline was created: file missing or invalid, or a workflow:rules that excludes your case.
| Situation | Symptom | Cause |
|---|---|---|
| Pipeline skipped | Pipeline visible but grey | [skip ci] in the commit message |
| Job missing from the pipeline | Pipeline created, but an expected job is not in it (nothing is greyed out) | No rules: of the job matches: the job is not added |
| No pipeline | Nothing in Pipelines | File missing, invalid, or no workflow:rules rule matches |
Cause #1: workflow:rules does not match
This is a classic trap: workflow:rules controls whether the pipeline is created, and a rule that is too narrow prevents it for a whole type of event.
Example of the problem:
workflow: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' # Incorrect: the push case is missing
# This pipeline is NEVER created on a plain pushWhen you push to a branch (with no open MR), $CI_PIPELINE_SOURCE is push, not merge_request_event. No rule matches → no pipeline is created.
Solution:
workflow: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_COMMIT_BRANCH' # Correct: the push case is coveredThis failure is practised in the Debug: job stuck on pending lesson, whose lab carries two of them: the job waiting for a runner, and the pipeline that never comes into existence. The second one is this page's failure, and playing it is how what GitLab really displays was measured.
Cause #2: All jobs have when: never
If no job is added to the pipeline (rules: that match nowhere), there is nothing left to run. What the interface then shows (an empty pipeline, no pipeline, or an error) depends on the pipeline type and is not detailed here: do not rely on the Pipelines tab, check with the CI Lint presented below.
Example:
build: rules: - if: '$CI_COMMIT_BRANCH == "main"' when: never # Excluded on main - when: never # Excluded everywhere else!
test: rules: - if: '$CI_COMMIT_BRANCH == "develop"' # On main, no rule matches → the job is not addedOn main, neither job is added: the pipeline has nothing to run.
If the rule concerns only one job, the other jobs of the pipeline run normally and that one stays missing from the list: GitLab does not show it greyed out, it simply does not add it to the pipeline. When an expected job is missing, look for its absence, not for a grey job.
Diagnosis: check that at least one job runs in every scenario.
Cause #3: Invalid .gitlab-ci.yml file
An invalid YAML file prevents the pipeline from being created.
Diagnosis:
- Build > Pipeline editor → Paste your file → Validate
- Use the "Simulate pipeline creation" option to see which rules block
- Or Build > Pipelines → Error message in red
Common errors:
| Error | Cause | Solution |
|---|---|---|
Invalid yaml syntax | Wrong indentation | Use 2 spaces, no tabs |
Unknown key | Typo | Check the spelling (script not scripts) |
jobs:xxx:rules config should be an array | Wrong structure | rules: must be a list |
Cause #4: Branch with an open MR
A branch that has an open MR can trigger two pipelines for the same commit: a branch pipeline (source push) and an MR pipeline. GitLab does not deduplicate on its own, workflow:rules does the choosing. If you wrote the rule that keeps only one, the push pipeline "disappears" as soon as an MR is opened, and it looks like a bug.
Example (pattern published by GitLab):
workflow: rules: - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS' when: never # Skip if the branch has an MR - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_COMMIT_BRANCH'With this config, pushing to a branch with an open MR does not create a push pipeline: the MR pipeline runs, and a single pipeline starts per commit. The "Pipeline on push, but not if an MR is open" section below explains why this rule does not block the MR pipeline.
Cause #5: [skip ci] commit message
A commit containing [skip ci] or [ci skip] in its message produces a skipped pipeline.
Common trap: GitLab evaluates the message of the commit pointed to by the pushed reference, not the one you just wrote. Creating a branch from a commit containing [skip ci] without adding anything on top of it therefore amounts to pushing that commit, and the pipeline is skipped again even though you asked for nothing.
Diagnosis:
Check the message of the last commit:
git log -1 --format=%BSolution: make a new commit without [skip ci], or use Run pipeline from the interface.
Step-by-step diagnosis
Follow these five checks in order: they go from the coarsest to the subtlest, and each one eliminates a family of causes. The first two take ten seconds and already rule out most cases. Step 4, the debug job, is the only one that shows you the real values of the variables in your exact context; it is the one that settles the matter when the rules look correct on paper.
-
Check that the file exists
The file must be named exactly
.gitlab-ci.yml, at the root of the repo. -
Validate the YAML
Build > Pipeline editor > Validate
-
Check
workflow:rulesWhat are your rules? Do they match your case?
workflow:rules:- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'- if: '$CI_COMMIT_BRANCH'- if: '$CI_COMMIT_TAG' -
Add a debug job
debug:script:- echo "SOURCE=$CI_PIPELINE_SOURCE"- echo "BRANCH=$CI_COMMIT_BRANCH"- echo "TAG=$CI_COMMIT_TAG"- echo "MR=$CI_MERGE_REQUEST_ID"rules:- if: '$CI_PIPELINE_SOURCE'when: alwaysThis job only runs if a pipeline is created: it helps when the pipeline exists but does not contain the expected jobs (merge request, tag), not when the Pipelines tab stays empty. In that second case, go back to step 3.
-
Use the CI Lint with simulation
Build > Pipeline editor > Validate, then run the simulation to see which jobs would be created on a push to the default branch.
Reference table: pipeline sources
The middle column is the one you care about: the exact value to compare in an if:. It is case-sensitive and always written in lowercase with underscores. Two confusions come up often: push covers both branches and tags, and pipeline designates multi-project pipelines, not child pipelines, which take the value parent_pipeline.
| Source | $CI_PIPELINE_SOURCE | When? |
|---|---|---|
| Push | push | Commit pushed |
| Merge Request | merge_request_event | MR created/updated |
| Schedule | schedule | Scheduled pipeline |
| Web | web | "Run pipeline" button |
| API | api | API call |
| Trigger | trigger | Trigger token |
| Parent | parent_pipeline | Parent-child pipeline |
| Multi-project | pipeline | Multi-project pipeline |
Examples of correct workflow:rules
Pipeline on push AND merge requests
This is the recommended starting configuration when you do not yet know what to put. It accepts everything that comes from a merge request and everything that comes from a branch. A consequence to accept: pushing to a branch that already has an open MR triggers two pipelines for the same commit, and doubles the runner minutes consumed.
workflow: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_COMMIT_BRANCH'Pipeline on push, but not if an MR is open (avoid duplicates)
This variant fixes the previous duplicate. The when: never rule does not block the merge request pipeline, because $CI_COMMIT_BRANCH is not defined in an MR pipeline: the condition cannot match. $CI_OPEN_MERGE_REQUESTS contains the list of merge requests whose source is the current branch, and stays empty otherwise. This is the pattern published by GitLab to keep a single pipeline per commit.
workflow: rules: - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS' when: never - if: '$CI_COMMIT_BRANCH'Pipeline on tags only
Typical configuration of a release repository: nothing runs day to day, everything is triggered when a tag is created. Know what you accept with this rule: all branch pushes and all merge requests create no pipeline, so no test runs before the tag. Reserve it for repositories whose content is already validated elsewhere.
workflow: rules: - if: '$CI_COMMIT_TAG'Pipeline everywhere except WIP branches
The exclusion must come first, before the rules that accept: GitLab applies the first rule that matches and ignores the following ones. Placed last, the wip- rule would never be evaluated. The =~ operator compares the variable with a regular expression delimited by slashes, and the ^ anchor makes it apply to the start of the branch name.
workflow: rules: - if: '$CI_COMMIT_BRANCH =~ /^wip-/' when: never - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' - if: '$CI_COMMIT_BRANCH' - if: '$CI_COMMIT_TAG'Debug checklist
Go through this list in order before opening a ticket or asking for help: it takes up the causes of the guide, from the most trivial to the most subtle. The first three lines can be checked in under a minute. If everything is ticked and the pipeline stays skipped, the problem is probably a security policy at the group level, which can restrict pipeline creation or the behavior of [skip ci].
□ Does the .gitlab-ci.yml file exist at the root?□ Is the YAML valid? (Pipeline editor > Validate + Simulate)□ Does the commit contain [skip ci] or [ci skip]?□ Is there a workflow:rules section? → Yes: Does it cover your case (push, MR, schedule...)? → No: The pipeline should be created□ Does at least one job have rules that match?□ Does the branch have an open MR? → Check whether this is intended in workflow:rulesKey takeaways
- Pipeline skipped =
[skip ci]in the commit - A job missing from the pipeline = no
rules:of that job matches: the job is not added, it is not greyed out - No pipeline = file invalid, missing, or no
workflow:rulesrule matches [skip ci]in a commit can affect the branches created from that commit- The first rule that matches wins, so order matters
- CI Lint + Simulate to validate before pushing
Test your knowledge
These questions cover the five causes described above and how to read workflow:rules. If you hesitate between a skipped pipeline and a not created pipeline, reread the first table before you start.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: Summary: build a complete pipeline
A new full quiz with random questions
Work again only on the questions you missed