monorepo.
monorepo11 min read

Renovate vs Dependabot in a Polyglot Monorepo: Choosing the Right Update Bot

Renovate vs Dependabot in a Polyglot Monorepo: Choosing the Right Update Bot

Let me start with a confession. The first polyglot monorepo I helped maintain shipped a Friday-night CVE alert for a transitive npm dep, a Rust crate yank notice, and a base-image rebuild request from our security team -- all in the same 24 hours. Three ecosystems, three update bots, three different definitions of "urgent." By Monday morning I had thirty-seven open PRs and no idea which one to merge first. That weekend is what taught me to take dependency-update tooling seriously, and it's the reason I'm writing this article.

A polyglot monorepo is the kind of repository where a single git pull brings down a Rust workspace, a Python uv workspace, a TypeScript bun project, a few Dockerfiles, a docker-compose.yml, a couple of GitHub Actions workflows, and -- if you're unlucky -- a Terraform module nobody has touched in eight months. Each of those ecosystems has its own dependency resolver, its own lockfile format, and its own definition of "minor version bump." Managing those updates by hand is how supply-chain CVEs become Friday-night incidents.

Two tools dominate the automated dependency-update space on GitHub: Dependabot, the GitHub-native solution that ships with every repository, and Renovate, the open-source bot originally built by Rhys Arkins and now maintained by Mend. Both will open pull requests when a dependency has a new version. The interesting question is what happens when your repository isn't a single Node.js app, but a workspace with seven manifest formats in five directories.

In this article I'll walk through how each bot handles a polyglot monorepo across the dimensions that actually matter day-to-day: ecosystem coverage, configuration ergonomics, grouping and noise control, scheduling, security alerts, and how each fails under real-world load.

1. The Honest Baseline: What Both Bots Do Well

What if the two bots you're picking between are, for the boring 80% of the job, more or less interchangeable? They are -- and admitting that upfront is the only honest place to start a comparison. Both will:

  • Detect a manifest file (package.json, Cargo.toml, pyproject.toml, go.mod, etc.) and open a pull request when a dependency in it has a newer version.
  • Respect semver ranges declared in the manifest, so they won't force a major version bump unless you explicitly opt in.
  • Run the relevant lockfile updater (npm install, cargo update -p <crate>, uv lock --upgrade-package, etc.) so the PR is mergeable without local work.
  • Update transitive dependencies pinned in lockfiles when a vulnerability advisory says you should.
  • Plug into GitHub's security advisory database so a known-CVE bump is prioritised.

If your repository is a single ecosystem with fewer than fifty direct dependencies, either bot is fine. The defaults are sensible. You'll get PRs. Pick whichever your CI happens to be friendlier with and move on.

The pain starts at scale and at the boundaries between ecosystems -- which is exactly the case in a polyglot monorepo.

2. Ecosystem Coverage: The First Filter

A colleague once told me his team picked Dependabot in an afternoon because "it supports everything we need," then spent the following quarter writing a homegrown cron job because it turned out the Helm values file and the pinned kubectl version in a Bash script were, in fact, things they needed. Coverage is the first filter for a reason. As of late 2025, Dependabot covers the obvious ones -- npm, pip, Maven, Gradle, NuGet, Cargo, Go modules, Composer, Bundler, Docker, GitHub Actions, Terraform, and a handful of others. The full and current list lives in the Dependabot ecosystems reference. What it doesn't cover, it simply doesn't cover; you can't teach it a new manifest format.

Renovate's coverage is wider and more uneven. Its managers list currently enumerates over ninety managers, including some that are unusual: nix, bazel, helm-values, flux, pep621 for PEP 621 pyproject.toml, pep723 for inline PEP 723 script metadata, pixi, gleam, several Bicep variants, and a regex manager that lets you teach it any text-based version string by writing a regex with named capture groups. That regex manager is the escape hatch that solves a depressingly common monorepo problem: "we pin the version of some CLI tool in a Bash script and it has no manifest."

For a polyglot monorepo, the practical question is: what languages and tools do you actually use? If the answer is contained inside Dependabot's supported list, ecosystem coverage is a wash. If the answer includes anything custom -- a homegrown manifest, a Helm chart pinned in YAML, a CLI version in a Makefile, an inline # requires comment in a script -- Renovate's customManagers (formerly regexManagers) is the only realistic option. The full DSL is documented in the Renovate custom managers guide.

3. Configuration: One Big Config vs N Little Configs

Why do two tools solving the same problem -- "tell me about new versions" -- end up with config files that look nothing alike? The answer is an architectural decision each project made on day one, and once you see it, every other difference in this article becomes downstream of it.

Dependabot uses a single .github/dependabot.yml file, and within it you declare one update block per ecosystem and per directory. If your monorepo has a Rust workspace at apps/desktop/, a Python workspace at services/, a TypeScript project at webapp/, and Dockerfiles in infra/, that means at least four separate update blocks, each with their own schedule, labels, and reviewers. The configuration file grows linearly with the number of ecosystem/directory pairs. Here is a representative excerpt:

# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "cargo"
    directory: "/apps/desktop"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 5
  - package-ecosystem: "pip"
    directory: "/services/api-gateway"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 5
  - package-ecosystem: "npm"
    directory: "/webapp"
    schedule:
      interval: "daily"
    open-pull-requests-limit: 10
  - package-ecosystem: "docker"
    directory: "/infra"
    schedule:
      interval: "weekly"
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"

This is fine for five blocks. By the time you have twenty-five it's a copy-paste swamp, and there's no mechanism to share configuration between blocks. You can't define a "default labels and reviewers" once and inherit; every block has to re-declare.

Renovate takes the opposite approach. Its configuration model is a single JSON or JSON5 file (renovate.json, renovate.json5, or .github/renovate.json) that declares global defaults at the top level and uses a packageRules array to scope overrides by manager, package pattern, file path, dependency type, or any combination thereof. The same monorepo as above might look like:

{
  $schema: "https://docs.renovatebot.com/renovate-schema.json",
  extends: [
    "config:recommended",
    "group:monorepos",
    "schedule:weekends",
    ":semanticCommits",
  ],
  rangeStrategy: "bump",
  prHourlyLimit: 4,
  prConcurrentLimit: 12,
  packageRules: [
    {
      matchManagers: ["npm"],
      matchFileNames: ["webapp/**"],
      groupName: "webapp npm dependencies",
      schedule: ["before 6am on monday"],
    },
    {
      matchManagers: ["cargo"],
      groupName: "rust workspace",
      schedule: ["before 6am on monday"],
    },
    {
      matchManagers: ["pip_requirements", "pep621"],
      groupName: "python deps",
    },
    {
      matchDepTypes: ["devDependencies"],
      automerge: true,
      automergeType: "branch",
    },
  ],
}

The two snippets don't encode equivalent behaviour -- that's the point. Renovate lets you start from a community preset (config:recommended, group:monorepos, schedule:weekends) and only declare what you want different from it. The list of presets is in the Renovate presets index. Dependabot has no preset system; what you write is what you get.

If your monorepo configuration fits comfortably in a single screen of YAML, Dependabot's verbosity is a non-issue. If you have any ambition to share rules across ecosystems -- "always pin GitHub Action SHAs," "auto-merge dev dependency patches across all languages," "never bump Postgres major" -- Renovate's preset-and-rules model will save you weeks of churn over the next two years.

4. Grouping: The Noise Problem

In a polyglot monorepo, the single biggest cause of bot fatigue is too many PRs. A weekly run that opens forty PRs is one that nobody reviews. Both bots support grouping, but the depth of control differs sharply.

Dependabot groups are declared per update block using a groups key, with patterns and dependency-type selectors. You can say "group all aws-sdk-* packages into one PR" or "group all minor and patch updates into one PR per ecosystem." That's genuinely useful and is a relatively recent improvement to the tool. What you can't easily do is cross-cut by file path within an ecosystem, or build complex selectors that combine matchDepTypes, matchPackagePatterns, matchUpdateTypes, and matchCurrentVersion simultaneously.

Renovate's packageRules is essentially a small rule engine. The matcher keys compose: you can write a single rule that says "for npm dev dependencies in the webapp/ directory whose current version is < 5.0.0 and whose update type is patch, group them under the name webapp dev patches and auto-merge them after 3 days of stability." That kind of expressiveness is overkill for a small repo and indispensable for a large one.

The practical effect is that a well-tuned Renovate config in a busy monorepo opens five to ten meaningful PRs per week, each one batching tens of underlying version bumps. The same monorepo on Dependabot tends to open thirty to fifty PRs unless you spend significant time on the groups blocks.

5. Scheduling and Rate Limiting

Both bots support cron-style scheduling, but Renovate's schedule language accepts a richer set of expressions: natural-language strings ("before 6am on monday"), traditional cron, and multiple windows per rule. You can stack a schedule per packageRules entry, which means low-risk batches can run nightly while major version bumps wait for a quarterly window.

Dependabot's schedule is one of daily, weekly, or monthly, with optional time and timezone. That's enough for most teams, and it has the considerable virtue of being impossible to misconfigure. Renovate's flexibility is also Renovate's footgun: if you write a schedule that never matches, Renovate silently never runs the rule, and the only way to find out is to read the Dependency Dashboard issue it auto-maintains in your repo.

That Dependency Dashboard, incidentally, is one of Renovate's most underrated features. It's a single GitHub issue that lists every pending PR, every rate-limited update, every error, and every awaiting-approval major bump, with checkboxes you can tick to trigger or rebase any of them. There's no Dependabot equivalent; Dependabot's status lives in the Insights tab and is much harder to skim.

6. Security Updates and Vulnerability Response

Both bots wire into vulnerability databases. Dependabot pulls from the GitHub Advisory Database, which aggregates GHSA, NVD, and a handful of language-specific feeds. Renovate uses the same advisory data plus its own enrichment layer, and it can open vulnerability PRs that are separate from the normal version-bump stream -- useful when you want the security pipeline to bypass the normal grouping and schedule rules.

The latency to first PR after a CVE disclosure is roughly comparable in practice -- usually within hours, occasionally within a day. The difference shows up in how each bot handles a vulnerable transitive dependency that isn't directly listed in any of your manifests. Dependabot will open a PR pinning the transitive in the lockfile when the ecosystem supports it (npm overrides, Yarn resolutions). Renovate does the same and additionally supports packageRules that can force a minimum version constraint on any matching dependency across the whole repo, which is useful when you want a long-lived guardrail rather than a one-shot PR.

7. Failure Modes

This is the section that most comparisons skip. Both bots will, at some point, leave a PR in a broken state. The interesting question is how easy it is to recover.

Dependabot failures tend to be opaque. A PR that fails to update its lockfile will close with a comment like "Dependabot encountered an error" and a link to a CI log that may or may not be informative. Recovery usually involves manually running the equivalent local command, committing the lockfile, and closing the PR. There's no retry button.

Renovate failures are typically logged into the Dependency Dashboard with the underlying error message and a checkbox to retry. The bot is also notably more verbose in PR descriptions about what it did and why -- release notes, changelogs, age of release, adoption percentage -- which makes triage faster when something looks wrong.

8. A Decision Framework

After running both bots on monorepos of varying sizes, the recommendation distils to roughly this:

  • Pick Dependabot if your monorepo touches two or three ecosystems, you don't need to group across directories, you value zero-config setup, and you'd rather not run another GitHub App. Dependabot is built in, free, and good enough.
  • Pick Renovate if your monorepo touches four or more ecosystems, you have any custom version pinning that requires the regex manager, you want preset-driven configuration you can share across repositories, you need fine-grained grouping and auto-merge rules, or you want the Dependency Dashboard issue as a single triage surface.
  • Don't run both on the same paths. They'll fight, and the resulting PR storm will train your team to ignore both bots forever.

Whichever you choose, the failure mode that actually hurts a polyglot monorepo isn't picking the "wrong" tool -- it's picking either tool and then leaving its config at defaults for six months while the dependency graph drifts. The bot is a force multiplier on whatever review discipline you already have. Spend the afternoon writing a config that matches your team's tolerance for PR noise, and revisit it whenever a new ecosystem enters the repo. That investment pays off the first time a critical CVE lands and the PR is already open, already green, and already grouped with the three other safe bumps it depends on.