Automatic dependency updates – a Renovate Bot introduction

Renovate bot is a tool that automatically updates software dependencies declared in your Git repository via pull requests. In this Renovate introduction, I explain the benefits of automated dependency updates, how Renovate works, how to operate/run Renovate yourself (if necessary), and how you can configure its behavior for each repository.

Originally posted on 2021-07-11, updated on 2026-08-08.

Renovate introduction

Renovate (Bot) is a CLI tool that regularly scans your Git repositories for pinned software dependencies (e.g., Node modules in package.json). It then checks the corresponding registries that host these dependencies (e.g. npmjs.com) for newer versions. If it finds any, it automatically creates a branch and a Pull Request (PR) that updates the dependency version. For GitLab, the terminology for PR would be Merge Request.

Renovate is highly configurable. For instance:

  • You can instruct Renovate to automatically merge the PRs it created once your automated tests (run by the CI pipeline for the PR) have succeeded
  • You can also customize the configuration for specific dependencies, e.g., tell Renovate to ignore updating a particular dependency, because you are using an outdated version of it on purpose, for some good reason.

Why use automated dependency updates at all?

Who needs a tool like Renovate, and why? I dare to say: every software development team! In any case, not updating your dependencies is never an option. That would cause technical debt to quickly accumulate, without you being even aware of it. Renovate provides transparency regarding how many dependencies are outdated, and eliminates the routine activity of manually checking which dependencies are out of date. Even if you don’t plan to be on the latest and greatest version for every dependency, transparency is still a good thing. It helps you plan and schedule the maintenance work for dependency updates.

Updating dependencies regularly with tools like Renovate (instead of updating them rarely) has these benefits:

  • The increment of each upgrade is small (e.g., just a minor version upgrade), which reduces the risk that the update breaks your application.
  • It is easier to plan the work, because integrating small increments is less work.
  • The chances of being affected by security vulnerabilities are smaller if you update to the latest version of a dependency shortly after it was released.
  • It pushes your team towards building better automated tests, because then you can configure Renovate to automatically merge PRs, reducing the maintenance work even further.

Supported SCM platforms and dependency types

Renovate only supports a selected set of SCMs, including GitLab, GitHub, Bitbucket, Azure DevOps, Forgejo and Gitea (including the SaaS and self-hosted alternatives). These are probably the most popular SCM platforms that offer PRs. For those SCMs, Renovate knows how to use their APIs, e.g., to authenticate, create branches, or open PRs.

Similarly, Renovate supports most of the popular dependency ecosystems (see here for a complete list), such as Gradle/Maven (Java) or Cargo (Rust). You could argue that those (popular) dependency types that are not supported (e.g. CMake for C/C++) also lack a central registry that Renovate could query to find newer versions of a dependency.

Using Renovate

How you choose to run Renovate highly depends on the SCM platform. If you use GitHub.com (SaaS), then you don’t need to configure any operations-related aspects. As documented here, the makers of Renovate (Mend) provide and maintain the Renovate GitHub App, which you can install into your repository with a single click. In other words: you don’t have to worry about things like making sure that the most recent Renovate version runs, and that it runs regularly – Mend does all that for you.

However, if you use any other SCM platform, e.g. GitLab (self-hosted or SaaS), you have to handle these operations-related aspects yourself!

The following sections go into further detail about how you can operate Renovate yourself (Operations section), and how developers can configure what Renovate does when visiting a repo (Development section), including the ability to use a different configuration for each repository.

Operations (self-hosting)

You can run Renovate in a few ways (see docs), such as:

  • Run as NPM CLI tool (installed via “npm install -g renovate“, in an environment where npm is available), e.g. triggered regularly via a cron job
  • Run as a Docker image: for instance, you install Docker on a Linux box, and have a cron job create and run a Docker container for the ready-to-use renovate/renovate image in regular intervals.
  • Run Renovate in a (regularly scheduled) CI pipeline
  • Run in Kubernetes, as CronJob or via the third-party renovate-operator – the renovate-operator is the best option if you need Renovate to visit a very large number of repositories (say, beyond 20). Here, a simple cron job (or pipeline) would take too long (i.e., several hours) to process all repositories

Irrespective of the concrete environment you choose, you need to configure Renovate so that it knows which SCM instance to connect to, or which repos to scan. The configuration can be either done as a config.js / config.json file, or via CLI arguments, or environment variables (the renovate-operator does it via a Kubernetes CRD).

The configuration options include:

  • The type of SCM you are targeting with Renovate (e.g. gitlab vs. gitea)
  • The SCM server URL (e.g. https://github.company.com/api/v3 for GitHub Enterprise, or https://gitlab.company.com/api/v4 for a self-hosted GitLab instance)
  • Authentication credentials: the typical approach is that you create a separate (Bitbucket / GitLab / GitHub / Gitea / Azure DevOps) account for Renovate, and then create some kind of personal access token for that account. You configure Renovate to use this personal access token. Thus, any actions Renovate performs (such as creating a branch or PR) will be done with that user account. For GitHub Enterprise, using a GitHub app as user account is recommended. See here for details.
  • A GitHub.com personal access token: when your chosen SCM platform is not GitHub.com (thus, you are self-hosting), Renovate still needs a GitHub.com PAT, to avoid rate limits of the github.com API (which Renovate queries a lot to find new releases or load change logs). I recommend you create a new GitHub account for this, and create a personal access token (the “classic” variant!) as described here. You don’t need to check any checkboxes in the scopes/permissions dialog when creating the PAT.
  • The Git identity Renovate should use for commits it creates (e.g. “Renovate Bot <bot@your-org.com>“)
  • Which repositories Renovate should scan: you can either configure the autodiscover mode, where Renovate runs on every repository the account can access, or you explicitly declare a list of repositories.
  • Various other options, as explained in the Self Hosted Configuration documentation page.
  • Repository-specific options (further elaborated on in the Development section below) that you want to configure globally for every repository Renovate visits. See all those configuration options on the Repository configuration options documentation page that indicate that they are mergeable. A good example is the hostRules key, where you can configure credentials for private registries (e.g., a private Artifactory server). Renovate automatically merges the repo-specific settings from the config.js file with the repo-specific settings of the repository itself (renovate.json[c]).

Once Renovate is running (e.g. triggered once per hour via cron or a scheduled CI/CD pipeline), the development teams need to configure it to visit their repositories. This either happens when a developer invites Renovate’s SCM account into their private repository (if Renovate is configured in autodiscover mode), or the developer asks Renovate’s operations team to add the repo’s name explicitly in the config.js file (if Renovate is configured with a list of repositories).

Tip: If you are interested in running your own Renovate instance in GitLab, take a look at the official GitLab runner repository.

Development

Whenever Renovate scans your repository, it uses a configuration file such as renovate.json, located at the root of your repository. It tells Renovate how it should behave specifically for this repository. The reference for what you can put into this file is found on the Repository configuration options documentation page. Don’t confuse this renovate.json file with the config.js file that you (may have) used to configure Renovate’s operational aspects (in case you are self-hosting Renovate, which I explained above)!

To avoid that Renovate wreaks havoc the first time it visits your repo (due to an incorrect configuration), Renovate’s default behavior is to start an onboarding phase (as described in the docs). Renovate runs the following logic:

  • If there is a renovate.json file (or one of its alternatives) in the root of the repository’s default branch (typically master or main): Renovate assumes onboarding has completed, and will scan your repository for outdated dependencies (unless you set enabled to false, in which case Renovate will leave your repository immediately)
  • Else:
    • Is there already an onboarding PR (typically named “Configure Renovate”) in the repo?
      • If yes:
        • If the PR is still open, or already closed (but not merged): don’t further scan the repository – wait for the user to merge the onboarding PR first
        • If it is merged: don’t further scan the repo – there used to be a renovate.json file in the past (created when merging the onboarding PR) into the default branch. But since then, the developers deleted the file from the default branch on purpose, indicating that they no longer want Renovate to scan the repository
      • If not: Renovate creates a new onboarding PR titled “Configure Renovate” (and a corresponding Git branch, typically named “renovate/configure“) that contains a commit with a new renovate.json

The description text of the onboarding PR explains many details, such as the detected dependency types (such as pip, docker, etc.), a summary of Renovate’s configuration in understandable plain English (rather than printing the configuration JSON object), and which PRs Renovate would create on its next visit, assuming that you merged the onboarding PR by then.

The initial content of the renovate.json file will be a very basic configuration. Typically, the developer tweaks its content prior to merging the onboarding PR, by checking out the “renovate/configure” branch and updating the file’s content with a new commit. Note that the developer may want to use the renovate-config-validator CLI tool (see docs) to validate the renovate.json file before committing it. The next time Renovate visits the repository (which may take a little while), it detects that you changed the renovate.json file in the (still-open) onboarding PR, and updates the PR’s description accordingly.

Of course, you can continue updating the renovate.json file even after the onboarding PR was merged. Renovate always uses the most recent renovate.json file located at the root of the repository’s default branch. You can change this behavior via the configuration options useBaseBranchConfig and baseBranchPatterns.

Understanding the configuration file

A developer who wants to tune the Renovate configuration for their repo for the first time will be overwhelmed by Renovate’s configuration flexibility. On my first attempt, I had questions like these:

  • There are dozens of Configuration Options – which ones should I change, and what are their default values?
  • How do packageRules (reference docs) work, which tweak Renovate’s behavior for specific packages?

Understanding default values

In a typical onboarding PR, the configuration file looks like this:

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:best-practices"]
}
Code language: JSON / JSON with Comments (json)

This file looks rather empty, so your first instinct might be to just look at the many options on the Configuration Options page and inspect their default values. For instance, you might think that the Dependency Dashboard feature is disabled (see here) or that there are no packageRules configured by default.

However, this is incorrect because of Renovate’s presets feature. The above example makes use of this feature by inheriting from the config:best-practices preset, as indicated by the highlighted line. As explained here, a preset essentially bundles one or more configuration options, setting specific values for them. Presets are often hierarchical to increase their level of reuse. You can reference presets that you host yourself, via a notation like “gitlab>abc/foo:xyz.json” (docs). However, you are most likely going to use the built-in presets shipped with Renovate, referencing them by a string of the form
<preset-category>:<preset-name>“. Each preset category has its own page in the Renovate documentation that explains its concrete presets:

  • config:<preset-name>” presets are explained on the Full config page
  • :<preset-name>” (which is the same as "default:<preset-name>") are explained on the Default presets page
  • All other presets (“<something>:<preset-name>“) have a corresponding preset page named like “something” (e.g. “docker:<preset-name>” presets are on the Docker presets page)

So, in the above renovate.json example file, the config:best-practices preset is expanded to many other presets (such as ":configMigration"). Next, these more concrete presets are translated to actual configuration options, as found on the Configuration Options page.

If you want to dive deeper into the topic, it makes sense to study these presets referenced by config:best-practices more closely, to understand which of these default settings may not be suitable for your project. But if you don’t have time for that, just leave the defaults, then tweak the rules over time whenever you discover that Renovate’s behavior is not suitable.

One important issue with the default config:best-practices preset is that Renovate creates PRs immediately after it discovers an outdated dependency. If that dependency is updated often (e.g. multiple times per day), this also results in many frequent PR updates, with a lot of (email) notification spam and merging work. The Noise reduction documentation proposes possible solutions to this problem.

Understanding packageRules to tweak your config

The packageRules key in the renovate.json file lets you fine-tune/override Renovate’s behavior for specific dependencies. Selecting these dependencies is done via the matchXXXX keys, e.g. matchFileNames, matchPackageNames or matchUpdateTypes. packageRules is an array of (JSON) objects. The allowed keys in each object can be one or more of the sub-keys explained in the packagesRules documentation (like matchPackageNames), but can also be (most of the) other keys documented on the Configuration Options page, like automerge.

You should know that all rule objects in the array are valid at the same time! The matching logic does not work as you know it from many other systems, where rules #3 and #4 are no longer evaluated if rule #2 was a match. Instead, the “better/closer” matching rules take precedence. For instance, if rule #1 sets automerge=true for matchPackageNames=["foo"] and rule #2 sets automerge=false for matchPackageNames=["foo"] and matchUpdateTypes=["major"], then automerge would be true only for all dependencies with an update type of minor or patch. However, the order of rule objects in the packageRules array does also matter: rules declared later (towards the end of the array) overwrite values of an equally-well-matching rule declared earlier.

Let’s look at a concrete example:

{
...  // other stuff here, e.g. "extends": ["config:best-practices"],
"packageRules": [
    {
      // Whenever Renovate finds updates for any package (except those starting with
      // @material-ui, @types, react, or typescript), it will create a single(!) branch+PR for
      // all the detected changes, instead of creating multiple branches/PRs (one per package)
      "matchPackageNames": [
        "!@material-ui/**",
        "!@types/**",
        "!react*",
        "!typescript*"
      ],
      "groupName": "deps" // note: "deps" is just a self-chosen name without any meaning, but it appears in the PR title
    },
    {
      // Renovate will create a single branch/PR for any changes detected for NPM/YARN
      // dependencies of type "dependencies" and "devDependencies" which start with @material-ui
      "matchPackageNames": [ "@material-ui**" ],
      "groupName": "Material-UI",
      "matchDepTypes": [
        "dependencies",
        "devDependencies"
      ]
    },
    ...
  ],
...
}Code language: JSON / JSON with Comments (json)

I provide a list of recommended configuration options in the cheat sheet article.

Understanding update types

When the author of a software dependency offers an update, there are different types of updates. Renovate distinguishes between the following types, which you should understand well:

  • major: when the dependency version changed from e.g. 1.0.3 to 2.0.0
  • minor: when the dependency version changed from e.g. 1.0.3 to 1.1.0
  • patch: when the dependency version changed from e.g. 1.0.3 to 1.0.4
  • digest: when the dependency’s version did not change, but the checksum/digest has. At the time of writing, Renovate seems to detect this update type for Docker images and GitHub Actions. As an example, consider a specific Python Docker image you use in your software, e.g. python:3.9.2 – this particular image tag still changes regularly over time, because the Python release team has a CI pipeline that rebuilds the python:3.9.2 image regularly (e.g. to keep up with the latest security patches in the base OS image). The digest will be different for each built image.
  • rollback: applies when Renovate detected that the concrete version of the dependency referenced in your Git repo is no longer available in the corresponding artifact registry, e.g. because the dependency was pulled by the author because it was incorrectly built, buggy, or malicious. By default, Renovate will leave such dependencies untouched, but you can enable the rollbackPrs feature to change this behavior.

These update types are used in different places in the renovate.json file, e.g. as a value in the matchUpdateTypes element (within packageRules), or as a key on the root level of the configuration JSON object (e.g. major)

Conclusion

Renovate is the perfect tool to automate the maintenance of software dependencies. It offers more configuration flexibility compared to other solutions such as GitHub’s Dependabot. However, this is also a downside! You should expect increased workload and “annoyed” developers at the beginning (during the onboarding phase), because it takes time to configure Renovate correctly. The configuration is a bit tricky, due to the huge flexibility.

I recommend that whoever introduces Renovate reads the docs carefully and builds a cheat sheet for the rest of your company (your developers), which includes the most common configuration options. Assume that your developers don’t want to spend a huge amount of time learning Renovate’s configuration syntax. Take a look at the cheat sheet I created in this article!

I also highly recommend using coding agents (like GitHub Copilot, Claude Code, etc.) to assist you with tuning Renovate. Use it together with Context7 (so that the agent has access to the most recent Renovate documentation) and my renovate-log-parser (so that the agent can efficient parse Renovate debug logs to diagnose problems).

Once you have become somewhat familiar with Renovate, I also recommend my advanced tips and tricks article.

By the way: if you are operating Renovate yourself, I recommend you change the configuration file name created as part of the onboarding from renovate.json to renovate.jsonc. This lets your developers add explanatory comments to their JSON file. You can make this change in Renovate’s configuration (see onboardingConfigFileName).

Leave a Comment