Dependency Update Policy
One line: take security fixes promptly; don’t chase non-security version bumps, and when you do take them, let releases age first. This is enforced with Dependabot only — one tool, per-repo config, no Renovate.
The Policy
| Update type | Behavior | Your job |
|---|---|---|
| Security (CVE/GHSA) | PR created immediately — cooldown never delays security updates | Merge promptly: critical/high same-day; moderate/low may bake 2–3 days |
| Version (routine) | PR created only after a release-age cooldown (npm: 30/14/7 days for major/minor/patch; other ecosystems: 14 days). Minor+patch grouped into one weekly PR | Merge during normal maintenance; no urgency |
Why the cooldown: a brand-new release is the highest-risk artifact in the supply chain — compromised-maintainer payloads and botched releases are usually detected (and yanked or .1-patched) within days. Letting releases bake avoids adopting them in that window. Security PRs are exempt because a known vulnerability outweighs a speculative bad release; the compensating control is that a human merges the PR (and can glance at the dependency diff — a “security fix” touching install scripts or adding network calls is a red flag).
Why not Renovate: the org runs on progressivesurface.ghe.com (GitHub Enterprise Cloud with data residency), where Mend’s hosted Renovate app is unavailable — Renovate would mean self-hosting a bot. Dependabot’s native cooldown option (GA July 2025) covers the release-age requirement with zero infrastructure. The Renovate rollout referenced in older configs/docs was dropped 2026-07-27.
Configuration
Rolled out to all active repos with dependency manifests on 2026-07-27. Dependabot version-update config is per-repo by design — there is no org-wide dependabot.yml (only private-registry config and security-update enablement can be centralized). New repos must add .github/dependabot.yml:
# Dependabot config — security updates (immediate) + version updates (cooldown)
#
# Security PRs (CVE/GHSA) are NOT subject to cooldown and flow immediately —
# merge them promptly. Version updates wait out a release-age cooldown so new
# releases bake before adoption (supply-chain / regression guard).
# Verify: Repo Settings → Code security → Dependabot security updates = ENABLED.
# Policy: PSI Wiki → development/dependency-update-policy
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
cooldown:
default-days: 14
semver-major-days: 30
semver-minor-days: 14
semver-patch-days: 7
groups:
npm-minor-patch:
applies-to: version-updates
update-types:
- "minor"
- "patch"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
cooldown:
default-days: 14
groups:
actions-all:
applies-to: version-updates
patterns:
- "*"Ecosystem notes
- npm is the only ecosystem with semver-granular cooldown (
semver-major/minor/patch-days); for pip, nuget, docker, gradle, github-actions usecooldown: default-days: 14only. - Multi-project .NET repos: use
directories: ["/**"]on the nuget block. - Monorepos/workspaces: list each manifest directory (globs allowed, e.g.
"/packages/*"). - Lockfiles are the real exposure control — CI and deploys must use
npm ci(or equivalent) so nothing enters production except through a reviewed PR.
Gotchas
Learned the hard way in psi-portal (2026-08-10); all of it applies to every repo on this config:
- A comment in
.npmrccan kill the updater outright. Dependabot’s npm-config reader does not honour#.psi-portal’s.npmrcillustrated the auth entry it deliberately omits using an ellipsis character as a stand-in for the registry host; Dependabot read that comment as a registry entry and every run died ondependency_file_not_resolvable {message: "URI must be ascii only ..."}. Total failure, not per-package — no security update could be prepared at all, for six weeks, while 38 alerts accumulated. Keep.npmrcASCII and never write a host-and-token line in it, even as illustration. - A broken updater is nearly silent. The only symptom is a failed run under Actions → the
Dependabottab, which nobody watches. Alert counts keep climbing normally, so the security tab looks like a triage backlog rather than a broken pipeline. Worth a glance whenever a repo’s Dependabot PRs go quiet for a few weeks. ignorehas a safe form and an unsafe one.update-types: ["version-update:semver-major"]is documented to affect version updates only — security PRs for a major still arrive. Ignoring byversions:range suppresses security updates in that range too, which is how a CVE ends up producing no PR at all. Preferupdate-types; treat aversions:ignore as a decision to accept the risk in that range.open-pull-requests-limitdoes not cap security PRs, only version-update PRs. A repo can sit well past its limit legitimately.
Intentional exclusions
- PSI.All, PSI.BC.ALL — legacy vendored .NET Framework Trunk layouts; nuget version-bump PRs are unwanted noise (PSI.All still gets github-actions updates). Security alerts still surface via the dependency graph.
- Repos with no dependency manifests and no workflows carry no config.
History
- Pre-2026-07: old template (limit 5, no cooldown) documented in deploy-to-azure; enacted configs had drifted to security-only (
open-pull-requests-limit: 0) pending a Renovate rollout that never shipped. - 2026-07-27: Renovate plan dropped; cooldown-enabled Dependabot config rolled out org-wide (~45 repos).