Keep protected branches green with GitLab merge trains
Merge trains serialize merge requests against the current branch tip so each change is validated before it lands, reducing broken-main incidents.
07 Apr 2026, 00:28 UTC

Your team opens two merge requests against main. Both pass their pipelines individually. A maintainer merges them in quick succession. The second merge request was validated against last week’s main; by the time it lands, the first merge request has already changed the branch tip. The result: a red protected branch and an emergency revert.
The useful takeaway is that GitLab merge trains queue merge requests targeting the same protected branch and run each pipeline in sequence against the current branch tip. A merge request only lands after its pipeline succeeds in the context of what main will look like once it is merged. This reduces broken-main incidents without requiring an external merge-queue bot.
How a merge train changes the merge order
A merge request is a proposed change to a branch. A protected branch is one that restricts who can push or merge directly. Normally, pipelines for merge requests run against the feature branch. If two merge requests are approved at the same time, both pipelines can pass while the branch tip moves under them.
A merge train serializes this. GitLab places the merge requests in a queue. For each entry, it creates a temporary ref — Git’s pointer to a specific commit — that combines the target branch with the queued merge request, runs the pipeline against that ref, and merges only if the pipeline is green. The next merge request in the train then starts against the new tip, which now includes the previous change.
This is not the same as making individual CI jobs run faster. It changes when a merge request is allowed to land, not how quickly a single test suite executes.
Worked example: enable and queue two merge requests
Merge trains are configured per project under Settings > Merge requests > Merge trains. You can enable merge train pipelines and optionally require them to succeed before a merge request can be merged. Availability depends on your GitLab tier and instance version; historically this has been a Premium or Ultimate feature, so confirm it is visible in your project before planning around it.
Here is a concrete sequence:
- Create two merge requests,
!101and!102, both targetingmain. - Add a CI job that runs unit tests. Example configuration:
stages:
- test
unit-tests:
stage: test
script:
- npm ci
- npm test
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- Push a feature branch from your local clone. Replace
fix-login-timeoutwith your branch name:
# Run in your local repository clone. Requires write access to the project.
git push origin fix-login-timeout
Expected check: the branch appears in the project and you can create a merge request. Risk: force-pushing a branch that is already queued in a merge train can invalidate that train entry.
- Enable merge trains, then queue
!101. Its pipeline runs against a ref representingmainplus!101. If green,!101merges. !102remains queued. Once!101lands,!102starts its pipeline against the updatedmainplus!102. If that passes, it merges.
In the Merge requests > Merge trains view, you should see entries move from waiting to running to merged or failed. To confirm a pipeline came from the train rather than a standard merge-request pipeline, open the pipeline details and check the source and ref shown in the UI.
Trade-offs and limits
The main cost is wall-clock time. Because pipelines run one after another, stacked changes take longer to reach main than they would with parallel merges. Keep pipelines fast and use needs to run independent jobs in parallel within each train entry.
Other limits to verify in your environment:
- Merge trains may interact unexpectedly with
only/exceptbranch rules or jobs that readCI_COMMIT_BRANCH, because the pipeline runs against a combined ref rather than the feature branch alone. - Merge trains are not a substitute for interruptible pipelines or auto-cancel settings. Redundant pipelines can still consume runner minutes.
- If your median pipeline duration is already high, a merge train can become a bottleneck. Measure pipeline duration before enabling.
Check the result and roll back if needed
Practical verification: create two merge requests with a fast test job. Queue both. Confirm the second pipeline does not start until the first has merged. If you need to stop using merge trains, return to Settings > Merge requests > Merge trains and disable the feature. Because enabling it changes project state, disable it before queueing additional merge requests if you want to halt new train entries.
Merge trains trade merge throughput for branch stability. For teams shipping to a shared protected branch several times a day, that is often a worthwhile trade.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.