How to Auto-Cancel GitLab Pipelines for Merge Requests — But Not on main
When working with CI/CD pipelines, speed is everything. Developers often push multiple commits in rapid succession — especially when working with merge requests. In such cases, running every single pipeline for every commit is not only wasteful, it’s noisy. That’s where GitLab’s auto-cancel feature comes in.
🤔 The Problem
By default, GitLab offers a setting to “auto-cancel redundant pipelines”, which you’ll find under:
Settings → CI/CD → General Pipelines → Auto-cancel redundant pipelines
If enabled and combined with the interruptible keyword
in the job configuration, when a new pipeline starts for the same branch, previous pipelines are canceled automatically.
That’s great for fast-moving work on merge requests — but if you’re deploying from main
, you likely never want those pipelines canceled.
Unfortunately, GitLab’s UI setting is global, and affects all branches, including main
.
🔍 What We Want
| Pipeline Type | Auto-Cancel? | Why |
|--------------------|--------------|-----------------------------------------------------|
| Merge Request (MR) | Yes | All jobs are interruptible: true |
| main branch | No | Production deploys should never be interrupted |
💡The idea?
We can use rules with interruptible [1] which was introduced in GitLab version 16.10 in March 2024. [2]
The idea is to set a job as interruptible as default but with the rules to set the interruptible
property to false, like this:
build:
interruptible: true
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
interruptible: false
- when: on_success
The condition above evaluates if the commit branch is the same as the default branch (which is generally the main branch). If that is the case, the job is considered non-interruptible.
But what is the role of when: on_success
?
This is a tricky one and here is my understanding which might be wrong, so please correct me by leaving a comment down below.
Adding a rules:
configuration to any job can change or override the default behavior.
The rules:
keyword defines conditions for when a job should be included or excluded from a pipeline. Each item in the rules list is evaluated in order. If a rule evaluates to true, the job is included. If no rule matches, the job is excluded.
Setting the condition when: on_success
means the job should only run if all previous jobs in the pipeline’s stages have succeeded.
If we remove it, we are removing a condition that ensures that the job will run, and are left with only the condition that the branch must be the default branch. So no pipelines will be created for other branches. .
✅ Complete pipeline
Let’s take a look at a more realistic example involving a pipeline with multiple stages and jobs where we use a job template.
You can notice that instead of writing the rules configuration in all jobs, we have just defined a job template that can be used in all jobs without duplicating the configuration.
# Job template
.default_job:
image: alpine
interruptible: true
rules:
- if: $CI_COMMIT_REF_NAME == $CI_DEFAULT_BRANCH
interruptible: false # Override default interruptible value
- when: on_success
build:
extends: .default_job
stage: build
script:
- echo "🔨 Building..."
test:
extends: .default_job
stage: test
script:
- echo "🧪 Testing..."
deploy:
extends: .default_job
stage: deploy
script:
- echo "🚀 Deploying..."
If you want to learn how to build pipelines in GitLab CI/CD, I have created an online course that starts with the basics and helps you build a CI/CD pipeline that takes a project and deployes it to the cloud. Learn more about the course.
🧪
Testing it
You might want to add a sleep command in one of the jobs to extend the execution time.
If we make two commits to the main branch, we should see both pipelines running:
Let’s create a branch and push two consecutive changes there. You should notice the pipeline for the first change being canceled.
If you are creating a merge request, you might notice two pipelines being triggered.
If that is the case, you can disable the merge request pipeline by adding this configuration to the top of the .gitlab-ci.yml
file:
workflow:
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
when: never
- when: always
Researching & writing this article took me quite a few hours. How long does it take you to press that 👏 a few times (up to 50 times)? Thank you for supporting me!
Why this happens and how this works is described in this post: Fix GitLab CI Duplicate Pipelines in Merge Requests when Using rules:.
Conclusion
I hope this tutorial helped you get started with auto-cancelling pipelines in GitLab CI/CD. Leave a comment in the section below if you have any questions. I would love to hear from you!
Thank you for sticking with this article until the end. If you enjoyed it, please leave a comment, share, and press that 👏 a few times (up to 50 times). It will help others discover this information, and maybe it will help someone else as well.
Follow me on Medium and YouTube if you’re interested in more tutorials like this.