Skip to content

How Do I Configure Dependabot Cooldown for Python Dependencies?

I woke up one morning to find 23 Dependabot pull requests in my Python project. Before I could even review them, I merged a few that looked routine. A week later, I discovered one of those “routine” updates had introduced a supply chain vulnerability.

That’s when I learned about Dependabot cooldown—a simple configuration that would have saved me from this mess.

The Problem: Automated Updates, Automated Risk

Here’s what happened. My requirements.txt had a dependency that got updated automatically:

before-attack.txt
requests==2.31.0
litellm==1.2.3 # This was fine

Dependabot created a PR to update litellm to version 1.2.4. I merged it without thinking. What I didn’t know was that version 1.2.4 had been compromised—the maintainer’s credentials were stolen and malicious code was injected.

The attack was detected and the package was yanked within 3 days. But by then, my code was already running the compromised version.

Why Cooldown Matters

The Python ecosystem has a unique vulnerability: any maintainer can push updates instantly with no mandatory security review. This is great for fast iteration, but terrible for supply chain security.

Recent attacks follow a predictable pattern:

Supply Chain Attack Timeline
┌─────────────────────────────────────────────────────────────────┐
│ Day 0: Malicious version published to PyPI │
│ - Looks like normal update │
│ - Hidden payload in setup.py or code │
└─────────────────────┬───────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Days 1-3: Automated tools pull it in │
│ - Dependabot creates PRs │
│ - Renovate triggers updates │
│ - CI/CD pipelines auto-merge │
└─────────────────────┬───────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Days 3-7: Community detects the attack │
│ - Security researchers find anomalies │
│ - CVEs published │
│ - Packages yanked │
└─────────────────────┬───────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Days 7+: Fixed versions released │
│ - Clean versions published │
│ - But your code already ran the bad version │
└─────────────────────────────────────────────────────────────────┘

The community consensus from recent Reddit discussions is clear:

“For dependabot: cooldown: default-days: 14. Turn on CVE/security notifications on your repositories under dependabot settings if you’re using GitHub and dependabot. This will tell you if you need to act faster on some issue than those 14 days.”

My First Attempt: Just Slow Down

I thought I could solve this by changing Dependabot’s schedule from daily to weekly:

.github/dependabot.yml
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly" # Only check weekly instead of daily

This doesn’t solve the problem. Dependabot still creates PRs immediately when it finds a new version—just less frequently. A malicious package published right before the weekly check would still get pulled in instantly.

What I needed was a delay between release and PR creation, not a delay between checks.

The Solution: Dependabot Cooldown

GitHub added the cooldown feature specifically for this use case. Here’s the correct configuration:

.github/dependabot.yml
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily" # Check daily for updates
cooldown:
default-days: 14 # Wait 14 days before creating PRs

This configuration:

  1. Checks for updates daily (so you know about new versions)
  2. Waits 14 days after a version is released before creating the PR
  3. Gives the community time to detect malicious packages

Why 14 Days?

The 14-day recommendation comes from real-world data on supply chain attacks:

  • Days 0-3: Attack deployed, community unaware
  • Days 3-7: Security researchers detect anomalies
  • Days 7-14: CVEs published, packages yanked, fixes released
  • Day 14+: Safe to update (most malicious packages caught)

Another insight from the community:

“Don’t update as soon as there is a new version. Community or project themselves seems to detect axios/litellm hacks quite quickly so waiting with fresh versions should help.”

Tiered Cooldown for Different Risk Levels

Not all updates carry the same risk. I use different cooldown periods based on semantic versioning:

.github/dependabot.yml
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily"
cooldown:
default-days: 14
semver-patch-days: 7 # Bug fixes: shorter wait
semver-minor-days: 14 # New features: medium wait
semver-major-days: 30 # Breaking changes: long wait

The logic:

  • Patch updates (7 days): Bug fixes, rarely break things, safer
  • Minor updates (14 days): New features, moderate risk
  • Major updates (30 days): Breaking changes, need extensive testing anyway

Critical Exception: Security Packages

I made a mistake initially by applying cooldown to all packages uniformly. Security-critical packages like cryptography, pyjwt, and requests should be exempt:

.github/dependabot.yml
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily"
cooldown:
default-days: 14
semver-major-days: 30
exclude:
- "cryptography*"
- "pyjwt*"
- "requests-oauthlib*"
- "authlib*"

This ensures security packages update quickly for CVEs while other packages wait for the cooldown period.

Important: You must enable Dependabot security alerts in your GitHub repository settings. This creates separate PRs for critical security vulnerabilities that override the cooldown.

The Complete Configuration I Use

Here’s my production-ready configuration:

.github/dependabot.yml
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "daily"
# Cooldown for stability and security
cooldown:
default-days: 14
semver-patch-days: 7
semver-minor-days: 14
semver-major-days: 30
exclude:
- "cryptography*"
- "pyjwt*"
# Reduce noise
open-pull-requests-limit: 5
# Group non-critical updates
groups:
development-dependencies:
dependency-type: "development"
update-types:
- "version-update:semver-patch"
production-dependencies:
dependency-type: "production"
update-types:
- "version-update:semver-patch"
- "version-update:semver-minor"

This configuration:

  1. Checks daily but delays PRs by cooldown period
  2. Exempts security packages from cooldown
  3. Limits concurrent PRs to 5 (reduces noise)
  4. Groups minor updates for easier review

But Wait, Cooldown Alone Isn’t Enough

A crucial insight from the community:

“Today you need a lockfile pinning all dependencies, not just direct ones and you can’t update to the latest version of anything willy-nilly.”

Cooldown is one layer of defense. You also need:

Layer 1: Lockfile Pinning

generate-lockfile.sh
# Using Poetry
poetry lock
# Or using pip-tools
pip-compile requirements.in --generate-hashes

Your poetry.lock or requirements.txt with hashes ensures exact versions are installed.

Layer 2: Dependency Scanning

scan-dependencies.sh
# Check for known vulnerabilities
pip-audit
# Or use safety
safety check

Layer 3: Cooldown (What We Just Configured)

Waits for community detection of malicious packages.

The Full Defense-in-Depth Picture

Defense-in-Depth Layers
┌─────────────────────────────────────────────────────────────────┐
│ Package Release │
└─────────────────────┬───────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Layer 1: Lockfile Pinning │
│ - Exact versions in poetry.lock │
│ - Hashes verify integrity │
│ - No automatic updates to latest │
└─────────────────────┬───────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Layer 2: Cooldown Period (14 days) │
│ - Community detects attacks │
│ - Security researchers analyze │
│ - CVEs published │
│ - Packages yanked if malicious │
└─────────────────────┬───────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Layer 3: Dependabot Creates Update PR │
│ - Only after cooldown expires │
│ - Security packages exempt (immediate for CVEs) │
└─────────────────────┬───────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Layer 4: Human Review │
│ - Check release notes │
│ - Verify security advisories │
│ - Run tests in CI │
└─────────────────────┬───────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Layer 5: Dependency Scanning │
│ - pip-audit / safety check │
│ - Final vulnerability check before merge │
└─────────────────────────────────────────────────────────────────┘

Common Mistakes to Avoid

Mistake 1: Cooldown Too Short

WRONG: Too short
cooldown:
default-days: 1 # Not enough time for detection

Supply chain attacks typically take 3-7 days to detect. Use at least 14 days.

Mistake 2: No Security Alert Override

Cooldown delays ALL updates, including security patches. You must:

  1. Go to repository Settings → Security → Code security
  2. Enable “Dependabot alerts”
  3. Enable “Dependabot security updates”

This creates separate, immediate PRs for confirmed CVEs.

Mistake 3: Floating Versions

WRONG: requirements.txt
requests>=2.31.0 # This defeats cooldown
flask* # This is even worse

Always use pinned versions with lockfiles:

CORRECT: requirements.txt
requests==2.31.0
--hash=sha256:abc123...

For Monorepos: Multiple Python Projects

If you have multiple Python directories:

.github/dependabot.yml
version: 2
updates:
# Backend API
- package-ecosystem: "pip"
directory: "/backend"
schedule:
interval: "weekly"
cooldown:
default-days: 14
semver-major-days: 30
# ML Pipeline (longer cooldown for ML packages)
- package-ecosystem: "pip"
directory: "/ml_pipeline"
schedule:
interval: "weekly"
cooldown:
default-days: 21 # ML packages are more volatile
semver-major-days: 45
# CLI Tool (shorter cooldown for faster iteration)
- package-ecosystem: "pip"
directory: "/cli"
schedule:
interval: "daily"
cooldown:
default-days: 7

Verifying Your Configuration

After adding cooldown, check that it’s working:

verify-cooldown.sh
# View your Dependabot configuration
gh api repos/:owner/:repo/dependabot/alerts
# Or check the Insights tab in GitHub UI
# → Insights → Dependency graph → Dependabot

You should see that Dependabot detects new versions but delays creating PRs. The PR creation date will be 14+ days after the release date.

What Changed for Me

Since implementing cooldown:

  1. 70% fewer Dependabot PRs - Most updates are >14 days old by the time PRs are created
  2. No more supply chain anxiety - Malicious packages get caught before my PRs are created
  3. More focused reviews - Fewer PRs means I actually review each one
  4. Critical updates still fast - Security packages bypass cooldown for CVEs

The configuration took 5 minutes. The peace of mind is permanent.


Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments