This guide walks you through setting up the merge queue for your repository.
- Repository must be on GitHub
- You need admin access to the target repository
- Node.js 20.x installed (for local development/testing)
If you haven't already:
-
Create a new repository on GitHub:
- Name:
merge-queue - Visibility: Private (recommended) or Public
- Initialize with README: No (we have our own)
- Name:
-
Push the merge-queue code:
cd /path/to/merge-queue git remote add origin [email protected]:BloomAndWild/merge-queue.git git push -u origin main
-
Install dependencies and build:
npm install npm run build
-
Create initial release:
git tag v1.0.0 git push origin v1.0.0
-
Verify the tag appears on GitHub under Releases
That's it for the merge-queue repo — no state branch or additional setup needed.
The merge queue authenticates via a GitHub App. Installation tokens are generated automatically on each workflow run — no manual rotation, no user seat required, and a better audit trail.
-
Go to your organization settings → Developer settings → GitHub Apps → New GitHub App (or for a personal account: Settings → Developer settings → GitHub Apps)
-
Configure the app:
- GitHub App name:
Merge Queue(or any unique name) - Homepage URL: Your merge-queue repository URL
- Webhook: Uncheck "Active" (not needed)
- GitHub App name:
-
Under Repository permissions, set:
Permission Access Why Pull requests Read & Write Merge PRs, post comments, manage labels Contents Read & Write Update branches, delete merged branches Actions Read & Write Trigger workflow self-dispatch Commit statuses Read Read CI status results for validation Checks Read Read check run results for validation Metadata Read Required by default -
Under Where can this GitHub App be installed?, select "Only on this account"
-
Click "Create GitHub App"
-
Note the App ID shown on the app's settings page
-
Scroll down to Private keys and click "Generate a private key" — a
.pemfile will download automatically
-
On the app's settings page, click "Install App" in the sidebar
-
Select your organization (or personal account)
-
Choose "Only select repositories" and pick the repositories that need the merge queue
-
Click "Install"
In each target repository:
-
Go to Settings → Secrets and variables → Actions
-
Under the Variables tab, click "New repository variable":
- Name:
MERGE_QUEUE_APP_ID - Value: The App ID from step 6 above
- Name:
-
Under the Secrets tab, click "New repository secret":
- Name:
MERGE_QUEUE_APP_PRIVATE_KEY - Value: Paste the full contents of the
.pemfile you downloaded
- Name:
Copy the example workflows from examples/target-repo-workflows/.
-
Create
.github/workflows/directory if it doesn't exist:mkdir -p .github/workflows
-
Copy the three workflow files from the merge-queue repository:
Using curl
cd .github/workflows/ BASE=https://raw.githubusercontent.com/BloomAndWild/merge-queue/main/examples/target-repo-workflows curl -O "$BASE/merge-queue-entry.yml" curl -O "$BASE/merge-queue-manager.yml" curl -O "$BASE/merge-queue-remove.yml"
Manual copy
- Copy files from
examples/target-repo-workflows/ - Paste into your repo's
.github/workflows/
- Copy files from
-
Update the workflow files:
Find and replace
BloomAndWildwith your GitHub organization/username:# Before uses: BloomAndWild/merge-queue/src/actions/add-to-queue@v1 # After uses: mycompany/merge-queue/src/actions/add-to-queue@v1
-
Customize configuration (optional):
Edit the workflow files to adjust settings like:
merge-method:squash,merge, orrebaseblock-labels: Labels that prevent queueingupdate-timeout-minutes: How long to wait for tests
To customise the trigger label, set the
MERGE_QUEUE_LABELrepository variable instead of editing workflow files — the example workflows already reference it with a fallback toready.
The queue uses several labels. Create them in your repository.
Tip: The trigger label defaults to
ready. To use a different name, set theMERGE_QUEUE_LABELrepository variable (Settings → Secrets and variables → Actions → Variables) and create a label with that name instead.
-
Go to Issues → Labels → New label
-
Create these labels:
Label Color Description ready(or your custom trigger label)#0e8a16(green)Add this to queue a PR queued-for-merge#fbca04(yellow)PR is waiting in queue merge-processing#1d76db(blue)PR is being processed merge-updating#5319e7(purple)Branch is being updated merge-queue-failed#d73a4a(red)Validation or tests failed merge-queue-conflict#b60205(dark red)Merge conflict detected Quick create script:
# Requires GitHub CLI (gh) # Replace "ready" with your custom label name if you set MERGE_QUEUE_LABEL gh label create "ready" --color "0e8a16" --description "Add this to queue a PR" gh label create "queued-for-merge" --color "fbca04" --description "PR is waiting in queue" gh label create "merge-processing" --color "1d76db" --description "PR is being processed" gh label create "merge-updating" --color "5319e7" --description "Branch is being updated" gh label create "merge-queue-failed" --color "d73a4a" --description "Validation or tests failed" gh label create "merge-queue-conflict" --color "b60205" --description "Merge conflict detected"
git add .github/workflows/
git commit -m "Add merge queue workflows"
git push origin main-
Create a simple PR (fix typo, update README, etc.)
-
Get it approved (ensure it has required approvals)
-
Ensure all checks are passing
-
Add the trigger label (default:
ready) -
Watch for:
- ✅
queued-for-mergelabel added - ✅ Comment confirming addition to queue
- ✅
-
Wait up to 5 minutes (or manually trigger the queue-manager workflow)
-
Watch for:
- ✅
merge-processinglabel added - ✅ PR automatically merged
- ✅ Branch deleted (if configured)
- ✅ Success comment posted
- ✅
-
Create a PR with failing tests
-
Add the trigger label (default:
ready) -
Verify:
- ✅
merge-queue-failedlabel added - ✅ Comment explaining failure
- ✅ PR removed from queue
- ✅
-
Create PR #1 and merge it manually
-
Create PR #2 (now behind master)
-
Add the trigger label (default:
ready) to PR #2 -
Verify:
- ✅ Branch automatically updated
- ✅ Tests re-run
- ✅ PR merges after tests pass
Edit .github/workflows/merge-queue-entry.yml and merge-queue-manager.yml:
with:
# Merge method
merge-method: squash # Change to 'merge' or 'rebase'
# Branch deletion
delete-branch-after-merge: true # Change to false to keep branches
# Blocking labels
block-labels: do-not-merge,wip,draft # Add custom labels
# Test timeout
update-timeout-minutes: 30 # Increase for slow test suitesCause: Incorrect action reference syntax or private repo access not enabled
Solution:
- Ensure the path comes before
@refin action references:# Correct uses: BloomAndWild/merge-queue/src/actions/add-to-queue@v1 # Wrong — GitHub treats everything after @ as the version uses: BloomAndWild/merge-queue@v1/src/actions/add-to-queue
- If the merge-queue repository is private, enable access from other repos in the same organisation: go to the merge-queue repo → Settings → Actions → General → "Access" section → select "Accessible from repositories in the organisation"
Cause: App permissions or workflow file errors
Solution:
- Check Actions tab for error messages
- Validate workflow YAML syntax
- Verify
MERGE_QUEUE_APP_IDvariable andMERGE_QUEUE_APP_PRIVATE_KEYsecret exist - Confirm the GitHub App is still installed on the repository
Cause: Insufficient permissions on the GitHub App
Solution:
- Check the App's permission settings match the table in Step 1
- Verify the App is installed on this specific repository
Cause: Validation failures or queue stuck
Solution:
- Check PR comments for error details
- View Actions logs in target repository
- Search for PRs with
queued-for-mergeormerge-processinglabels to see queue state - Manually trigger queue-manager workflow
Cause: Tests take longer than configured timeout
Solution:
- Increase
update-timeout-minutesin workflows - Optimize test suite performance
Search for open PRs with queue labels to see the current state:
- Waiting: PRs with
queued-for-mergelabel - Processing: PRs with
merge-processinglabel - Failed: PRs with
merge-queue-failedormerge-queue-conflictlabel
-
Go to target repository → Actions tab
-
Select workflow run
-
View logs for debugging
No manual token rotation is needed — installation tokens are generated automatically on each workflow run and expire after one hour.
If you need to rotate the App's private key (e.g. as part of a security
policy), generate a new one on the App settings page, update the
MERGE_QUEUE_APP_PRIVATE_KEY secret in each target repository, then
optionally revoke the old key.
When new version is released:
-
Pull latest changes to merge-queue repo:
git pull origin main
-
Tag new release:
git tag v1.1.0 git push origin v1.1.0
-
Update workflow files in target repos:
# Change from uses: BloomAndWild/merge-queue/src/actions/[email protected] # To uses: BloomAndWild/merge-queue/src/actions/[email protected]
-
Security
- Installation tokens are short-lived, auto-generated, and not tied to a user account
- Install the GitHub App only on repositories that need the merge queue
- Restrict the "Where can this GitHub App be installed?" setting to your own account
- Never commit private keys or secrets to git
-
Testing
- Test queue with dummy PRs first
- Monitor first few real PRs closely
- Keep eye on Action logs initially
-
Configuration
- Start with default settings
- Adjust based on team workflow
- Document custom configurations
-
Communication
- Inform team about merge queue
- Document in repository README
- Create team guide for using the trigger label (default:
ready)
- Read ARCHITECTURE.md to understand internals
- Check CONTRIBUTING.md for development guidelines
- Join discussions for questions and feedback
- Documentation: Check
/docsdirectory - Issues: Report bugs on GitHub
- Questions: Use GitHub Discussions