Automating Dependency Updates with Renovate

Permalink to "Automating Dependency Updates with Renovate"

Part of Continuous Dependency Monitoring, this page gives a production renovate.json and its Dependabot equivalent for turning dependency drift and security advisories into reviewable pull requests automatically.

Quick Reference

Permalink to "Quick Reference"
Property Value
Config file location renovate.json (repo root) — also .github/renovate.json, .renovaterc.json, or renovate key in package.json
Schema https://docs.renovatebot.com/renovate-schema.json
Base preset extends: ["config:recommended"]
Routine schedule schedule: ["before 6am on monday"]
Security fix schedule vulnerabilityAlerts.schedule: ["at any time"]
Version strategy rangeStrategy: "pin" (applications)
Lockfile refresh lockFileMaintenance: { enabled: true }
Patch automerge packageRules[].automerge: true on matchUpdateTypes: ["patch"]
Status surface Dependency Dashboard issue + per-PR checks

Keep one configuration file per repository. Renovate reads it on every run and reconciles open pull requests against it, so the file is the single source of truth for update behaviour.

How Renovate Automates the Update Feed

Permalink to "How Renovate Automates the Update Feed"

Renovate runs as a hosted GitHub App (or a self-hosted job), scans your manifests and lockfile against the registry, and opens a pull request for every dependency that has a newer version — with the lockfile already regenerated and a changelog in the description. It runs in two modes from one config: a slow, batched cadence governed by schedule for routine upkeep, and an immediate, out-of-band pull request driven by vulnerabilityAlerts when an update closes a known advisory. This is the remediation half of a monitoring loop — the detection half (advisory feeds and CI audit gates) and the triage flow that decides what to do with each finding are covered in the parent workflow.

Renovate Update Flow Diagram showing renovate.json and the lockfile feeding the Renovate app, which emits routine version-bump pull requests on a weekly schedule and security pull requests immediately. renovate.json config lockfile resolved deps Renovate scan + resolve Routine PR weekly window Security PR at any time schedule advisory

Canonical Implementation

Permalink to "Canonical Implementation"

The following renovate.json is a complete, production-ready configuration for a deployed application. Commit it to the repository root.

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": [
    "config:recommended",
    ":dependencyDashboard",
    ":semanticCommits"
  ],
  "timezone": "UTC",
  "schedule": ["before 6am on monday"],
  "prConcurrentLimit": 5,
  "prHourlyLimit": 2,
  "rangeStrategy": "pin",
  "rebaseWhen": "conflicted",
  "labels": ["dependencies"],
  "lockFileMaintenance": {
    "enabled": true,
    "schedule": ["before 6am on monday"]
  },
  "vulnerabilityAlerts": {
    "labels": ["security", "dependencies"],
    "schedule": ["at any time"],
    "prPriority": 10
  },
  "packageRules": [
    {
      "description": "Automerge patch updates once CI is green",
      "matchUpdateTypes": ["patch", "pin", "digest"],
      "automerge": true,
      "automergeType": "pr",
      "platformAutomerge": true
    },
    {
      "description": "Group all non-major dev tooling into one PR",
      "matchDepTypes": ["devDependencies"],
      "matchUpdateTypes": ["minor", "patch"],
      "groupName": "dev dependencies"
    },
    {
      "description": "Require manual review for major bumps",
      "matchUpdateTypes": ["major"],
      "automerge": false,
      "addLabels": ["breaking"]
    }
  ]
}

The vulnerabilityAlerts.schedule of "at any time" is what makes security fixes bypass the Monday window; prPriority: 10 floats them above routine pull requests when the concurrency limit is reached. rangeStrategy: "pin" rewrites "^5.4.0" to an exact "5.4.10" so every install is reproducible. Renovate regenerates the lockfile with whichever package manager it detects in the repository, so Enforcing Package Manager Versions with Corepack is what keeps the bot’s lockfile output byte-identical to what a developer would produce locally.

Pinning changes what the manifest means. A caret range is a standing instruction to the installer — “take any 5.x release you find” — which is resolved at install time and can differ between machines and dates. After pinning, the manifest names one build, and the only way the version moves is a pull request you can read, test and revert:

Anatomy of a pinned dependency range The caret range written in package.json is split into caret, major, minor and patch segments that together match any 5.x release; rangeStrategy pin rewrites the whole range into one exact version so every install resolves identically. package.json as written ^ 5 .4 .0 matches any 5.x release caret major minor patch rangeStrategy: pin package.json after pin "vite": "5.4.10" one build every install Later releases arrive as a pull request that moves the pin, never as a silent install-time upgrade

Variant Examples

Permalink to "Variant Examples"

Dependabot equivalent (dependabot.yml)

Permalink to "Dependabot equivalent (dependabot.yml)"

If you prefer GitHub’s native bot, the closest equivalent lives at .github/dependabot.yml. Dependabot has no direct analogue to rangeStrategy: pin (it respects your manifest ranges) or lockFileMaintenance, but it covers scheduling and grouping:

version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "06:00"
      timezone: "UTC"
    open-pull-requests-limit: 5
    labels: ["dependencies"]
    groups:
      dev-dependencies:
        dependency-type: "development"
        update-types: ["minor", "patch"]

Security fixes are handled separately by Dependabot’s own security-updates feature, which is enabled at the repository level rather than in this file and always runs immediately. The two bots overlap on scheduling, grouping and security urgency, and diverge on how much control they give you over the resolved version:

Renovate and Dependabot capability matrix A four-row matrix comparing Renovate and Dependabot: Renovate can rewrite ranges to exact versions and refresh the whole lockfile, Dependabot keeps the manifest range and has no lockfile refresh, and both open security fixes immediately and group related packages into one pull request. Capability Renovate Dependabot Rewrite ranges to exact versions rangeStrategy: pin manifest range kept Refresh the entire lockfile lockFileMaintenance no equivalent Open security fixes immediately vulnerabilityAlerts repo-level setting Group related packages in one PR groupName groups: block

Grouping monorepo packages

Permalink to "Grouping monorepo packages"

In a monorepo, collapse related ecosystem updates into a single pull request so reviewers see one coherent change instead of a dozen:

{
  "packageRules": [
    {
      "description": "One PR for the whole ESLint toolchain",
      "matchPackageNames": ["eslint"],
      "matchPackagePrefixes": ["@typescript-eslint/", "eslint-config-", "eslint-plugin-"],
      "groupName": "eslint"
    }
  ]
}

Automerge only patch, hold minor and major

Permalink to "Automerge only patch, hold minor and major"

To automerge patch updates while still requiring review for anything that could change behaviour, scope automerge narrowly and disable it explicitly for the wider types:

{
  "packageRules": [
    { "matchUpdateTypes": ["patch"], "automerge": true },
    { "matchUpdateTypes": ["minor", "major"], "automerge": false, "dependencyDashboardApproval": true }
  ]
}

dependencyDashboardApproval: true holds minor and major updates in the Dependency Dashboard until you tick a checkbox, so they never open unsolicited.

Gotchas and Edge Cases

Permalink to "Gotchas and Edge Cases"
  • Automerge is only as safe as your test suite. platformAutomerge will merge a green pull request with no human in the loop. If CI does not exercise the updated code path, a broken — or maliciously republished — patch reaches the default branch unreviewed. Restrict automerge to patch/pin/digest, keep a required status check, and never automerge minor or major. The blast radius shrinks further if the update cannot run arbitrary code during install at all, which is the case made in Disabling npm Install Scripts — a postinstall hook in an automerged patch executes on your CI runner with whatever credentials that job holds.

  • rangeStrategy: pin changes the meaning of your manifest. Pinning rewrites caret and tilde ranges to exact versions, so your package.json becomes a list of frozen versions and Renovate itself becomes the only path to an upgrade. That is the intent for a deployed application, but it is the wrong default for a published library, where consumers need range flexibility — use bump or widen there instead.

  • lockFileMaintenance is noisy if left on a tight schedule. It refreshes the entire lockfile — every transitive pin — on its cadence, which can produce a large, hard-to-review diff. Keep it on the same weekly window as routine updates, not "at any time", or it will open a sprawling pull request whenever any transitive dependency ships a release. A bot-authored lockfile diff still deserves the review discipline described in Detecting Lockfile Tampering in Pull Requests: check that changed resolved URLs still point at your registry and that every integrity field moved together with its version, because a large machine-generated diff is exactly where a hand-edited line hides best.

  • Security fixes silently wait if vulnerabilityAlerts is unset. Without an explicit vulnerabilityAlerts block, advisory fixes are subject to your routine schedule and can sit unopened until the next window — up to a week of avoidable exposure. Always give it "schedule": ["at any time"]. The block also requires that Dependabot alerts are enabled on the repository, since Renovate reads that feed to know a package is vulnerable.

  • Concurrency limits can starve the security queue. With prConcurrentLimit reached by routine bumps, a new security pull request may not open until a slot frees. Setting prPriority high on vulnerabilityAlerts (as in the canonical config) ensures the security fix jumps ahead rather than queueing behind cosmetic updates.

Verification Steps

Permalink to "Verification Steps"

1. Dry-run the configuration locally

Permalink to "1. Dry-run the configuration locally"

Before relying on the hosted app, run Renovate in dry-run mode to see exactly which pull requests it would open without creating any:

LOG_LEVEL=debug npx renovate --dry-run=full --platform=github \
  --token="$RENOVATE_TOKEN" OWNER/REPO

Expected output — the log lists each detected update and the branch it would create, and confirms no writes occurred:

INFO: Dependency extraction complete {"deps": 42}
INFO: DRY-RUN: Would create PR: Update dependency vite to v5.4.10
INFO: DRY-RUN: Would automerge PR after checks
INFO: Renovate is exiting with a success code

2. Validate the config file schema

Permalink to "2. Validate the config file schema"

Catch a malformed renovate.json before it reaches a run:

npx --package renovate -c renovate-config-validator

Expected output on a valid file:

 INFO: Validating renovate.json
 INFO: Config validated successfully

3. Confirm behaviour on the Dependency Dashboard

Permalink to "3. Confirm behaviour on the Dependency Dashboard"

After the first real run, open the Dependency Dashboard issue Renovate creates. It lists open pull requests, pending scheduled updates, and any awaiting-approval items. A security fix should appear under Open immediately with the security label, while routine bumps sit under Pending with their next scheduled time. If a security fix is stuck under Pending, the vulnerabilityAlerts block is missing or Dependabot alerts are not enabled on the repository.

Frequently Asked Questions

Permalink to "Frequently Asked Questions"
Where does the renovate.json file go?

In the repository root by default. Renovate also accepts .github/renovate.json, .renovaterc.json, or a renovate key in package.json. Only one of them is read per repository and the first match in that search order wins, so a leftover file under .github can silently override the root config you are editing. Keep exactly one and delete the rest.

Should Renovate pin or widen version ranges?

For applications, use rangeStrategy pin so the manifest records an exact version and every install is reproducible. For published libraries, prefer bump or widen so consumers are not forced onto a single version and cannot end up carrying duplicated copies of the same dependency. Pinning is the safer default for anything you deploy rather than publish.

Is it safe to automerge dependency updates?

Automerge is safe only for update types your test suite fully covers, which in practice means patch-level updates behind a required, green CI check. Automerging minor or major updates without human review risks shipping a breaking change, or a maliciously republished version, straight to the default branch. Keep platformAutomerge scoped to patch, pin and digest updates.

Why is Renovate not opening any pull requests?

Check three things in order. The Dependency Dashboard issue lists every update Renovate knows about and the reason each one is held. The schedule comes next, since a weekly window means nothing opens between runs. Then prConcurrentLimit and prHourlyLimit, which stop new pull requests once the cap is reached. dependencyDashboardApproval also holds updates until you tick their checkbox.


Permalink to "Related"
Continuous Dependency Monitoring Supply Chain Auditing & Depend…