Posts

Dependabot Configuration That Does Not Hate You Back

Dependabot is one of those tools that can either quietly keep your dependencies moving or bury your team under a pile of tiny pull requests. The difference is configuration. Out of the box, Dependabot tends to think in …

May 18, 2026 13 min read 2648 words

In this article

Dependabot is one of those tools that can either quietly keep your dependencies moving or bury your team under a pile of tiny pull requests.

The difference is configuration.

Out of the box, Dependabot tends to think in very small units:

  • one dependency
  • one update
  • one pull request
  • repeat until morale improves

That is fine for a small repo with three dependencies and the emotional complexity of a toaster. It is less fine for real projects with application packages, GitHub Actions, Docker images, Terraform modules, dev tooling, lockfiles, and a CI bill that starts to look like it has hobbies.

So here is the point:

Dependabot should not be treated as a firehose. It should be configured as a dependency review system with batching, delay, and explicit ecosystem coverage.

That means three things:

  1. Group low-risk updates so humans review fewer, better pull requests.
  2. Cooldown routine version updates so brand-new releases do not hit your repo five minutes after publishing.
  3. Add ecosystems intentionally so Dependabot watches more than just your application package manager.

This is not about making Dependabot fancy. It is about making it usable enough that people do not learn to ignore it.

And if your dependency automation teaches people to ignore dependency automation, congratulations, you have built a very small compliance-themed Roomba.

The Problem With Default Dependabot

Default Dependabot is helpful, but it is not opinionated enough about human attention.

By default, it can open separate pull requests for separate dependency updates. That sounds precise until you watch a project get a stack of tiny updates:

dependabot/npm/lodash-4.17.21
dependabot/npm/eslint-9.12.0
dependabot/npm/@types/node-22.8.1
dependabot/npm/jest-29.7.0
dependabot/github-actions/actions-checkout-4
dependabot/docker/node-22

Each one wants CI. Each one wants review. Each one wants someone to decide whether the change is safe.

The hidden cost is not the YAML. The hidden cost is attention.

When the bot gets noisy, teams adapt in predictable ways:

  • they rubber-stamp updates
  • they batch-merge without reading
  • they leave the PRs open forever
  • they turn off version updates and only react to security alerts
  • they quietly develop a “Dependabot blindness” reflex

That last one is the real failure mode.

Dependency automation is not just about freshness. It is a trust workflow. You are letting a machine propose changes to code you did not write, from maintainers you may not know, published through registries you do not control.

That is useful.

That is also a supply-chain boundary.

Treat it like one.

Start With The Baseline

Dependabot lives in .github/dependabot.yml.

The required shape is simple:

  • version: 2 tells GitHub which config syntax you are using.
  • updates is the list of ecosystems Dependabot should monitor.
  • package-ecosystem names the package manager or dependency source.
  • directory points to the manifest location.
  • directories points to multiple manifest locations and supports globbing.
  • schedule controls how often Dependabot checks for version updates.

A basic config might look like this:

version: 2

updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"

  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"

  - package-ecosystem: "docker"
    directory: "/"
    schedule:
      interval: "weekly"

That is enough to get updates for:

  • npm, pnpm, or yarn manifests through package-ecosystem: "npm"
  • GitHub Actions workflows through package-ecosystem: "github-actions"
  • Dockerfiles through package-ecosystem: "docker"

NOTE: GitHub Actions uses directory: "/". Dependabot looks in .github/workflows from there, plus root action metadata files like action.yml or action.yaml.

This baseline works, but it still has the PR confetti problem.

So we group.

Group The Boring Stuff

Dependabot groups let you combine related dependency updates into fewer pull requests.

That is not just a convenience feature. It changes the review shape.

Instead of reviewing twelve tiny dev-tool patches, you can review one “dev tooling patch/minor updates” PR. You still need CI. You still need a brain. You just do not need twelve browser tabs and the patience of a saint.

Here is a practical npm grouping setup:

version: 2

updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
    groups:
      eslint:
        applies-to: "version-updates"
        patterns:
          - "eslint*"
          - "@eslint*"
        update-types:
          - "minor"
          - "patch"

      test-tooling:
        applies-to: "version-updates"
        patterns:
          - "jest*"
          - "@types/*"
          - "vitest*"
          - "playwright*"
        update-types:
          - "minor"
          - "patch"

      development-dependencies:
        applies-to: "version-updates"
        dependency-type: "development"
        update-types:
          - "minor"
          - "patch"

      production-dependencies:
        applies-to: "version-updates"
        dependency-type: "production"
        update-types:
          - "patch"

The important detail: group order matters.

If a dependency matches more than one group, Dependabot uses the first matching group. That means specific families should come before broad buckets.

This:

groups:
  eslint:
    patterns:
      - "eslint*"

  development-dependencies:
    dependency-type: "development"

is different from this:

groups:
  development-dependencies:
    dependency-type: "development"

  eslint:
    patterns:
      - "eslint*"

In the second version, eslint may get swallowed by the broader dev dependency group first.

That is not a disaster, but it is how configs slowly stop saying what you think they say. YAML is already a haunted indentation pamphlet. Do not make it do implied policy too.

Keep Major Updates Separate

Patch and minor updates are usually good candidates for grouping.

Major updates are different.

A major update is not always dangerous, because SemVer is a promise, not a physical law. But it is a signal that the package author is allowed to break your assumptions.

So my default rule is:

Group patch and minor updates. Treat major updates as planned work.

You can do that two ways.

First, only include patch and minor updates in your groups:

groups:
  minor-and-patch:
    applies-to: "version-updates"
    patterns:
      - "*"
    update-types:
      - "minor"
      - "patch"

Then let major updates appear as individual PRs.

Or, for projects where major updates need explicit planning, ignore major version updates and create scheduled upgrade work separately:

ignore:
  - dependency-name: "*"
    update-types:
      - "version-update:semver-major"

That is not permission to forget them forever.

If you ignore major updates, put them somewhere visible:

  • a quarterly dependency upgrade ticket
  • a platform backlog item
  • a recurring maintenance sprint task
  • a dashboard your team actually looks at

An ignored major update should be a conscious deferral, not a dependency witness protection program.

Use Cooldown As A Supply-Chain Speed Bump

Dependabot cooldown defines a minimum package age before Dependabot opens a version update PR.

Read that sentence again, because this feature is more important than it looks.

A cooldown is not just “please bother me later.”

It is a small supply-chain risk control.

When a new dependency version is published, the riskiest time to consume it is often immediately after release. That is when:

  • broken releases are discovered
  • compromised maintainer accounts get noticed
  • malicious packages get reported
  • bad transitive changes start showing up in other people’s CI
  • changelog omissions become very loud GitHub issues

Waiting a few days lets the broader ecosystem do what the broader ecosystem does best: step on the rake first.

That sounds cynical. It is also how many teams already operate manually. Cooldown just turns the instinct into configuration.

For SemVer-friendly ecosystems, I like this shape:

cooldown:
  default-days: 7
  semver-major-days: 45
  semver-minor-days: 7
  semver-patch-days: 3

My usual starting point:

  • Patch updates: 3 days
  • Minor updates: 7 days
  • Major updates: 30 to 60 days
  • Non-SemVer ecosystems: use default-days

GitHub’s docs show semver-major-days, semver-minor-days, and semver-patch-days, but those bump-specific settings only apply where the package manager supports SemVer-style version classification. For ecosystems that do not support that distinction, use default-days.

Also important:

NOTE: cooldown applies to version updates, not security updates.

That is what you want.

Routine freshness can wait a few days. A known vulnerability may need a faster path.

Add More Than Your App Package Manager

A lot of teams add Dependabot for npm or pip and stop there.

That is a start, but it is not the whole supply chain.

Your repo may also depend on:

  • CI actions
  • Docker base images
  • Terraform providers
  • OpenTofu providers
  • pre-commit hooks
  • devcontainer images
  • language toolchains
  • build plugins
  • package managers in subdirectories

Each ecosystem needs its own updates entry.

Here are common package-ecosystem values worth knowing:

EcosystemDependabot value
npm, pnpm, yarnnpm
pip, pip-compile, pipenv, poetrypip
uvuv
Go modulesgomod
Mavenmaven
Gradlegradle
NuGetnuget
Cargocargo
Composercomposer
Bundlerbundler
Dockerdocker
Docker Composedocker-compose
GitHub Actionsgithub-actions
Terraformterraform
OpenTofuopentofu
Dev containersdevcontainers
pre-commitpre-commit
Bunbun
Helmhelm
Nix flakesnix
Rust toolchainrust-toolchain
Swiftswift

The special cases matter:

  • npm covers npm, pnpm, and yarn.
  • pip covers pip, pip-compile, pipenv, and poetry.
  • GitHub Actions should use directory: "/".
  • directories supports multiple paths and globs; directory does not.

For a monorepo, this is often cleaner:

updates:
  - package-ecosystem: "npm"
    directories:
      - "/apps/*"
      - "/packages/*"
    schedule:
      interval: "weekly"
    groups:
      npm-minor-and-patch:
        patterns:
          - "*"
        update-types:
          - "minor"
          - "patch"
        group-by: "dependency-name"

The mental model is simple:

repo
+-- .github/workflows      -> github-actions
+-- package.json           -> npm
+-- apps/web/package.json  -> npm
+-- Dockerfile             -> docker
+-- compose.yaml           -> docker-compose
+-- infra/*.tf             -> terraform
+-- .pre-commit-config.yaml -> pre-commit

If the dependency source matters to your builds, releases, or runtime, it probably deserves a Dependabot entry.

Multi-Ecosystem Groups For Infrastructure

Per-ecosystem groups are good when you want npm updates with npm updates, pip updates with pip updates, and so on.

But infrastructure updates often cross ecosystem lines.

For example:

  • Docker base image updates
  • Terraform provider updates
  • OpenTofu provider updates
  • GitHub Actions updates

Those may all belong to the same platform review motion.

That is where multi-ecosystem-groups helps. It lets you define a top-level group, then assign multiple ecosystem entries to it.

version: 2

multi-ecosystem-groups:
  infrastructure:
    schedule:
      interval: "weekly"
      day: "tuesday"
      time: "09:00"
      timezone: "America/Chicago"
    labels:
      - "dependencies"
      - "infrastructure"

updates:
  - package-ecosystem: "docker"
    directory: "/"
    patterns:
      - "*"
    multi-ecosystem-group: "infrastructure"

  - package-ecosystem: "terraform"
    directory: "/infra"
    patterns:
      - "*"
    multi-ecosystem-group: "infrastructure"

  - package-ecosystem: "github-actions"
    directory: "/"
    patterns:
      - "*"
    multi-ecosystem-group: "infrastructure"

The practical benefit is not just fewer PRs.

It is that the right humans see the right kind of change.

Application dependency updates and infrastructure dependency updates have different blast radiuses. They deserve different review paths.

A Practical Full Config

Here is a fuller example I would actually start from for a TypeScript service with GitHub Actions, Docker, and Terraform.

Adjust names, labels, directories, assignees, and CODEOWNERS for your repo. Please do not cargo-cult my Tuesday. Tuesday has done enough.

version: 2

multi-ecosystem-groups:
  infrastructure:
    schedule:
      interval: "weekly"
      day: "tuesday"
      time: "09:00"
      timezone: "America/Chicago"
    labels:
      - "dependencies"
      - "infrastructure"
    commit-message:
      prefix: "deps(infra)"
      include: "scope"

updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"
      day: "monday"
      time: "09:00"
      timezone: "America/Chicago"
    open-pull-requests-limit: 3
    labels:
      - "dependencies"
      - "javascript"
    assignees:
      - "octocat"
    commit-message:
      prefix: "deps"
      prefix-development: "deps-dev"
      include: "scope"
    cooldown:
      default-days: 7
      semver-major-days: 45
      semver-minor-days: 7
      semver-patch-days: 3
    groups:
      eslint:
        applies-to: "version-updates"
        patterns:
          - "eslint*"
          - "@eslint*"
          - "typescript-eslint"
          - "@typescript-eslint/*"
        update-types:
          - "minor"
          - "patch"

      test-tooling:
        applies-to: "version-updates"
        patterns:
          - "jest*"
          - "vitest*"
          - "playwright*"
          - "@types/*"
        update-types:
          - "minor"
          - "patch"

      npm-development:
        applies-to: "version-updates"
        dependency-type: "development"
        update-types:
          - "minor"
          - "patch"

      npm-production-patches:
        applies-to: "version-updates"
        dependency-type: "production"
        update-types:
          - "patch"
    ignore:
      - dependency-name: "*"
        update-types:
          - "version-update:semver-major"

  - package-ecosystem: "github-actions"
    directory: "/"
    patterns:
      - "*"
    cooldown:
      default-days: 7
    multi-ecosystem-group: "infrastructure"

  - package-ecosystem: "docker"
    directory: "/"
    patterns:
      - "*"
    cooldown:
      default-days: 7
    multi-ecosystem-group: "infrastructure"

  - package-ecosystem: "terraform"
    directory: "/infra"
    patterns:
      - "*"
    cooldown:
      default-days: 14
    multi-ecosystem-group: "infrastructure"

A few notes on that config:

  • npm patch and minor updates are grouped.
  • npm major updates are ignored so they can become planned work.
  • cooldown gives new releases a little time before routine PRs appear.
  • infrastructure updates are reviewed together.
  • GitHub Actions, Docker, and Terraform use default-days cooldown because not every ecosystem gets useful SemVer bump-specific handling.
  • open-pull-requests-limit lowers the default open version-update PR limit so the bot does not turn CI into a slot machine.

You may prefer to let major updates open as individual PRs instead of ignoring them. That is valid. The important thing is that major updates should not quietly sneak into a broad “everything is fine” group.

Tips The Docs Do Not Emphasize Enough

The official docs tell you what the knobs do. They do not always tell you how teams tend to hurt themselves with the knobs.

So here are the field rules I would actually use.

Do Not Auto-Merge Major Updates By Default

Auto-merge is tempting for patch updates with excellent tests.

For major updates, I would be much more conservative.

Major updates often need:

  • migration notes
  • changelog review
  • release note review
  • runtime smoke testing
  • manual QA
  • rollout planning

If your test suite is excellent, maybe you can automate more. Most test suites are not excellent. Most test suites are a polite collection of hopes with some assertions attached.

Keep Security Updates Fast And Visible

Routine version updates and security updates are not the same workflow.

Cooldown applies to version updates, not security updates, and that distinction is good. A random minor version can wait. A known exploited vulnerability may not have that luxury.

Use labels, assignees, CODEOWNERS review rules, and branch protection so security updates get routed correctly.

GitHub used to support a reviewers key in dependabot.yml, but that option was retired in favor of CODEOWNERS. If you see older examples using reviewers, treat them as stale unless your specific GitHub Enterprise Server version still documents that behavior.

Do not bury security updates inside a sea of routine package freshness chores.

Review The Lockfile

The manifest tells you what you asked for.

The lockfile tells you what you got.

That distinction matters.

A Dependabot PR that changes one direct dependency can still move transitive dependencies underneath it. If you only look at package.json, pyproject.toml, or pom.xml, you can miss the actual shape of the change.

The lockfile is where the dependency graph leaves fingerprints.

Pin GitHub Actions When Your Risk Model Requires It

GitHub Actions are code from other repositories running inside your CI environment.

That is supply-chain territory.

Many teams pin third-party actions to commit SHAs to reduce tag-move risk, then use dependency automation to keep those pins moving. That is more operational work than using floating tags like v4, but it can be a reasonable tradeoff for sensitive repos.

The key is not “always pin everything because a conference talk said so.”

The key is knowing which workflows can touch secrets, publish artifacts, deploy infrastructure, or write back to the repo.

Those workflows deserve more paranoia.

Use ignore Like A Scalpel

ignore is useful.

It is also where updates go to disappear if nobody owns the follow-up.

Good uses of ignore:

  • suppressing major updates that are handled by planned upgrade work
  • temporarily skipping a broken upstream release
  • avoiding a known bad version range

Bad uses of ignore:

  • hiding noisy updates because the grouping config is weak
  • suppressing things nobody wants to understand
  • turning Dependabot into decorative YAML

If the bot is noisy, fix the workflow first.

Put Ownership In The Config

Labels, assignees, CODEOWNERS, and commit message prefixes look boring.

That is because they are boring.

Boring is good here.

Dependency PRs should land in a queue that someone owns. If every Dependabot PR goes to “the team,” it goes to nobody, and nobody is apparently very busy.

The Practical Policy

If I were setting up Dependabot on a serious repo today, my policy would be:

  1. Enable every ecosystem that can affect builds, CI, releases, or runtime.
  2. Group patch and minor updates by review shape.
  3. Keep major updates separate or convert them into planned upgrade work.
  4. Add cooldown for routine version updates.
  5. Keep security updates visible and urgent.
  6. Review lockfile changes.
  7. Limit open PRs so automation does not become background noise.

That gives you the actual benefit of Dependabot: regular, reviewable dependency movement without training the team to ignore the robot.

Because the goal is not “more dependency PRs.”

The goal is a codebase that stays current, stays reviewable, and does not invite every fresh package release directly into production like it brought snacks.

Configure the bot. Make the trust boundary explicit. Let automation help without handing it the keys to your attention span.

-Rob

References