Configuring Renovate for Grouped Updates in a Monorepo
Renovate is opening a flood of individual PRs across your monorepo packages, and reviewers have stopped looking. This page shows how to group and schedule updates so the bot stays useful at monorepo scale.
Exact symptoms and error messages
Dozens of near-identical PRs land each week, one per package per dependency:
fix(deps): update dependency eslint to v9.15.0 (packages/ui)
fix(deps): update dependency eslint to v9.15.0 (packages/api)
fix(deps): update dependency eslint to v9.15.0 (packages/web)
... 27 more ...
Root cause analysis
By default Renovate treats each dependency in each package as its own update. In a monorepo where the same dependency is shared across packages, that multiplies into unreviewable noise. The fix is to group shared updates and pin the schedule, which aligns with routing lockfile churn through a few reviewable PRs, per Lockfile Management Strategies.
Monorepos amplify the noise because the same dependency is declared in many packages. Renovate, by default, opens an update per dependency per package, so a single eslint bump can fan out into a dozen near-identical PRs. Reviewers pattern-match that as noise and stop looking, which defeats the entire point of automation — the bumps pile up unreviewed and the repo drifts anyway. Grouping collapses that fan-out back into one intent-bearing change.
Scheduling addresses a second failure mode: a continuous stream of PRs that arrive at random times competes with feature work for attention. Pinning updates to a predictable window turns dependency maintenance into a routine chore rather than a constant interruption, and it keeps CI load — every PR triggers the affected build — bounded and predictable.
The flood of individual pull requests comes from Renovate's default of treating each dependency in each package as its own update. In a monorepo where the same dependency is declared across many packages, a single version bump fans out into a PR per package, and reviewers pattern-match that volume as noise and stop looking — which defeats the automation, because the updates pile up unreviewed and the repo drifts anyway. Grouping collapses that fan-out into one intent-bearing change that a reviewer can actually evaluate.
The volume is also unbounded in time without a schedule. Renovate by default opens updates as soon as new versions appear, so a busy dependency set produces a continuous stream that competes with feature work for attention. Pinning updates to a window turns dependency maintenance into a predictable, batchable chore and bounds the CI load each update round generates, which matters because every update PR triggers the affected build across the workspace.
Resolution and configuration patch
// renovate.json
{
"extends": ["config:recommended"],
"schedule": ["before 6am on monday"],
"packageRules": [
{ "matchUpdateTypes": ["minor", "patch"], "groupName": "all non-major", "groupSlug": "non-major" },
{ "matchDepTypes": ["devDependencies"], "groupName": "dev dependencies" },
{ "matchUpdateTypes": ["major"], "dependencyDashboardApproval": true }
]
}
Group related updates, schedule them, and cap concurrency so the queue is reviewable:
// renovate.json
{
"extends": ["config:recommended"],
"schedule": ["before 6am on monday"],
"prConcurrentLimit": 5,
"packageRules": [
{ "matchUpdateTypes": ["minor", "patch"], "groupName": "non-major" },
{ "matchDepTypes": ["devDependencies"], "groupName": "dev dependencies" },
{ "matchUpdateTypes": ["major"], "dependencyDashboardApproval": true },
{ "matchPackagePatterns": ["^@typescript-eslint", "^typescript$"], "groupName": "typescript" }
],
"vulnerabilityAlerts": { "labels": ["security"], "schedule": ["at any time"] }
}
The name-pattern group keeps a toolchain's packages moving in lockstep, and the separate vulnerabilityAlerts rule keeps security fixes on an immediate fast lane rather than waiting for the weekly batch.
CLI validation and debug commands
# Dry-run Renovate locally to preview the PRs it would open
LOG_LEVEL=debug npx renovate --dry-run=full
# Confirm shared deps resolve to one version across the workspace
pnpm why eslint
Prevention and CI guardrails
- Group minor/patch updates into a single scheduled batch.
- Require approval for majors via the dependency dashboard.
- Pin a schedule so PRs arrive predictably instead of continuously.
- Keep shared dev tooling (eslint, tsconfig) in its own group for one-glance review.
-
Group minor and patch updates into one scheduled batch, with dev dependencies in their own group.
-
Keep majors behind an approval step so each breaking bump is reviewed in isolation.
-
Give security updates their own immediate, ungrouped rule so they never wait for the batch.
-
Cap
prConcurrentLimitso a large update round lands as a controlled trickle. -
Group minor and patch updates into one scheduled batch, with dev dependencies in their own group.
-
Use name-pattern groups so a toolchain's packages (TypeScript, its ESLint plugin) move in lockstep.
-
Keep majors behind approval and security fixes on an immediate, ungrouped fast lane.
-
Enable lockfile maintenance on its own schedule so transitive resolutions stay fresh.
-
Cap concurrency so a large update round lands as a controlled trickle rather than a flood.
Separating security updates from the batch
Grouping routine bumps is right, but security fixes should not wait for Monday's batch. Give vulnerability alerts their own rule so they arrive immediately, labeled and ungrouped, while everything else stays batched. That preserves the reviewable cadence for routine work without delaying the updates that actually matter.
{
"vulnerabilityAlerts": {
"labels": ["security"],
"schedule": ["at any time"],
"groupName": null
}
}
The result is a two-speed pipeline: security fixes on a fast lane with their own PRs, and routine minor/patch updates collected into a predictable weekly batch. Reviewers learn to trust the label, and nothing important is buried in a pile of @types/* bumps.
An automerge policy for low-risk updates
Grouping cuts the count of PRs; automerge cuts the human touch on the ones that are genuinely safe. The key is a policy narrow enough to trust: patch and minor updates to dev dependencies, gated on a green CI run, are the classic candidates. Production dependencies and any major bump stay manual.
{
"packageRules": [
{
"matchDepTypes": ["devDependencies"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true,
"automergeType": "pr",
"platformAutomerge": true
}
]
}
Automerge is only as safe as the test suite behind it, so treat it as a statement of confidence in CI. Start with dev dependencies where a regression is caught by your own build, watch how it behaves for a few weeks, and widen the policy only if the signal stays clean. The goal is to spend human review on the updates that can actually break production, not on a stream of @types/* patches.
Grouping strategy that matches your review process
The right grouping is the one your team will actually review, so the strategy should map to how dependencies get looked at rather than to an abstract ideal. A small team that clears dependencies in a weekly session wants a single batched pull request they can approve in one sitting; a larger team with domain owners may prefer groups split by area — build tooling, testing, runtime — so the right person reviews each. The principle is that every PR should be a coherent unit of judgment: a batch mixing a risky runtime bump with a dozen type-definition patches forces the reviewer to either scrutinize everything or rubber-stamp the lot.
Name-pattern groups are the tool for keeping interdependent packages together. A TypeScript group holding typescript and its @typescript-eslint peers, or a testing group holding a runner and its plugins, ensures those packages update in lockstep and avoids the version-mismatch build failures that come from bumping one without the others. Combined with a schedule and concurrency limits, name-pattern grouping turns a monorepo's update stream from an unreviewable flood into a small set of coherent, predictable PRs — which is the difference between an update bot that keeps the repo current and one whose PRs everyone ignores.
Automerging the safe majority
Grouping cuts the number of PRs; automerge cuts the human touch on the ones that are genuinely safe, and together they let scarce review attention go to the updates that can actually break production. The classic safe set is patch and minor updates to dev dependencies, gated on a green CI run — a regression there is caught by your own build rather than shipped to users. Production dependencies and any major bump stay manual.
{ "matchDepTypes": ["devDependencies"], "matchUpdateTypes": ["minor", "patch"], "automerge": true }
Automerge is only as trustworthy as the test suite behind it, so treat enabling it as a statement of confidence in CI. Start narrow with dev dependencies, watch the behavior for a few weeks, and widen the policy only if the signal stays clean. Keep security fixes on their own reviewed fast lane rather than blind-automerging them. The result is a two-speed pipeline where automation clears the trivial majority and humans see exactly the updates that warrant judgment — which is the entire point of taming the monorepo update flood.
Lockfile maintenance and keeping the graph fresh
Grouping and scheduling update the versions your manifests declare, but a monorepo's lockfile also accumulates drift in its transitive dependencies that no manifest change touches — sub-dependencies that could resolve to newer patched versions if the lockfile were regenerated. Renovate's lockFileMaintenance addresses this by periodically regenerating the lockfile on its own schedule, opening a pull request that refreshes transitive resolutions without changing any declared range. This keeps the resolved graph current with upstream patches that arrive below the level your manifests express.
{ "lockFileMaintenance": { "enabled": true, "schedule": ["before 6am on monday"] } }
Running lockfile maintenance on a schedule, separate from the grouped version updates, means the graph stays fresh in two dimensions: the declared versions move through the grouped update PRs, and the transitive resolutions move through the maintenance PR. Both go through CI, so a transitive update that changes behavior is caught before merge. For a monorepo, where the single root lockfile pins a large transitive graph, this scheduled refresh is what prevents the resolved dependencies from silently falling behind the patches available upstream while the declared ranges stay unchanged.
Frequently Asked Questions
Does grouping hide which package changed?
No — the grouped PR still updates each affected package's manifest and the lockfile. Grouping changes how many PRs you review, not what is in them.
Can Renovate keep security updates ungrouped?
Yes. Give vulnerabilityAlerts its own rule and label so security fixes arrive as separate, fast-lane PRs while routine bumps stay batched.
Will grouping delay a critical security patch?
Not if you give vulnerabilityAlerts its own rule with an immediate schedule and no group. Routine version bumps stay batched, while security fixes bypass the batch and arrive as separate, labeled PRs the moment they are available.
How do I stop Renovate flooding CI on Monday morning?
Cap prConcurrentLimit and prHourlyLimit so PRs land in a controlled trickle, and keep the batch grouped so it is one PR rather than dozens. That bounds the CI load each update window generates.
Can one Renovate config serve many repos?
Yes — put your rules in a shared preset and extends it from each repo's renovate.json. Repositories inherit the grouping, schedule, and limits, and only override what is genuinely repo-specific.
How do I stop Renovate opening dozens of PRs in a monorepo?
Group updates so a shared dependency's bump across packages becomes one PR: batch minor/patch into a non-major group, dev dependencies into their own, and use name-pattern groups for toolchains. Add a schedule and a prConcurrentLimit so the queue stays reviewable.
How do I keep a toolchain like TypeScript updating together?
Use a name-pattern group matching typescript and @typescript-eslint/* so they bump in lockstep, avoiding the version-mismatch build failures that come from updating one without its matching peers.
Does grouping delay security updates?
Not if you give vulnerabilityAlerts its own rule with an immediate schedule and no group. Routine bumps stay batched while security fixes arrive as separate, labeled PRs the moment they are available.
What does Renovate's lockfile maintenance do?
It periodically regenerates the lockfile to refresh transitive dependency resolutions — sub-dependencies that could move to newer patched versions — without changing any declared range. On a schedule separate from version updates, it keeps the resolved graph current with upstream patches your manifests do not express.
How do I preview what Renovate would do before enabling it?
Run Renovate locally in dry-run mode (LOG_LEVEL=debug npx renovate --dry-run=full) against your repo to see the grouped PRs it would open, so you can tune the grouping and schedule before it starts opening real pull requests.
Can Renovate update the lockfile without changing any manifest?
Yes — its lockfile-maintenance feature regenerates the lockfile on a schedule to refresh transitive resolutions, opening a PR that changes no declared range. This keeps the resolved graph current with upstream patches your manifests do not express.
Related
- Dependency Auditing and Automated Updates — the overview for auditing and update automation.