Skip to content

Dependabot: Alerts, Security Updates and Version Updates

Lesson 3 of 8Intermediate15 min readGit Security & DevSecOps · Code & Dependency SecurityVerified: Dependabot options reference and supported ecosystems, September 2026

“Dependabot” names three distinct systems that people routinely conflate, and the confusion produces both of the classic failures: an organisation that turned on alerts and thought it had automated updates, and one that turned on version updates and drowned.

Alerts tell you a dependency you already have is known to be vulnerable. Security updates open pull requests fixing those alerts. Version updates keep dependencies current whether or not anything is wrong.

Different triggers, different volumes, different failure modes.

TriggerOutputVolume
AlertsA new advisory, or a new dependencyAn alert in the security tabLow, spiky
Security updatesAn alert with an available fixA pull requestLow
Version updatesYour configured schedulePull requestsHigh, and entirely yours to control

Alerts and security updates are enabled through repository settings and need no configuration file. Version updates require dependabot.yml, and it is version updates that generate the volume people associate with Dependabot.

An alert is the join between your dependency graph and the GitHub Advisory Database. It fires when a new advisory matches something you already depend on, or when you add a dependency that already has one.

Two properties that shape how you handle them:

They are retrospective by nature. An advisory published today concerns code you have been running for months. The alert is new; the exposure is not.

Severity is a property of the vulnerability, not of you. A critical advisory in a package whose vulnerable function you never call may be genuinely irrelevant. A medium one on your authentication path may be the most urgent thing you have.

Converting the first into the second is triage, and it is not automatable — Vulnerability alerts is about doing it well.

When an alert has a fix available, Dependabot opens a pull request upgrading to a fixed version.

This is the highest-value automation in the feature set, because it closes the gap between “we know” and “we fixed” without anyone doing work. Two things determine whether it actually helps:

Your CI has to be good enough to trust. A dependency bump merged on a green build is only safe if the build tests something. This is the real prerequisite, and it is a testing investment rather than a security one.

The fix has to be reachable. A patch released only in a major version means the pull request is a breaking change, and it will sit. Those are the ones needing human attention, and they are worth identifying explicitly rather than letting them age in the queue with everything else.

Configured in .github/dependabot.yml, and the source of essentially all Dependabot volume.

.github/dependabot.yml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
groups:
production-dependencies:
dependency-type: "production"
update-types: ["minor", "patch"]
development-dependencies:
dependency-type: "development"
ignore:
- dependency-name: "typescript"
update-types: ["version-update:semver-major"]
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

Every element of that file is doing noise control, which is the whole discipline.

The second block is the one most repositories omit and the one with the clearest security value.

Actions referenced by a commit SHA — as pinning actions recommends — are pinned forever, including to a version with a known vulnerability. Dependabot’s github-actions ecosystem updates those pins, and it updates the version comment alongside the SHA.

That resolves the standing objection to SHA pinning: pinning is safe but goes stale, and this is what un-stales it.

KeyEffect
open-pull-requests-limitHard cap on concurrent pull requests for this ecosystem
groupsCombine multiple updates into one pull request
schedule.intervaldaily, weekly or monthly
ignoreExclude specific dependencies or update types
versioning-strategyHow the manifest is edited — widen a range, or pin
cooldownWait before adopting a newly published release (version updates only)
target-branchOpen against a branch other than the default

groups is the most impactful. Ungrouped, a repository with forty dependencies produces a stream of individual pull requests. Grouped by dependency type and update type, the same week produces two.

Group by what you would review together. Patch updates to development dependencies are one decision; a major version of your web framework is another. A single group containing both is a pull request nobody can evaluate.

cooldown is worth understanding as a supply-chain control. Adopting a release the hour it publishes means you are the person who discovers a compromised package. A few days’ delay means somebody else does. The cost is a few days’ delay on non-security updates, which is almost always acceptable — and it does not apply to security updates, which is correct.

The identifier is the literal package-ecosystem value. Verified against GitHub’s documentation, September 2026:

bazel · bun · bundler · cargo · composer · conda · deno · devcontainers · docker · docker-compose · dotnet-sdk · elm · github-actions · gitsubmodule · gomod · gradle · helm · julia · maven · mix · nix · npm · nuget · opentofu · pip · pre-commit · pub

Two mappings that surprise people: pnpm and Yarn both use npm, and Poetry, pipenv and pip-compile all use pip. The identifier names the ecosystem, not the tool.

Three entries are worth enabling beyond the obvious ones. docker updates base images in Dockerfiles, which is where a great many unpatched CVEs live. devcontainers and docker-compose cover the development environment, which is code executing on developer machines. And pre-commit updates your hook versions, which are third-party code running on every commit.

Multi-directory and monorepo configuration

Section titled “Multi-directory and monorepo configuration”

A repository with several manifests needs one update block per location, or one block covering several with directories:

version: 2
updates:
- package-ecosystem: "npm"
directories:
- "/services/api"
- "/services/web"
- "/packages/*"
schedule:
interval: "weekly"
groups:
all-patch:
update-types: ["patch"]

directories accepts glob patterns, which is what makes a monorepo with twenty packages configurable without twenty blocks. Note that grouping applies per update block, so a group in a directories block can combine updates across all of them — usually what you want, and occasionally not, if the services release independently.

multi-ecosystem-groups goes one step further, combining updates across different package managers into a single pull request. In a repository where a version bump has to happen in both a package.json and a Dockerfile to be coherent, that is the difference between one reviewable change and two that are individually broken.

The trade-off with any grouping is bisection. One pull request updating twelve dependencies that breaks the build takes longer to diagnose than twelve that break individually. Group by what you would review together, and keep the risky updates — majors, build tooling, anything cryptographic — ungrouped so a failure names itself.

Alert volume is manageable through auto-triage rules, which automatically dismiss or snooze alerts matching criteria you define. GitHub provides a default rule and organisations can define custom rules.

The legitimate use is a class of alert that genuinely does not apply — development-only dependencies where the vulnerability requires an attacker-facing deployment, for instance.

The illegitimate use is any rule broad enough that you no longer know what it is suppressing.

Merging dependency updates automatically is a real option and it is a decision about your test suite, not about your risk appetite.

Auto-merge is defensible when: the update is a patch or minor version, CI meaningfully tests the change, and the dependency is not one where a compromise would be catastrophic.

Auto-merge is not defensible when: CI is a linter, the update is a major version, or the dependency is something like a build tool or a cryptography library where a subtle behaviour change matters.

The uncomfortable framing: auto-merging dependency updates is auto-deploying code you have not read, written by people you do not know, on the strength of your test suite. That is a completely reasonable trade when the test suite is good. It is a supply-chain attack’s ideal target when it is not.

A moderate position that works well: auto-merge grouped patch updates to development dependencies; require review for everything else.

A pull request opened by Dependabot triggers workflows, and it runs under different rules from an ordinary pull request. This catches nearly everyone once.

GITHUB_TOKEN is read-only for workflows triggered by Dependabot pull requests, regardless of the repository’s default. Repository secrets are not available; Dependabot has its own secret store, and workflows on Dependabot pull requests read from that.

This is a deliberate security boundary, and it is the right one. A Dependabot pull request contains third-party code changes — an updated dependency is new code executing in your build. Giving that a writable token and your production secrets would make dependency updates an execution path into your credentials.

The consequence is that a workflow needing write access on a Dependabot pull request — to add a label, post a comment, or enable auto-merge — will fail, and the failure looks like a permissions bug rather than a policy.

The safe pattern is to do the privileged work in a separate workflow_run job that does not check out the pull request’s code:

name: dependabot automation
on:
workflow_run:
workflows: ["CI"]
types: [completed]
permissions:
contents: write
pull-requests: write
jobs:
automerge:
if: >-
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success' &&
github.event.workflow_run.actor.login == 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- run: gh pr merge --auto --squash "${PR_URL}"
env:
PR_URL: ${{ github.event.workflow_run.pull_requests[0].html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

The important property: this job never checks out or executes the updated dependency. It reads a result and acts on it. See Workflow security for the general form of this pattern and why the naive version is dangerous.

For private registries, credentials are declared once and referenced per ecosystem:

version: 2
registries:
npm-internal:
type: npm-registry
url: https://npm.internal.example.com
token: ${{ secrets.INTERNAL_NPM_TOKEN }}
updates:
- package-ecosystem: "npm"
directory: "/"
registries:
- npm-internal
schedule:
interval: "weekly"

Two security points. The token is a Dependabot secret, stored separately from Actions secrets — they are different stores and configuring one does not configure the other. And it should be read-only: Dependabot needs to resolve packages, not publish them.

Threat. A dependency in your tree contains a vulnerability an attacker can reach, or a malicious version is published and you adopt it.

Attack surface. Every package in the transitive tree, which for a typical application is hundreds to thousands of packages from hundreds of maintainers. Plus the resolution process, which decides which artefact a name and version range actually fetch.

Impact. Determined by whether the vulnerable code path is reachable from your usage, which is a question about your code rather than about the advisory.

Control. Dependabot addresses two parts of this: knowing when something you have becomes known-bad (alerts), and fixing it quickly (security updates). Version updates address a third, less obvious one — a codebase that is broadly current can take an urgent patch, and one that is two years behind cannot without a migration project.

Verification. Add a known-vulnerable dependency to a scratch repository and confirm an alert appears and a pull request follows. Then check your real repositories: how many have alerts enabled, and what is the median age of an open security update pull request? The second number is the one that describes your actual posture.

Note what Dependabot does not address: a malicious package published deliberately, a typosquatted name, or a compromised maintainer account. Those produce no advisory until somebody notices, and by then you have already installed it. That is dependency security’s subject.

Version updates get treated as maintenance rather than security, and that framing understates them.

The argument is about response capacity. When a critical advisory lands in a framework you depend on, the fix is usually published for the current major version. A codebase one minor version behind takes it in an afternoon. A codebase four major versions behind cannot take it at all without a migration, and the realistic outcome is running a known-vulnerable version for months.

Staying current is therefore not about having the newest packages. It is about being able to move when you have to, and that capacity is built continuously or not at all.

The corollary is a useful metric: not “how many outdated dependencies do we have?” but “how long would it take us to adopt a patch in our most critical dependency?” A team that cannot answer that has an unknown response time, which during an incident becomes a discovered one.

Confusing the three systems. Alerts enabled is not updates enabled, and version updates are not security updates.

No groups configuration. The default is one pull request per dependency, which is how a repository accumulates forty open updates.

Auto-merging without adequate CI. Automated adoption of unreviewed third-party code on the strength of a test suite that does not test.

Omitting the github-actions ecosystem. SHA-pinned actions never update, and stale pins are the predictable cost of pinning.

Broad auto-triage rules. A permanent, invisible reduction in what you are told about.

Ignoring a dependency to silence it. ignore is for deliberate decisions, not for making an alert go away. The vulnerability is unaffected.

Expecting cooldown to protect security updates. It applies to version updates only, by design.

Treating an open pull request as remediation. The vulnerability is present until it merges and deploys.

The pull requests are the product, and reviewing them well is a distinct skill from reviewing code.

Read the release notes, which Dependabot includes. They are in the pull request body, and they are the only description of what changed. A patch release with a note saying “fixed a bug in default argument handling” is a behaviour change in your application.

Check the version jump. 1.2.3 to 1.2.4 and 1.2.3 to 2.0.0 are different reviews. Semantic versioning is a promise, not a guarantee, but it is the signal available.

Look at what else moved. A lock file update accompanying a single direct dependency bump often changes several transitive ones. Those are dependencies you did not choose, arriving in a pull request about something else.

Trust CI proportionally. If your test suite covers the code paths this dependency serves, a green build is meaningful. If it does not, the green build says the code still compiles.

Look at the dependency itself for anything unusual. A package that suddenly gains new maintainers, adds an install script, or grows substantially in size is worth a moment. This is the manual version of what dependency review automates, and the automated version is better.

The single most useful habit: do not batch-approve. A queue of twenty updates approved in one sitting is twenty unreviewed changes, and it is exactly the workflow a supply-chain attack relies on. If the queue is too large to review, the answer is grouping and cooldowns, not faster clicking.

A repository that has never had Dependabot enabled produces an initial burst: every outdated dependency at once, plus every existing advisory.

  1. Enable alerts first, and nothing else. Read what comes back. That list describes the risk you already have.

  2. Triage the alerts — live and reachable, versus theoretical. See Vulnerability alerts.

  3. Enable security updates. These are low-volume and high-value, and they act on the list from step 1.

  4. Improve CI before enabling version updates, if it needs it. Version updates without meaningful tests produce pull requests nobody can safely merge.

  5. Add dependabot.yml with aggressive grouping and a low pull request limit. Start at open-pull-requests-limit: 3 and a weekly schedule.

  6. Work through the backlog over a few weeks, then raise the limit.

  7. Add the github-actions and docker ecosystems, which are usually the most neglected and often the most valuable.

  8. Consider auto-merge for grouped patch updates once CI is genuinely trustworthy.

Steps 1 to 3 are the security work. Steps 4 onward are the maintenance work that makes the security work sustainable, and doing them in the other order is what produces the abandoned queue.

Dependabot is three systems sharing a name. Alerts are a notification service. Security updates are a remediation service. Version updates are a maintenance service. The first two are about risk you already have; the third is about not accumulating more.

  • Alerts, security updates and version updates have different triggers, volumes and failure modes
  • Alerts join your dependency graph against the advisory database, and are retrospective by nature
  • Security updates only help if CI meaningfully tests the change
  • cooldown delays adopting new releases and applies to version updates only
  • groups is the single most effective noise control available
  • The github-actions ecosystem is what keeps SHA-pinned actions from going stale
  • pnpm and Yarn both use npm; Poetry and pipenv both use pip
  • Auto-triage rules are standing decisions and need a review date
  • Auto-merge is a statement about your test suite, not about your risk appetite
  • Dependabot secrets are a separate store from Actions secrets

Use a disposable repository with a small dependency manifest.

  1. Enable alerts and security updates. Add a dependency with a known advisory — an old version of a widely-used package. Predict: how long until an alert appears?

  2. Predict: does a pull request appear automatically? Which setting controls that?

  3. Add a dependabot.yml with version updates and no groups, with several outdated dependencies. Trigger a check. Predict: how many pull requests?

  4. Add groups combining patch and minor updates. Trigger again. Predict: how many now?

  5. Add the github-actions ecosystem, with an action pinned to an old SHA. Predict: does the update change the SHA, the version comment, or both?

  6. Set open-pull-requests-limit: 1 and observe the behaviour when more updates are available.

  7. Add an ignore entry for a major version bump and confirm it is respected.

  8. Delete the repository.

GitHub Actions Security ChecklistToken permissions, fork pull requests, script injection and supply chain — with the attack each item prevents.

The repository security templates — secrets management and least-privilege token guides — are in the Professional Toolkit.