Blog
GitHub merge queue: how it works and how to set it up
Keep main green when many approved pull requests land at once, with a workflow you can copy and the settings that decide how fast the queue moves.
Alex Mercer
GitHub's merge queue is the step between an approved pull request and the base branch. When you add a pull request to the queue, GitHub tests it on top of the latest base branch and every pull request queued ahead of it, then merges it once the required checks pass on that combination.
Teams turn it on for busy branches, where two pull requests can each pass CI on their own and still break main together. To set it up, your CI has to run for merge groups (the merge_group event in GitHub Actions), and a repository admin turns on Require merge queue for the branch. If you're new to the steps before the queue, start with what a pull request is and how it moves from draft to merge.
Why teams turn on a merge queue
CI on a pull request tests the code as it was when the run started. On a busy branch, main keeps moving after your checks pass, so nothing has tested your change together with the pull requests that merged in the meantime.
For example, pull request A renames getUser() to fetchUser() and updates every caller. Pull request B, opened earlier, adds one more call to getUser(). Each passes CI against the main it was tested with, and Git reports no conflict because the two touch different lines. Merge both and main breaks, although each pull request was green.
Without a queue, GitHub's required status checks either leave this risk open or make authors update their branch and rerun CI (About protected branches).
Setup | What CI tested before the merge | The cost |
|---|---|---|
Required checks, branch may be behind | The pull request against an older | Checks "may fail after you merge your branch if there are incompatible changes with the base branch" |
Require branches to be up to date before merging | The pull request against the current | Each time someone else merges first, the author updates the branch and waits for CI again |
Merge queue | The pull request with the current | CI runs again on every merge group |
GitHub says the queue "provides the same benefits as the Require branches to be up to date before merging branch protection" without making authors update their branch and wait for checks before they try to merge. It calls the queue "particularly useful on branches that have a relatively high number of pull requests merging each day from many different users" (Managing a merge queue).
How the merge queue works
A pull request can enter the queue once it meets the branch's requirements, such as required approvals and passing status checks, and someone with write access adds it. GitHub then merges queued pull requests in first-in-first-out order, with the required checks satisfied at every step.
Each queued pull request gets a merge group: a temporary branch that holds the latest base branch, the pull requests ahead of it and its own changes. These branches start with gh-readonly-queue/{base_branch} and have a different commit SHA from the pull request. For each one, GitHub sends a merge_group event with the activity type checks_requested, then waits for your CI to report the required checks on that commit.
With three pull requests queued for main:
Queue position | What CI tests for that merge group |
|---|---|
1. #101 |
|
2. #102 |
|
3. #103 |
|
When the groups for #101 and #102 pass, GitHub merges the temporary branch for #102 into main. That lands both pull requests, since the branch already contains #101.
If a group fails a required check, or its pull request conflicts with the base branch, GitHub removes that pull request from the queue and shows the reason on its timeline. The groups behind it are rebuilt without it. If the group for #101 fails, the queue recreates the temporary branch for #102 from main and #102 alone, then runs its checks again (How merge queues work).
Who can use the merge queue
GitHub's docs say: "Pull request merge queues are available in any public repository owned by an organization, or in private repositories owned by organizations using GitHub Enterprise Cloud."
Inside those repositories, "People with admin permissions can manage merge queues for pull requests targeting selected branches of a repository," and anyone with write access can add a pull request that meets the branch's requirements (Managing a merge queue).
A merge queue can't be enabled on a branch protection rule whose branch name pattern uses a wildcard (*). In rulesets, the Require merge queue rule works only in rulesets created at the repository level, not the organization level (available rules for rulesets).
How to set up a GitHub merge queue
1. Run your CI on merge_group
Do this before you turn the queue on. A merge queue "will wait for required checks to be reported before it can proceed with merging," and the merge_group event is separate from pull_request and push. If a workflow that produces a required check doesn't list merge_group, GitHub's docs say "status checks will not be triggered when you add a pull request to a merge queue. The merge will fail as the required status check will not be reported."
This workflow reports the same check on pull requests and in the queue:
Per GitHub's events reference, checks_requested is the only activity type the event supports, and naming it keeps the workflow specific if more types are added later. The branches filter matches the branch the merge group targets. Inside the run, GITHUB_SHA is the merge group's commit and GITHUB_REF is its ref.
If your CI runs outside GitHub Actions, configure it to build when a branch that begins with gh-readonly-queue/{base_branch} is pushed. Those are the queue's temporary branches (third-party CI providers).
2. Turn on Require merge queue
With a classic branch protection rule, go to Settings > Branches > Add classic branch protection rule in the repository (Managing a branch protection rule):
Under "Branch name pattern", enter the branch name, such as
main, with no wildcard.Select Require status checks to pass before merging and add the check from the workflow's
testjob.If reviews are required, select Require a pull request before merging, then Require approvals, and Require review from Code Owners for owned paths.
Select Require merge queue, then choose its settings.
With rulesets, go to Settings > Rulesets > New ruleset > New branch ruleset (Creating rulesets for a repository). Target the branch, then add the "Require status checks to pass before merging" and "Require merge queue" rules. The ruleset has the same queue settings, some under other names.
3. Pick the queue settings
Setting | What it controls | The trade-off |
|---|---|---|
Merge method | Whether the queue merges, rebases or squashes every pull request it lands | If the branch also requires a linear history, only squash and rebase merges are allowed |
Build concurrency (1 to 100) | How many merge groups get a | Higher clears the queue faster, but runs more CI at the same time and rebuilds more groups when an entry ahead fails |
Only merge non-failing pull requests | Whether every entry must pass its own checks | Turned off, a pull request whose own checks failed can still merge in a group whose last pull request passed, since that run covered the combined changes |
Status check timeout | How long the queue waits for CI before treating checks as failed | Too short removes slow but healthy builds; too long lets a stuck check hold the queue |
Maximum pull requests to merge (1 to 100) | The most pull requests merged into the base branch at once | Lower caps how many changes go out in one deploy |
Minimum pull requests to merge (1 to 100) | The fewest pull requests merged at once | Higher batches merges when each merge starts a long CI build or deploy |
Wait time | How long to wait for the minimum before merging a smaller group | Longer makes bigger batches; shorter merges sooner when the queue is quiet |
With five pull requests queued and build concurrency set to 3, GitHub sends checks_requested for the first three and sends the fourth when one of them reports a result. Group sizes don't reduce the queue's CI runs: merge limits "do not combine merge_group builds" and only affect merges once groups have passed.
In a ruleset, "Only merge non-failing pull requests" is called "Require all queue entries to pass required checks", the two merge limits are "Minimum/maximum group size", and the wait time is "Wait time to meet minimum group size" (ruleset settings).
Adding a pull request to the queue
On the pull request, click Merge when ready, then Confirm merge when ready. You can click it before every requirement passes, and GitHub adds the pull request once they're met. From a terminal, gh pr merge adds the pull request to the queue if its required checks have passed and turns on auto-merge if they haven't (Merging a pull request with a merge queue).
To see the queue, open the repository's Branches page, or scroll to the bottom of the timeline on a pull request that requires the queue and click the merge queue link. GitHub CLI can't show it (Viewing merge queues).
GitHub's stacked pull requests "are merge-queue aware", and merging one pull request in a stack also merges every pull request below it (About stacked pull requests). How stacked PRs work covers the stack itself. Since stacked pull requests became generally available on October 6, 2026, a stack enters and lands through the merge queue as a single merge group, and the merge commit method creates one merge commit per pull request (GitHub changelog).
Common problems and how to fix them
Required checks never report on merge_group
The queue waits for checks that never arrive, and the pull request is eventually removed for timing out. Add merge_group to every workflow that produces a required check. On GitHub Enterprise Cloud, that includes workflows your organization requires through rulesets (Managing a merge queue).
Check workflow filters too. A workflow skipped by path filtering, branch filtering or a commit message leaves its checks "Pending", and they block merging. A job skipped by an if: condition reports "Success" instead (Handling skipped but required checks).
Required checks that differ between pull_request and merge_group
GitHub couples the two. The queue uses the required status checks from the same branch protection rule or ruleset as the pull request. Every required check has to report on the merge group, including one that only makes sense on a pull request, such as a job that validates the pull request title.
GitHub's status checks reference says a skipped job reports "Success" and "will not prevent a pull request from merging, even if it is a required check." That lets you choose where each job does its work:
Flip the condition to github.event_name == 'pull_request' for a job like the title check, which should pass in the queue without running. Running a slow suite only in the queue saves CI on every push to a pull request, but authors see its failures later, and each failure removes a pull request and rebuilds the groups behind it.
Flaky tests
A test that fails at random hurts more in a queue: one false failure removes a pull request, and every group built on top of it runs again. Turning off "Only merge non-failing pull requests" lets a pull request merge when the last pull request in its group passes. GitHub suggests it "if you have intermittent test failures, but don't want false negatives to hold up the queue." Set the status check timeout above your slowest healthy build so slow runs aren't treated as failures.
Jumping the queue
When you add a pull request, you can move it to the top of the queue. GitHub warns that jumping "will cause a full rebuild of all in-progress pull requests," because reordering breaks the commit graph, and that heavy use slows merges for the branch (Jumping to the top of the queue).
Admins can also merge without the queue, with the "Merge without waiting for requirements to be met (bypass branch protections)" option where branch protection allows it, or with gh pr merge --admin. A change merged that way skips the queue's combined test.
Removing a pull request
Click Remove from queue at the bottom of the pull request, or use the ... menu next to it on the queue page. GitHub CLI can't remove a pull request from a queue, but the GraphQL API has a dequeuePullRequest mutation.
GitHub also removes pull requests on its own: when CI reports failures for the merge group, when checks time out, or when a branch protection failure can't be resolved automatically (why a pull request was removed). The reason shows on the pull request's timeline. Fix the cause, then click Merge when ready again.
Where review fits before the queue
The queue tests whether the required checks pass on the combined code. Whether the change should merge at all is settled earlier, in code review, because GitHub only admits a pull request that meets the branch's requirements. Those requirements can include required approvals and code owner reviews, and a CODEOWNERS file decides who must approve which paths.
AI code review fits at that stage as well. Once cubic's GitHub App is installed, it reviews new pull requests automatically, and its auto-approval can submit the GitHub approval for low-risk changes that match the repository's policy. Auto-approval is off by default, and a Shadow mode shows what it would have approved without approving anything. GitHub branch protection still applies, including required checks, required reviewers and code owner rules. On a branch that requires a merge queue, the button in the Merge menu of cubic's pull request view reads "Add to merge queue", and the menu shows "Queued to merge" while the pull request waits (Check and merge).
FAQ
Does a merge queue run CI twice?
Yes. Required checks run on the pull request and again on each merge group that contains it. The merge group is a separate commit with its own SHA, and the queue waits for the required checks to report on that commit, so the pull request's earlier results don't count. To cut the cost, run your slowest suite only in the queue, with a job condition on the merge_group event.
What's the difference between auto-merge and a merge queue?
Auto-merge merges a single pull request "after all required reviews and status checks pass", and a repository has to enable it before anyone can use it (auto-merge docs). It doesn't test the pull request together with other pull requests, and GitHub disables it if someone without write permissions pushes to the head branch or switches the base branch. The two can work together: on a branch that requires a queue, gh pr merge turns on auto-merge for a pull request whose required checks haven't passed yet.
Does the merge queue rebase my pull request?
Only when the queue's merge method is rebase. You don't update your branch for the queue, because it tests its own temporary branches and then lands your change with the merge method set for the queue. On a queue branch, gh pr merge doesn't need a merge strategy flag either. If your pull request conflicts with the base branch, the queue removes it, and you resolve the conflict on your branch before queuing it again.
Can I use a merge queue on a repository owned by a personal account?
GitHub's availability statement only covers repositories owned by organizations: public ones on any plan and private ones on GitHub Enterprise Cloud. A repository under a personal account isn't on that list (Managing a merge queue).
