Your Dependencies Are Already Out of Date
Right now, while you’re reading this, some package in your package.json or requirements.txt has a CVE sitting in it that was patched two versions ago. Nobody filed a ticket. Nobody noticed. Your CI is green because the exploit isn’t in your test suite — it’s in your prod traffic.
Dependency updates are the unsexy, mandatory maintenance that everyone agrees is important and nobody does consistently. That’s exactly the problem these two bots solve: Renovate and Dependabot. Both open PRs to bump your deps. Both are free for most use cases. But they’re built for different audiences, and if you’re running a self-hosted git stack — Gitea, Forgejo, GitLab CE — the choice basically makes itself.
Let’s sort this out.
What They Are (Fast Version)
Dependabot started as a startup, got acquired by GitHub in 2019, and is now baked into github.com as a first-party feature. It’s the path of least resistance if you’re already on GitHub. Zero setup for basic use — just drop a .github/dependabot.yml and you’re done.
Renovate (now maintained by Mend, formerly WhiteSource) is the open-source heavy hitter. The community edition runs anywhere — GitHub, GitLab, Gitea, Forgejo, Bitbucket, Azure DevOps. It’s more configuration-heavy upfront but infinitely more flexible once you know what you’re doing.
Here’s the honest summary: Dependabot is a friendly butler. Renovate is a butler who also does your taxes, writes shell scripts, and will absolutely judge your pinned versions.
Self-Hosting: Where Dependabot Falls Apart
This is the dealbreaker for a lot of self-hosters.
Dependabot natively supports:
- github.com — fully featured, free, just works
- GitHub Enterprise Server — available, but it’s an enterprise product with an enterprise price tag
That’s it. There is no clean path to run Dependabot against a Forgejo instance, a self-hosted GitLab CE, or a Gitea server. GitHub open-sourced the dependabot-core gem, so technically you could wire it up yourself, but there’s no supported, maintained runner for non-GitHub platforms. You’d be on your own maintaining glue code. Hard pass.
Renovate’s self-hosting story is completely different. The community edition (renovatebot/renovate) is a Node.js process you run yourself, on a schedule, pointed at your git instance. It supports:
- Forgejo / Gitea (via the
giteaplatform) - GitLab CE/EE (via the
gitlabplatform) - GitHub (self-hosted or .com)
- Bitbucket Server/Cloud
- Azure DevOps
You run it as a cron job, a Docker container, or a CI pipeline. That’s it.
# One-shot run against a Forgejo instancedocker run --rm \ -e RENOVATE_TOKEN=your_forgejo_token \ -e RENOVATE_PLATFORM=gitea \ -e RENOVATE_ENDPOINT=https://git.yourdomain.com \ -e RENOVATE_REPOSITORIES='["yourorg/yourrepo"]' \ renovate/renovate:latestFor a recurring setup, drop that in a cron container or a CI job that runs every few hours. No license, no account, no cloud dependency.
Configuration Depth: Night and Day
Dependabot: Simple and Shallow
Dependabot config lives in .github/dependabot.yml. It’s readable, it’s quick, and it covers the basics well.
version: 2updates: # npm dependencies - package-ecosystem: "npm" directory: "/" schedule: interval: "weekly" day: "monday" time: "09:00" open-pull-requests-limit: 5 groups: dev-dependencies: dependency-type: "development"
# Docker images in Compose files - package-ecosystem: "docker" directory: "/" schedule: interval: "weekly" labels: - "dependencies" - "docker"
# GitHub Actions - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly"That’s a solid config. It groups dev deps, watches Docker and Actions. But here’s what you can’t do in Dependabot:
- Group packages across ecosystems (all security updates together regardless of ecosystem)
- Define custom update schedules per package
- Pin digests on Docker images automatically
- Automerge with custom merge strategies
- Ignore specific version ranges with regex
- Run custom scripts before/after PRs
You hit the ceiling fast on anything non-trivial.
Renovate: Deep Config, High Reward
Renovate’s config is a renovate.json in your repo root (or globally applied via your bot’s config). The learning curve is steeper, but you get serious power.
{ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:recommended" ], "timezone": "America/New_York", "schedule": ["before 6am on monday"], "labels": ["dependencies"], "prConcurrentLimit": 5, "prHourlyLimit": 2, "packageRules": [ { "description": "Automerge patch and minor updates for dev dependencies", "matchDepTypes": ["devDependencies"], "matchUpdateTypes": ["patch", "minor"], "automerge": true, "automergeType": "pr", "automergeStrategy": "squash" }, { "description": "Group all Docker image updates together", "matchManagers": ["dockerfile", "docker-compose"], "groupName": "Docker images", "groupSlug": "docker-images" }, { "description": "Pin Docker digests for production images", "matchManagers": ["dockerfile"], "matchPackageNames": ["node", "python", "nginx"], "pinDigests": true }, { "description": "Hold major updates for manual review", "matchUpdateTypes": ["major"], "dependencyDashboardApproval": true } ]}The dependencyDashboardApproval piece is worth calling out: Renovate creates a single “Dependency Dashboard” issue in your repo. Major version bumps sit there waiting for you to check a box before a PR is opened. You get the notification, you review the changelog, you approve when you’re ready. That’s the kind of workflow control that saves you from waking up to a broken main branch because React 19 automerged at 3 AM.
Real-World Config: Compose + npm Project
Here’s a practical renovate.json for a project that has a Docker Compose setup and a Node.js frontend — the kind of thing a lot of self-hosters are running:
{ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["config:recommended"], "timezone": "America/Chicago", "schedule": ["before 8am on saturday"], "prConcurrentLimit": 3, "labels": ["renovate", "dependencies"], "packageRules": [ { "description": "Group all Compose service image bumps", "matchManagers": ["docker-compose"], "groupName": "Compose images", "schedule": ["before 8am on saturday"] }, { "description": "Automerge npm patch updates", "matchManagers": ["npm"], "matchUpdateTypes": ["patch"], "automerge": true }, { "description": "Hold npm major updates for manual approval", "matchManagers": ["npm"], "matchUpdateTypes": ["major"], "dependencyDashboardApproval": true }, { "description": "Ignore internal/private packages", "matchPackageNames": ["@mycompany/**"], "enabled": false } ], "docker-compose": { "fileMatch": ["(^|/)docker-compose[^/]*\\.ya?ml$"] }}Compare that to what you’d write in Dependabot to get equivalent behavior — you’d need separate ecosystem entries, couldn’t group across managers, and the automerge behavior is less granular. It’s not that Dependabot is bad; it’s that Renovate was clearly designed by someone who runs a lot of repos.
Ecosystem Coverage
Both bots cover the major ecosystems. Here’s where they differ at the edges:
| Ecosystem | Renovate | Dependabot |
|---|---|---|
| npm / Yarn / pnpm | Yes | Yes |
| pip / Poetry / uv | Yes | Yes |
| Docker / Compose | Yes | Yes (Docker only) |
| Helm charts | Yes | No |
| Terraform modules | Yes | Yes |
| GitHub Actions | Yes | Yes |
| Ansible Galaxy | Yes | No |
| Custom regex | Yes | No |
| Git submodules | Yes | Yes |
| Gradle / Maven | Yes | Yes |
The Helm and Ansible Galaxy support in Renovate is genuinely useful if you’re maintaining infrastructure repos. Dependabot just doesn’t go there.
The custom regex manager in Renovate is wild. You can point it at any file, define a regex pattern that captures a version string, and Renovate will track updates for it. Got a version pinned in a Makefile? A .env file with POSTGRES_VERSION=16.2? Renovate can watch that. Dependabot cannot.
Security: Vulnerability Alerts
Dependabot has a built-in security alert system on GitHub — it watches the GitHub Advisory Database and will open PRs specifically for known CVEs, separate from regular version bumps. This is nice because it’s zero-config and fires fast when something scary drops.
Renovate doesn’t have its own vulnerability database, but it integrates with OSV (Open Source Vulnerabilities) via the osvVulnerabilityAlerts config option. Enable it and Renovate will flag vulnerable versions in its PRs.
{ "extends": ["config:recommended"], "osvVulnerabilityAlerts": true}It’s not as proactive as Dependabot’s dedicated alert PRs, but it works. For self-hosted setups where you’re not on GitHub, this is your path.
The Mend Acquisition Question
Renovate is maintained by Mend (formerly WhiteSource). The community edition is MIT-licensed and genuinely open source — you can self-host it without Mend touching your data. But Mend also sells a commercial hosted version with more features.
Is this a supply-chain trust concern? Mildly. Any tool that automatically opens PRs and modifies your dependencies is a high-value target. That’s why running Renovate self-hosted — where you control the runner, the credentials, and the network — is actually the more secure option compared to cloud-hosted bots with broad repo access.
The same logic applies to Dependabot, honestly. GitHub has full access to your repos already if you’re on github.com, so the trust model there is just “I already trust GitHub.”
Running Renovate: Scheduling and Rate Limits
One thing that catches people off guard: Renovate will, by default, open a PR for every single outdated dependency the first time it runs. On a project that hasn’t seen updates in six months, that’s a wall of PRs that will make you immediately regret setting it up.
Two config options save you from this:
{ "extends": ["config:recommended"], "prConcurrentLimit": 5, "prHourlyLimit": 2, "onboarding": true, "onboardingConfig": { "extends": ["config:recommended"] }}prConcurrentLimit caps how many open Renovate PRs can exist at once. prHourlyLimit throttles how many it opens per hour. Start conservative — you can always loosen this once you’re comfortable with the volume.
The onboarding flag is worth enabling when you add Renovate to a repo for the first time. It opens a single “Configure Renovate” PR with a proposed renovate.json before doing anything else. You review it, merge it, and then Renovate knows you’ve consented to the process. Less surprising than waking up to 30 open PRs.
The Dependency Dashboard
One of Renovate’s genuinely clever features is the Dependency Dashboard — a single issue it maintains in your repo with a full status board. You can see:
- All pending updates (open PRs, rate-limited PRs)
- Updates waiting for your approval (major versions, pinned digests)
- Errors (misconfigured registry credentials, network failures)
It’s one place to go instead of hunting through issues and PRs. Check the box next to a major update, Renovate opens the PR. Ignore it, and nothing happens until you’re ready. That’s the workflow that actually survives contact with a busy week.
The Verdict
Use Renovate if:
- You’re on Forgejo, Gitea, GitLab CE, or any non-GitHub platform
- You have multiple repos across different ecosystems
- You want fine-grained control over grouping, scheduling, and automerge
- You’re managing Helm charts, Ansible roles, or Terraform modules
- You want a dependency dashboard instead of 47 individual PRs
- You care about running your update bot without vendor lock-in
Use Dependabot if:
- You’re on GitHub.com and want zero-setup, zero-maintenance
- Your stack is straightforward (npm + GitHub Actions, nothing exotic)
- You want native GitHub security alert integration with Advisory Database
- You have one or two repos and don’t need much config
- Your team already lives in GitHub and onboarding friction matters
Honestly, if you’re reading a blog called “SumGuy’s Ramblings” and you’re self-hosting things, you’re probably already in the Renovate camp whether you knew it or not. The self-hosted path is real, the config is worth learning, and the payoff is a bot that actually fits your stack instead of the other way around.
Set it up on a Saturday, let it run, and stop being the person who notices CVEs in the postmortem.
Running Renovate in GitLab CI
If your self-hosted stack is GitLab CE, you don’t need a separate cron container — you can run Renovate as a scheduled GitLab CI pipeline. Create a dedicated repo (e.g., ops/renovate-runner) and add this pipeline:
renovate: image: renovate/renovate:latest rules: - if: '$CI_PIPELINE_SOURCE == "schedule"' variables: RENOVATE_TOKEN: "$RENOVATE_TOKEN" RENOVATE_PLATFORM: "gitlab" RENOVATE_ENDPOINT: "$CI_SERVER_URL" RENOVATE_REPOSITORIES: '["yourgroup/repo1","yourgroup/repo2"]' LOG_LEVEL: "info" script: - renovateSet the RENOVATE_TOKEN as a CI/CD variable (masked, protected), then create a GitLab scheduled pipeline for this job to run every 4–6 hours. No Docker Compose, no systemd timer, no separate server. The CI runner handles it.
This pattern works equally well on Gitea Actions or Forgejo Actions — same YAML, adjust the platform variable.
Quick Start: Self-Hosted Renovate on Forgejo
# Create a bot user in Forgejo, generate a personal access token# Then run Renovate as a scheduled Docker job
docker run --rm \ -e RENOVATE_TOKEN="fgt_your_token_here" \ -e RENOVATE_PLATFORM="gitea" \ -e RENOVATE_ENDPOINT="https://git.yourdomain.com" \ -e RENOVATE_REPOSITORIES='["yourorg/repo1","yourorg/repo2"]' \ -e LOG_LEVEL="info" \ renovate/renovate:latestDrop a renovate.json in each repo, add the bot user as a member with write access, run the container on a cron schedule, and you’re done. Your deps will stop silently rotting.
Your future self — the one triaging the incident at 2 AM — will appreciate it.