Skip to content
Français
Account: not signed in, sync available My learning
Select theme
Français

By updated

Improve this page

GitLab logo

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:rules that 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_SOURCE and 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.

SituationSymptomCause
Pipeline skippedPipeline visible but grey[skip ci] in the commit message
Job missing from the pipelinePipeline 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 pipelineNothing in PipelinesFile 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 push

When 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 covered

This 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 added

On 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:

  1. Build > Pipeline editor → Paste your file → Validate
  2. Use the "Simulate pipeline creation" option to see which rules block
  3. Or Build > Pipelines → Error message in red

Common errors:

ErrorCauseSolution
Invalid yaml syntaxWrong indentationUse 2 spaces, no tabs
Unknown keyTypoCheck the spelling (script not scripts)
jobs:xxx:rules config should be an arrayWrong structurerules: 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:

Terminal window
git log -1 --format=%B

Solution: 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.

  1. Check that the file exists

    The file must be named exactly .gitlab-ci.yml, at the root of the repo.

  2. Validate the YAML

    Build > Pipeline editor > Validate

  3. Check workflow:rules

    What 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'
  4. 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: always

    This 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.

  5. 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_SOURCEWhen?
PushpushCommit pushed
Merge Requestmerge_request_eventMR created/updated
SchedulescheduleScheduled pipeline
Webweb"Run pipeline" button
APIapiAPI call
TriggertriggerTrigger token
Parentparent_pipelineParent-child pipeline
Multi-projectpipelineMulti-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:rules

Key takeaways

  1. Pipeline skipped = [skip ci] in the commit
  2. A job missing from the pipeline = no rules: of that job matches: the job is not added, it is not greyed out
  3. No pipeline = file invalid, missing, or no workflow:rules rule matches
  4. [skip ci] in a commit can affect the branches created from that commit
  5. The first rule that matches wins, so order matters
  6. 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

10 questions
5 min
70% to pass

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

Is this project useful to you?

The guides, courses, quizzes and labs stay free. If you use them regularly, you can contribute to their upkeep and development.

Become a supporting member (new tab) One-time support (new tab) Learn more

Follow the project on LinkedIn