Renovate and Dependabot Without the 3 A.M. Regret: A Practical Automerge Playbook
The first time I turned on fully automated dependency updates, I turned them off again within a week. Every morning I'd wake up to eleven open PRs, most of them patch bumps to packages three layers deep in the tree that I had never heard of and had no opinion about, mixed in with the occasional PR that quietly bumped a major version of something load-bearing with a one-line changelog that said "see migration guide" and linked to nothing useful. I closed the tool's config, went back to manually running npm outdated every couple of weeks, and told myself automation "wasn't ready for us." It wasn't the tool. It was that I'd installed it with the defaults and never actually configured a policy.
The second time, a year later, I did it properly, and it's now one of the lowest-maintenance, highest-value pieces of infrastructure on every repo I touch. Here's what changed.
Start with tiers, not one blanket policy
The core mistake in a naive setup is treating all dependency updates the same way. A patch bump to lodash and a major version bump to react are not the same category of risk, and your config shouldn't treat them that way. I split updates into three tiers: things that automerge with no human involved, things that get a PR I still have to click merge on, and things that get flagged for real review time.
Here's the renovate.json that reflects that split, trimmed to the parts that matter:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"timezone": "America/New_York",
"schedule": ["before 6am on monday"],
"prConcurrentLimit": 5,
"packageRules": [
{
"matchUpdateTypes": ["patch", "pin", "digest"],
"automerge": true,
"automergeType": "branch",
"platformAutomerge": true
},
{
"matchUpdateTypes": ["minor"],
"matchCurrentVersion": "!/^0/",
"automerge": true,
"automergeType": "branch",
"requiredStatusChecks": ["build", "test", "typecheck"]
},
{
"matchUpdateTypes": ["major"],
"automerge": false,
"labels": ["dependencies", "major-version", "needs-review"],
"reviewers": ["team:platform"]
},
{
"matchUpdateTypes": ["minor"],
"matchCurrentVersion": "/^0/",
"automerge": false,
"labels": ["dependencies", "0.x-minor-treat-as-major"]
}
]
}
That matchCurrentVersion": "!/^0/" rule is doing more work than it looks like. Packages still on 0.x treat minor bumps as breaking changes under semver convention — a 0.4.0 to 0.5.0 jump can legally break your build. Automerging those the same way you'd automerge a stable 4.1.0 to 4.2.0 bump is how you get bitten. I learned this the hard way when a 0.x charting library automerged a "minor" update that silently changed a default axis behavior across every dashboard in the product.
Grouping: fewer PRs, same coverage
Eleven separate PRs for eleven separate patch bumps is noise even when each one is individually safe. Grouping related packages into a single PR cuts review overhead dramatically without reducing what actually gets checked, since your CI still runs the full suite against the combined branch:
{
"packageRules": [
{
"matchPackagePatterns": ["^@testing-library/"],
"groupName": "testing-library packages"
},
{
"matchPackagePatterns": ["^eslint", "^@typescript-eslint/"],
"groupName": "lint tooling"
},
{
"matchSourceUrlPrefixes": ["https://github.com/aws/aws-sdk-js-v3"],
"groupName": "AWS SDK v3"
}
]
}
I also group by "same release" for monorepo-published packages specifically, because updating @radix-ui/react-dialog without its sibling primitives is a common source of subtle version-skew bugs that don't show up until runtime.
Major versions: never automerge, but don't let them rot either
Major version PRs are exactly where I want a human, but "don't automerge" isn't the same as "ignore." A major-version PR that sits open for four months just accumulates conflict debt and makes the eventual upgrade harder, not easier. What's worked for us is a standing recurring calendar item — every other Friday, someone on rotation spends thirty minutes going through open major-version PRs, not necessarily merging them, but at minimum reading the changelog and either scheduling the work or explicitly deciding to defer with a reason recorded in the PR.
For the genuinely big ones — a React major, a Node LTS bump, a database driver major — Renovate's dependencyDashboard is worth turning on. It gives you a single pinned issue listing every pending update across the repo, including ones being deliberately held back, so nothing quietly disappears into PR-list scroll:
{
"dependencyDashboard": true,
"dependencyDashboardTitle": "Dependency Updates Overview"
}
CI gating is the part that actually makes automerge safe
None of the automerge rules above are trustworthy without a CI pipeline that would actually catch a real regression. This is the piece people skip and then blame the automerge tool when something breaks. Before I trust patch and minor bumps to merge themselves, I want, at minimum: the full test suite, a typecheck pass, a production build, and — if the app has one — a smoke test against a preview deployment.
.github/workflows/ci.yml
name: CI
on: [pull_request]
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run typecheck
- run: npm test -- --coverage
- run: npm run build
And in Renovate's config, I explicitly require those checks to pass before branch automerge fires, using requiredStatusChecks as shown above, plus GitHub's own branch protection rules configured to require the same checks — belt and suspenders, because I've seen Renovate's own automerge race a slow CI run in edge cases, and branch protection is the backstop that actually blocks the merge button regardless of what Renovate thinks happened.
Dependabot: the same ideas, different config shape
If your org is on Dependabot instead of Renovate — common if you're already deep in the GitHub ecosystem and want one less third-party app installed — the same tiering philosophy applies, just expressed differently, since Dependabot doesn't have Renovate's automerge-by-update-type primitives built in natively:
.github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
groups:
testing-tools:
patterns:
- "@testing-library/*"
lint-tools:
patterns:
- "eslint*"
- "@typescript-eslint/*"
ignore:
- dependency-name: "react"
update-types: ["version-update:semver-major"]
open-pull-requests-limit: 10
Dependabot doesn't automerge on its own — you pair it with a separate GitHub Actions workflow that merges only patch/minor PRs after checks pass, using the dependabot/fetch-metadata action to inspect the update type:
name: Dependabot auto-merge
on: pull_request
permissions:
pull-requests: write
contents: write
jobs:
automerge:
if: github.actor == 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- uses: dependabot/fetch-metadata@v2
id: metadata
- if: steps.metadata.outputs.update-type != 'version_update:semver-major'
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
I've run both tools across different teams and lean toward Renovate when I have the choice, mostly because the packageRules matcher system is more expressive out of the box and the dependency dashboard issue is genuinely useful for visibility — but Dependabot's tight native GitHub integration and zero-setup default experience is a completely reasonable choice, especially for smaller repos where you don't want to think about it at all.
What "working" actually looks like
On a healthy setup now, my mornings look like this: zero PRs waiting on me for patch and safe-minor bumps, because they merged themselves overnight after CI passed. One or two grouped PRs a week for related package clusters, which I skim and approve in under a minute because the diff is small and the tests already ran. And a dependency dashboard issue that gets a real look every two weeks, where major versions get scheduled deliberately instead of either rotting for six months or getting rushed through on a Friday afternoon. The volume of updates didn't go down — if anything it went up, since staying current is cheap now. What went away was the dread of opening the PR list on a Monday morning.
Related Posts
Sponsor Our Newsletter
Reach thousands of developers who are actively evaluating AI tools, MCP servers, and dev infrastructure. Our weekly newsletter goes to engaged technical decision-makers.
All sponsored content is clearly labeled per our editorial policy.