How to Enable GitHub Merge Queue with GitHub Actions: The 5-Minute Setup
Enabling GitHub's merge queue is a five-line YAML change plus one repository setting. Here's the working config, the four things that will break the first week, and how to know when you've outgrown it.
GitHub’s merge queue became generally available in 2023. Enabling it on a repo takes about five minutes if you know which settings to flip and what your existing workflow needs. Most posts on this topic are GitHub’s own marketing. This one is the version that tells you what breaks before you find out the hard way.
The minimum to turn it on
Two changes: a repository setting and a workflow trigger.
Repository setting. Go to Settings → Branches → Branch protection rules for your default branch. Edit the rule (or create one). Enable “Require merge queue”. Pick a merge method (merge commit, squash, rebase) and a maximum batch size if you want grouping.
Workflow trigger. Add the merge_group event to whatever CI workflow you want to run on the queue:
# .github/workflows/ci.yml
on:
pull_request:
merge_group:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm test
That is it. Push to main with this change, enable the rule, and the next PR you mark with “Merge when ready” enters the queue, runs CI on a queue branch, and merges if it passes.
Why merge_group matters
Without merge_group in your workflow’s on: block, your CI does not run when GitHub creates the queue branch. The queue waits for required checks that never start. PRs sit until they time out.
The merge_group event is what GitHub fires when it builds a temporary branch to test a queued PR. The branch name follows the pattern gh-readonly-queue/<base>/pr-<n>-<sha>. CI runs against that branch, GitHub waits for the configured required checks, and only then commits the merge to the actual base.
If you forget the merge_group event, this step never happens. The PR is queued and stays queued.
The required-check-name gotcha
This is the most common silent failure when teams turn on the queue.
When you require a check called test in branch protection, GitHub looks for a check with that exact name on the queue branch. Your workflow file says:
jobs:
test:
name: test
runs-on: ubuntu-latest
The check appears on the PR as test. So far so good. But on the queue branch, the same workflow may produce a check with a different name — for example, if your workflow uses name: at the workflow level and at the job level, the resulting check name on the queue branch can differ.
The symptom: PRs enter the queue, CI runs, all checks turn green, the queue still rejects the PR. Branch protection thinks the required check test did not appear.
The fix: open a PR, queue it, watch the Checks tab on the queue branch (gh-readonly-queue/main/pr-...). Compare the check name there to the name configured in branch protection. They must match exactly. If they do not, rename one to match the other.
PR CI vs queue CI: when to run the same workflow
The simplest setup runs the same workflow on both pull_request and merge_group. Every PR runs full CI on push, then runs full CI again on the queue branch when it is about to merge. It is correct and simple, but you pay for the duplication twice.
The cheaper setup splits them. PR CI runs lightweight checks (lint, unit tests). Queue CI runs the full suite including E2E and integration. Same code, two workflows, two different triggers:
# .github/workflows/pr-ci.yml — fast checks on every push
on:
pull_request:
jobs:
lint-and-unit:
# ...
# .github/workflows/queue-ci.yml — full suite only on the queue branch
on:
merge_group:
jobs:
full-suite:
# ...
This is the two-step CI pattern. For a team with 50 PRs per week where 30 reach the queue, that is 50 cheap runs plus 30 full runs instead of 50 full runs. We covered the cost math in How to Cut Your GitHub Actions CI Bill.
Common breakage in the first week
These are the failures we have watched teams hit within seven days of turning it on.
Required checks that never run on merge_group. Same shape as the workflow trigger gotcha. A required check is configured in branch protection, but the workflow that produces it does not have merge_group in its on: block. The queue branch is created, that workflow does not run, the check never appears, the queue waits forever. Audit every required check the day you enable the queue.
Workflows skipped by paths: filters. If your workflow has paths: filters (paths: ['src/**']), the queue branch may not match the filter even when the original PR did. GitHub does not retroactively expand the diff for queue-branch evaluation. The fix is usually to remove paths: filters from queue-required workflows, or to add paths-ignore: ['docs/**'] instead which inverts the matching logic.
Long CI cycles throttle queue throughput. If your CI takes 30 minutes and you queue 10 PRs in a morning, the last one merges 5 hours later in a serial queue. GitHub’s native queue does not batch by default and does not run PRs in parallel. Watch the queue depth in the first week. If wait times balloon, either invest in CI speed or look at a queue that supports batching and parallel testing.
Failure messages that hide the actual cause. When a queued PR fails, GitHub’s UI tends to show a generic “removed from queue” status without surfacing which specific check failed or why. You have to open the queue branch (gh-readonly-queue/...) and read its Checks tab manually. This is fine when it happens once a week and frustrating when it happens daily.
When you outgrow it
GitHub’s native queue is enough for a single repo with one team and modest PR volume. It runs into limits for monorepos (no scope-aware batching), high-throughput teams (no parallel queues, no batching), regulated environments (limited audit trail), and any setup that needs custom merge conditions, priorities, or schedule freezes. The full breakdown lives at Mergify vs GitHub merge queue.
If your situation matches the basic case, the native queue does the job and is included with your GitHub plan. If it does not, there are options — but enable the native version first, watch where it bottlenecks, and let the friction tell you what to reach for.