Dependency Update Jobs on GitHub Actions
Dependabot and Renovate pull requests trigger the same workflows a human one does. Scope the update check, group the updates, and price the weekly batch.
Last verified:
A dependency bot opens pull requests on a schedule, and each one fires the same pull_request workflows a human pull request fires, so a weekly batch of ten update pull requests bills the full matrix ten times before anyone reads a diff. Two changes contain that: scope the workflow so a bot branch runs one update-check job rather than the whole matrix, and decide deliberately where the bot's own update job runs, because GitHub applies its self-hosted rules to managed runners.
This guide covers the documented Dependabot behavior on managed runners and the setup it needs, the workflow that keeps bot pull requests off the matrix without breaking required status checks, and a priced model of a weekly update batch.
Diagnosis
Two workloads travel under the word Dependabot, and they bill differently.
The update job resolves the new version, edits the manifest, and opens the pull request. It runs on GitHub's own infrastructure by default. Moving it onto your runners is an explicit opt-in: an owner turns on the Dependabot on self-hosted runners setting and provides a runner carrying the dependabot label, and GitHub does not run Dependabot updates on public repositories when self-hosted runners are in use (managing Dependabot on self-hosted runners, checked on 2026-08-13).
The workflow run triggered by the resulting pull request is where your minutes go, and it is indistinguishable from a human pull request unless you make it distinguishable. Three details set the volume:
- The default ceiling is five open pull requests per package ecosystem, raised or lowered with
open-pull-requests-limit, andgroupscollapses several updates into one pull request (Dependabot options reference, checked on 2026-08-13). Three ecosystems at the default means up to fifteen open branches, each rebuilding on every rebase. - A run triggered by Dependabot gets a read-only
GITHUB_TOKENand reads Dependabot secrets rather than Actions secrets (automating Dependabot with GitHub Actions, checked on 2026-08-13). A matrix leg that pulls a private registry credential from Actions secrets fails on bot branches only, and the failure looks like a flaky job until someone reads the log. - Renovate runs either as a GitHub App or as a scheduled workflow job in your own repository. In the second shape the update job is an ordinary workflow job that takes whatever
runs-onlabel you give it, so it is priced like any other job on your account.
The compounding case is a failing group. One bad transitive dependency turns into repeated re-runs across every open bot branch, which is the pattern described in containing re-run storms on GitHub Actions.
Fix
Four moves, ordered by how much they remove.
Cut the number of pull requests. A weekly schedule with groups turns a dozen individual bumps into one branch per ecosystem. This is the largest lever because it divides the run count directly.
Cut the jobs per pull request. A dependency bump needs proof that the tree still installs, compiles, and passes unit tests. It rarely needs the full platform matrix on the first pass. The honest split is to run one Linux update-check job on the bot branch and keep the four-platform matrix for human pull requests and for the merge commit on the default branch.
Keep required status checks reporting. A job skipped by an if condition reports no conclusion, and a required check that never reports leaves the pull request unmergeable (troubleshooting required status checks, checked on 2026-08-13). Add one gate job with if: always() that reads the needs results and make that the required check.
Place the update job on purpose. Leaving Dependabot's own update job on GitHub-hosted infrastructure is the correct default for most teams and the only option on public repositories. When a private repository needs the update job to reach an internal registry or a private network, the common issues documentation covers the Allow Dependabot option on a custom runner, which assigns Dependabot workflows to WarpBuild custom runners.
Configuration
Group first, in .github/dependabot.yml:
version: 2
updates:
- package-ecosystem: npm
directory: /
schedule:
interval: weekly
day: monday
open-pull-requests-limit: 3
groups:
npm-minor-and-patch:
update-types: [minor, patch]Major updates stay outside the group, so each arrives on its own branch with its own run. Then scope the workflow. The update-check job is the only thing a bot branch runs:
name: ci
on:
pull_request:
push:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
update-check:
if: github.actor == 'dependabot[bot]' || github.actor == 'renovate[bot]'
runs-on: warp-ubuntu-latest-x64-4x
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
- run: npm ci
- run: npm run build
- run: npm test -- --runInBand
matrix:
if: github.actor != 'dependabot[bot]' && github.actor != 'renovate[bot]'
strategy:
fail-fast: false
matrix:
runner:
- warp-ubuntu-latest-x64-4x
- warp-ubuntu-latest-arm64-4x
- warp-macos-latest-arm64-6x
- warp-windows-latest-x64-4x
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v4
with:
node-version-file: .nvmrc
- run: npm ci
- run: npm test
gate:
if: always()
needs: [update-check, matrix]
runs-on: warp-ubuntu-latest-x64-2x
steps:
- name: Fail if any required job failed
run: |
if [ "${{ contains(needs.*.result, 'failure') }}" = "true" ]; then
exit 1
fiSet gate as the required status check and leave update-check and matrix unrequired. gate reports on every run, so a bot branch merges on the update check alone and a human branch has to clear all four platforms. Label shapes and sizes come from the cloud runners documentation.
Cost or Time Model
Assume forty bot pull requests per month across three ecosystems, one run each, a matrix that expands to six legs (the four platforms above plus two extra Node versions on Linux) averaging seven minutes, and a scoped update check of nine minutes. Rates come from the pricing page.
| Configuration | Bot pull requests | Jobs each | Billed minutes | Cost at $0.008 per minute |
|---|---|---|---|---|
| Full matrix on every bot branch | 40 | 6 | 1,680 | $13.44 |
| Scoped update check | 40 | 1 | 360 | $2.88 |
| Scoped and grouped weekly | 12 | 1 | 108 | $0.86 |
The merge path is unchanged: each of the twelve grouped updates still runs the full matrix once on the default branch, adding 504 minutes and $4.03. Total for the scoped and grouped shape is 612 minutes at $4.90 against 2,184 minutes at $17.47 for the naive shape, a removal of 1,572 billed minutes per month on one repository. Multiply by the repository count before deciding this is small.
The same minutes on GitHub-hosted list prices, from the minute multipliers reference: warp-ubuntu-latest-x64-4x (4 vCPU, 16 GB) costs $0.008 per minute against $0.012 per minute for the 4-core Linux larger runner (4 vCPU, 16 GB): 33 percent lower list price. GitHub list price checked on 2026-08-13. The naive shape prices out at $26.21 and the scoped and grouped shape at $7.34 at that rate.
| Shape | Billed minutes | At $0.008 | At $0.012 |
|---|---|---|---|
| Full matrix on every bot branch, plus merges | 2,184 | $17.47 | $26.21 |
| Scoped and grouped, plus merges | 612 | $4.90 | $7.34 |
Every line above is minutes multiplied by a rate, which is the whole invoice.
The same arithmetic applies to the other recurring bot workloads on a repository: the scheduled scans in security scanning jobs on GitHub Actions and the rebuild cadence in the base image update workflow. Per pull request accounting for the human side is in what one pull request costs, and the account level view is in cutting GitHub Actions costs without rewrites.
FAQ
Can Dependabot update jobs run on WarpBuild runners?
On private repositories, yes, with a setting on each side. GitHub runs Dependabot updates on GitHub-hosted infrastructure unless an owner turns on the Dependabot on self-hosted runners setting and offers a runner carrying the dependabot label, and a managed runner registers through the same self-hosted path, so the rule applies to it. On the WarpBuild side the custom runner carries an Allow Dependabot option, documented in the common issues page, which assigns Dependabot workflows to WarpBuild custom runners. GitHub excludes public repositories from this path for security reasons.
How do I stop a bot pull request from running the whole matrix?
Gate the matrix job on the actor with a condition such as github.actor != 'dependabot[bot]', and give the bot branch a single update-check job instead. Then add a gate job that runs with always() and inspects the needs results, and make that gate job the required status check, because a job skipped by an if condition never reports a conclusion and a required check that never reports blocks the merge.
Does grouping updates hide a breaking dependency?
It can, so group by update type rather than grouping everything. Patch and minor updates batch well because a failing group is usually one obvious culprit in a short list. Keep major version updates out of the groups so each one arrives as its own pull request with its own run, which is the case where a bisect across a grouped branch would cost more time than the extra runs.
Start with $10 in free credits
Change the runner label in your workflow and keep the rest of your GitHub Actions setup. Runner time is billed per minute.