The dependency dread
You just shipped a Rust project. It compiles. The tests pass. You move on to the next thing. Three months later, a security advisory hits your feed: a critical vulnerability in a crate you depend on. Or you try to update your dependencies and cargo update pulls in a major version bump that breaks half your code. You stare at the terminal. Updating dependencies feels like defusing a bomb. You want the safety of new versions without the panic.
That's where Dependabot and Renovate come in. They turn dependency management from a scary manual chore into a steady stream of small, reviewable pull requests. You get fresh dependencies, security fixes, and feature updates without touching the command line. The bots handle the version bumps and lock file resolution. You handle the review.
How dependency bots work
Dependencies are external crates your code relies on. Your Cargo.toml lists what you need. Your Cargo.lock freezes the exact versions so your build is reproducible. Over time, those crates get updates. Patches fix bugs. Minor versions add features. Major versions might change APIs. You need to stay updated to get security fixes and improvements, but updating manually is tedious.
Dependabot and Renovate are bots that watch your repository. When a new version of a dependency drops, the bot creates a pull request. It updates the version in Cargo.toml, runs cargo update to refresh Cargo.lock, and pushes the changes. You get a PR. You review it. You merge it. The bot handles the grunt work.
Think of it like a smart grocery shopper. Your Cargo.toml is the recipe. Your Cargo.lock is the specific batch of ingredients you bought. The store gets new batches all the time. The bot checks the store weekly. It buys the new ingredients, brings them to you in a basket, and says, "I swapped the old flour for new flour, and the cake still tastes good." You check the basket. If it looks right, you bake. If not, you send it back.
Minimal setup
The fastest way to start is Dependabot on GitHub. You add a single configuration file to your repository. The bot reads it and starts scanning.
# .github/dependabot.yml
# Tells GitHub to watch Cargo.toml files in the root directory
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/"
# Check for updates once a week
schedule:
interval: "weekly"
# Limit open PRs to avoid backlog
open-pull-requests-limit: 5
This configuration tells Dependabot to look at Cargo.toml files in the root directory. It checks crates.io once a week. If a new version fits your version constraints, it opens a pull request. The limit of 5 prevents the bot from flooding your repo with PRs if you fall behind.
What happens behind the scenes
When the schedule triggers, the bot queries crates.io for your dependencies. It compares the available versions against your Cargo.toml constraints. If a new version fits your constraints, the bot creates a branch. It updates the version string in Cargo.toml. It runs cargo update to generate a new Cargo.lock. It commits the changes and opens a pull request.
The bot does more than just bump numbers. It resolves the dependency tree. If you depend on serde, and serde depends on serde_derive, the bot updates the whole tree. It captures transitive dependency updates in Cargo.lock even if you didn't list them in Cargo.toml. This keeps your lock file consistent with the latest compatible versions of everything.
The PR description lists what changed. It shows the version bump, the changelog entries, and the diff for Cargo.toml and Cargo.lock. If you have CI configured, the bot waits for your checks to pass before marking the PR as ready. You review the diff. If the build passes, you merge. The bot then closes the PR and moves on.
Realistic configuration
Real projects have dozens of dependencies. If every patch update gets its own PR, you drown in notifications. The community standard is to group patch updates. A patch update is almost always safe. Grouping them into one PR per week keeps the signal high and the noise low.
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "cargo"
directory: "/"
schedule:
interval: "weekly"
day: "friday"
time: "06:00"
timezone: "Europe/Berlin"
# Group patch updates to reduce noise
groups:
rust-patches:
patterns: ["*"]
update-types: ["patch"]
# Limit PRs to avoid overwhelming the queue
open-pull-requests-limit: 2
# Auto-merge small updates if CI passes
automerge: true
automerge_strategy: "squash"
This configuration groups all patch updates into a single PR named "rust-patches". It runs every Friday morning. It limits open PRs to 2. If the CI checks pass, it auto-merges the PR using a squash merge. This keeps your history clean and your dependencies fresh without manual intervention.
Convention aside: the community calls this the "patch grouping" rule. Everyone groups patches. Minor updates get their own PRs so you can review new features. Major updates get their own PRs so you can handle breaking changes. Patches just get bundled. It reduces PR fatigue and keeps the review queue focused on meaningful changes.
Renovate offers more granular control. It supports MSRV (Minimum Supported Rust Version) policies directly in the configuration. You can tell Renovate to only update dependencies if they support your MSRV.
// .github/renovate.json5
{
extends: ["config:recommended"],
packageRules: [
{
matchManagers: ["cargo"],
// Group all Rust dependencies
groupName: "all rust dependencies",
groupSlug: "rust-deps",
// Only group patch updates
matchUpdateTypes: ["patch"],
// Respect MSRV constraints
constraints: {
rust: ">=1.70",
},
},
],
}
Renovate reads the rust constraint and checks the dependency's MSRV before opening a PR. If a dependency bumps its MSRV above 1.70, Renovate skips the update. This prevents your project from breaking on older toolchains.
Pitfalls and compiler errors
The bot updates versions. It doesn't understand your code. If a dependency releases a minor version that breaks an internal API, your build might fail. The bot will open the PR, your CI will run, and the checks will fail. You'll see errors like E0432 (unresolved import) if a function moved, or E0277 (trait bound not satisfied) if a type changed. The bot doesn't fix these. You have to review the PR, see the failing checks, and decide whether to update your code or revert the dependency.
Another pitfall is MSRV bumps. If a dependency bumps its MSRV, your project might stop compiling on older toolchains. Renovate handles this with constraints. Dependabot has MSRV support too, but it's less flexible. Always check the changelog of dependencies before merging, even if the bot says it's a patch. Patch updates can fix bugs that change behavior. Review the changelog to make sure the fix doesn't break your assumptions.
Security updates are different from version updates. Dependabot creates separate PRs for security vulnerabilities. These PRs prioritize the fix over version constraints. They might bump a dependency to a version that violates your constraints to patch the hole. Review these carefully. They often require code changes to adapt to the fix.
Don't let dependencies rot. Automate the boring stuff. Trust the bot to update, but trust yourself to review. A green CI check on a dependency PR is your safety net. Merge fast when it's green.
When to use which tool
Use Dependabot when you are on GitHub and want zero-configuration setup. Dependabot is built into GitHub. You add one file and it works. It integrates with GitHub Actions and security advisories out of the box.
Use Renovate when you need advanced configuration or use GitLab/Bitbucket. Renovate supports more platforms and offers finer control over update strategies, scheduling, and labeling. It handles MSRV constraints more flexibly and can manage non-Rust dependencies alongside Rust ones in a unified workflow.
Use manual updates when your project has complex dependency interactions or you are learning. If your build depends on specific git commits or local paths, bots struggle. If you are new to Rust, updating dependencies by hand teaches you how Cargo.lock works and how to read changelogs.
Use grouping for patch updates to reduce PR noise. Group patches into a single weekly PR. Keep minor and major updates separate so you can review breaking changes individually.