Text size
Tried & Tested6 min read

Keeping dependencies up to date without the pain

Renovate and Dependabot can update everything. The setup that keeps it calm.

Keeping dependencies up to date without the pain
AI-generated editorial illustration

Every project starts with fresh dependencies. A year later, half of them are outdated. Two years later, upgrading one library requires upgrading five others, the framework, and the language version, and suddenly a "small update" is a three-week project.

The fix isn't heroic upgrade sprints. It's small, constant, automated updates. Renovate and Dependabot make that possible, if you set them up so they help instead of flooding you with pull requests.

Why updates pile up

  • Nobody owns them. Updates are important but never urgent, until a vulnerability makes them urgent.
  • Fear of breaking things. Without good tests, every update feels risky.
  • Too many at once. Turning on a bot in an old repo produces fifty pull requests on day one, and people just ignore them all.

Dependabot vs Renovate

Dependabot is built into GitHub. Easy to switch on, handles security alerts and version updates for the most common ecosystems. Good enough for many projects.

Renovate (by Mend, open source) is more powerful and flexible:

  • Supports far more package managers and file types: Dockerfiles, Helm charts, Kubernetes manifests, Terraform, GitHub Actions, and custom regex-based matching for almost anything.
  • Very flexible grouping, scheduling and automerge rules.
  • A Dependency Dashboard issue showing everything pending.
  • Works on GitHub, GitLab, Bitbucket, Azure DevOps and more.

For infrastructure-heavy repositories, I prefer Renovate, because it can update image tags, chart versions and pinned action SHAs too.

My Renovate config

A calm starting configuration:

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "extends": ["config:recommended", ":dependencyDashboard"],
  "schedule": ["before 7am on monday"],
  "timezone": "Europe/Berlin",
  "prConcurrentLimit": 5,
  "minimumReleaseAge": "3 days",
  "packageRules": [
    {
      "matchUpdateTypes": ["patch", "minor"],
      "matchCurrentVersion": "!/^0/",
      "groupName": "non-major dependencies",
      "automerge": true
    },
    {
      "matchManagers": ["github-actions"],
      "groupName": "github actions",
      "pinDigests": true
    },
    {
      "matchDatasources": ["docker"],
      "pinDigests": true
    }
  ],
  "vulnerabilityAlerts": {
    "schedule": ["at any time"],
    "labels": ["security"]
  }
}

What this does:

  • Weekly schedule so updates arrive in one batch, not all week long.
  • A limit of five open PRs so nobody gets flooded.
  • minimumReleaseAge waits a few days after a release before proposing it. Many malicious package versions are discovered and removed within days, and so are many broken releases.
  • Groups non-major updates into one PR, and automerges them if all checks pass.
  • Pins GitHub Actions and Docker images to digests, and keeps those digests updated.
  • Security fixes skip the schedule and arrive immediately.

Small updates every week are boring. A giant upgrade every two years is a project. Choose boring.

Automerge, safely

Automerging dependency updates might sound risky, but it's what keeps the process manageable. The rules are:

  • Good tests that run on every PR. Automerge without tests is gambling.
  • Required status checks in branch protection, so nothing merges unless CI is green.
  • Only for low-risk updates: patch and minor versions of stable (1.0+) packages, dev dependencies, linters.
  • Never automerge major versions. Those get a human review.

Handling breaking changes

Major updates need attention:

  • Renovate includes release notes in the PR. Read them.
  • Update one major at a time, not five together.
  • For big frameworks, set up a separate group and schedule time for it, like any other piece of work.
  • AI coding agents are increasingly useful here: when an upgrade breaks the build because an API changed, an agent can propose the code changes in the same PR.

Images and charts too

Application dependencies are only part of the story. Infrastructure has dependencies as well:

  • Base images in Dockerfiles
  • Helm chart versions in your GitOps repos
  • Container image tags in Kubernetes manifests
  • Terraform providers and modules
  • GitHub Actions versions

Renovate handles all of these. Keeping base images current is one of the easiest ways to reduce vulnerabilities in your containers.

So, where do I start?

Turn on Renovate (or Dependabot) in one repository with a weekly schedule and a PR limit. Enable automerge for patch updates once you trust your tests. Expand to minor updates, then to infrastructure files. Within a month, staying up to date becomes background noise instead of a project.

My take: dependencies go off like milk, not wine. Let a bot keep them fresh, a little every week.

— Desly