Speeding Up Git Checkout in GitHub Actions
Git checkout is slow in GitHub Actions when every job clones the whole repository from GitHub. WarpBuilds/checkout@v7 seeds the tree from a cached mirror.
Last verified:
Git checkout is slow in GitHub Actions because every job downloads the repository from GitHub again, and the size of that download tracks history depth, submodules, and LFS objects rather than the code the pull request touched. On WarpBuild runners you shrink the download by swapping actions/checkout for WarpBuilds/checkout@v7, which seeds the working tree from a cached git mirror so the fetch from GitHub covers only the new commits.
This page covers how to measure what your checkout actually costs, how the mirror works, the exact workflow change, and the arithmetic that turns seconds of checkout per job into weekly runner minutes.
Diagnosis
Checkout is two operations wearing one name. The action fetches git objects over the network, then writes a working tree to disk. Disk writes scale with the size of the tree you check out. Network time scales with the size of the object download, and that is where the minutes go.
Five inputs decide the size of that download. Measure each one on a fresh clone of your own repository before changing anything.
| Driver | What it adds to the download | How to measure it |
|---|---|---|
| Pack volume | The compressed object store for the history you request | git count-objects -vH and read size-pack |
| Fetch depth | fetch-depth: 0 replaces a one-commit fetch with the entire history | git rev-list --count HEAD for commit count, then grep workflows for fetch-depth |
| Submodules | One additional fetch per module, each with its own handshake and auth | git config --file .gitmodules --get-regexp url |
| Git LFS | Binary objects stored outside the pack and pulled separately | git lfs ls-files -s |
| Job fan-out | Repeats all of the above once per job in the run | Count the jobs a single workflow run creates |
actions/checkout defaults to fetch-depth: 1, so a plain workflow pulls one commit and stays cheap. The expensive workflows are the ones that had a reason to change that default. Changed-file detection, git describe version stamping, monorepo affected-target calculation, and coverage tools that diff against the merge base all want history, and the usual fix is fetch-depth: 0. That one line converts a small fetch into the whole pack on every job that carries it.
Submodules behave the same way at a smaller scale each and a larger scale in aggregate. submodules: recursive makes checkout walk the module graph and fetch each entry, so a repository with eight vendored modules performs nine fetches rather than one, each paying its own connection setup.
LFS is separate again. lfs: true pulls the binary payloads that the pack only points at, and on repositories that store fixtures, models, or game assets that payload is often larger than the entire git history.
The fifth driver is the one teams miss, because it is invisible in a single job log. Fan-out multiplies every driver above. A workflow with a 4-way test shard, a lint job, a type-check job, and two build jobs is eight checkouts of the same commit, issued within seconds of each other, against the same repository on GitHub. Run that workflow on 30 open pull requests in an afternoon and it is 240 full fetches of a tree that changed by a handful of files. WarpBuild adds capacity to that shape rather than throttling it: run as many jobs as your workflows need, since generally available Linux and Windows runners do not have plan-level concurrency caps. Wide fan-out is the correct answer for wall-clock time, and it is exactly what makes per-job checkout cost worth attacking.
To get your own number, read the Checkout step duration in a job log across ten recent runs and take the median rather than the best case. Then compare a pull request job against a job on the default branch. If the two differ sharply, the difference is usually branch-specific fetch work rather than the base history.
Fix
Swap the action.
- - uses: actions/checkout@v4
+ - uses: WarpBuilds/checkout@v7WarpBuilds/checkout is a fork of actions/checkout that adds git-mirror caching on WarpBuild runners. It shipped on July 14, 2026 and is recorded in the WarpBuild changelog. The mechanism is a cache in front of GitHub rather than a change to git itself:
- The mirror holds a per-repo
basebundle carrying full history, built once, plus a small per-branch delta bundle. - The action seeds that mirror onto the runner before checkout runs, then fetches from GitHub only the commits that arrived after the mirror was built.
- The seed arrives over parallel ranged reads from object storage rather than a single stream from GitHub.
- The remaining fetch against GitHub is small, which drops the request load your fan-out places on GitHub.
Three properties make the swap low risk.
It takes no new inputs. Every actions/checkout input keeps working unchanged: repository, ref, token, ssh-key, fetch-depth, sparse-checkout, lfs, and the rest. Workflows that pin fetch-depth: 0 or submodules: recursive keep those lines exactly as they are.
It adds one output, cache-hit, so you can tell from the log whether a run was seeded from the mirror or fell through to a plain fetch.
It fails open. Any cache error degrades to stock actions/checkout behavior, so a cold mirror or an unreachable cache produces a slower checkout instead of a failed job. No fallback logic belongs in your workflow.
The mirror lives on WarpBuild runners, so the job has to be running on a warp- label for it to apply. The labels and rates for the Linux fleet are in the cloud runners catalog and repeated in the cost model below.
The changelog entry publishes no measurement, so this page attaches no speed number to the swap. Take the median Checkout duration you collected in the diagnosis step, run ten more jobs after the change, and compare the two medians on your own repository.
Configuration
Here is a complete workflow. It runs a 4-way shard on warp-ubuntu-latest-x64-4x, a 4 vCPU, 16 GB Ubuntu 24.04 runner, keeps full history for changed-file detection, and logs the mirror result.
name: ci
on:
pull_request:
merge_group:
push:
branches: [main]
jobs:
test:
runs-on: warp-ubuntu-latest-x64-4x
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
steps:
- name: Checkout
id: checkout
uses: WarpBuilds/checkout@v7
with:
fetch-depth: 0
- name: Record mirror result
run: echo "checkout cache-hit=${{ steps.checkout.outputs.cache-hit }}"
- run: npm ci
- run: npm test -- --shard=${{ matrix.shard }}/4Repositories that carry vendored modules or binary fixtures pass those inputs through unchanged:
- uses: WarpBuilds/checkout@v7
with:
submodules: recursive
lfs: true
fetch-depth: 0Two behaviors shipped in July 2026 matter for real pipelines, and both are in the changelog.
Shallow clones, July 14, 2026. A workflow that leaves fetch-depth at its default of 1 wants one commit, and downloading a full base mirror to serve it would be a bad trade. The action now handles that case separately: it seeds a small per-branch shallow snapshot, carrying the tip's pack rather than the whole history. Once that snapshot is seeded and anchored, the fetch from GitHub covers only the commits since it, and the result is a normal shallow checkout that matches what upstream actions/checkout produces.
Merge queues, July 27, 2026. A merge_group event checks out a temporary queue ref that exists only while that entry sits in the queue, so it has no per-branch delta of its own and nothing to anchor against. The action now uses the base branch for merge queue runs, which is why the workflow above lists merge_group alongside pull_request without any extra configuration. Merge queue jobs sit directly on the merge path, so they are usually where checkout time is worth the most.
Two more configuration notes. allow-unsafe-pr-checkout defaults to false, keeping the upstream safety behavior for pull requests from forks; leave it alone unless you have already reasoned through fork-PR checkout. And sparse-checkout reduces what gets written to disk without reducing the history you asked for, so it pairs with a fetch-depth decision rather than replacing one.
For rollout across many repositories, use your repository automation to apply and verify the same one-line workflow change.
Cost or Time Model
Checkout minutes bill at the same per-minute rate as every other minute in the job. WarpBuild's Linux rates, from the pricing page:
| Runner label | vCPU | RAM | Price per minute |
|---|---|---|---|
| warp-ubuntu-latest-x64-2x | 2 | 8 GB | $0.004 |
| warp-ubuntu-latest-x64-4x | 4 | 16 GB | $0.008 |
| warp-ubuntu-latest-x64-8x | 8 | 32 GB | $0.016 |
| warp-ubuntu-latest-x64-16x | 16 | 64 GB | $0.032 |
| warp-ubuntu-latest-x64-32x | 32 | 128 GB | $0.064 |
| warp-ubuntu-latest-arm64-4x | 4 | 16 GB | $0.006 |
| warp-ubuntu-latest-arm64-8x | 8 | 32 GB | $0.012 |
| warp-ubuntu-latest-arm64-16x | 16 | 64 GB | $0.024 |
Rates checked on 2026-08-13 against the WarpBuild pricing page.
Now the conversion. One second of checkout removed from one job is worth jobs_per_week x seconds / 60 minutes per week. Fill in your own median from the diagnosis step; the table gives the shape at three job volumes.
| Seconds removed per job | 300 jobs per week | 1,200 jobs per week | 5,000 jobs per week |
|---|---|---|---|
| 10 | 50 minutes | 200 minutes | 833 minutes |
| 20 | 100 minutes | 400 minutes | 1,667 minutes |
| 30 | 150 minutes | 600 minutes | 2,500 minutes |
| 45 | 225 minutes | 900 minutes | 3,750 minutes |
| 60 | 300 minutes | 1,200 minutes | 5,000 minutes |
Priced at $0.008 per minute for warp-ubuntu-latest-x64-4x, the 1,200-jobs-per-week column runs from $1.60 per week at 10 seconds removed to $9.60 per week at 60 seconds. Over a month at 4.33 weeks, 30 seconds per job across 1,200 jobs per week is 2,598 minutes and $20.78.
The money is the smaller half of the result. The larger half is lead time, and it does not scale with job count at all. Shards run in parallel, so checkout sits once on the critical path of each run. Thirty seconds removed from a run that merges 60 times a day is 1,800 seconds, or 30 minutes a day of waiting spread across the team. On a merge queue that number compounds, because every entry in the queue pays it in sequence.
If checkout dominates your job time because the repository itself is large, read the large repository guide next. If the cost is concentrated in fan-out across many packages, the monorepo setup is the closer fit. If the seconds disappear before the first step runs rather than during checkout, the problem is provisioning, and the cold starts guide is the right page.
FAQ
Why is git checkout slow in GitHub Actions?
Because every job downloads the repository from GitHub again. Five inputs set the size of that download: the pack volume of the repository, the fetch depth the workflow asks for, submodules, Git LFS objects, and the number of jobs in the run that each repeat the whole fetch.
Is WarpBuilds/checkout a drop-in replacement for actions/checkout?
Yes. It is a fork of actions/checkout that adds git-mirror caching on WarpBuild runners. It takes no new inputs, adds one output named cache-hit, and falls open to stock actions/checkout behavior if the cache is unavailable, so a workflow switches by changing one uses line.
Does WarpBuild Checkout work with shallow clones and merge queues?
Yes. Shallow-clone handling shipped on July 14, 2026 and seeds a small per-branch shallow snapshot rather than the full base mirror, producing a normal shallow checkout. Merge queue support shipped on July 27, 2026 and anchors merge_group runs on the base branch.
Does fetch-depth 0 still work?
Yes. The mirror holds a per-repo base bundle carrying full history, built once, plus a small per-branch delta bundle. A workflow that needs full history for changed-file detection or version tagging keeps fetch-depth: 0, and the fetch from GitHub still covers only the commits that arrived since the mirror was built.
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.