Speed
Each row compares shipcheck on an Apple silicon Mac with GitHub's hosted runners on the same commit, unless the notes say otherwise. GitHub's time runs from the run's creation to its end, so it includes the queue. To make your own runs faster, see speed up runs.
What makes a run fast
- A tree that was already tested answers from shipcheck's cache with no run, in 0.13 to 1.05 s in the runs on this page. A rebase or a squash that keeps the tree needs no new run.
- After the first run, the toolchains, the packages and the build output, such as
target/, are already on the machine, so a warm run builds only what changed. - GitHub gives every job a machine of its own. shipcheck runs as many jobs at once as the machine allows, so a workflow of many short jobs can take longer here.
A first run is cold. It downloads the images, the toolchains and the packages, and builds every dependency, so it is often slower than GitHub.
Act mode
Measured on an idle Apple silicon Mac, Docker Desktop with 14 CPUs and 15.6 GiB, act 0.2.89.
| Repository | Run | shipcheck | GitHub Actions | Date |
|---|---|---|---|---|
| A large Rust app, 4 jobs, about 2,100 tests | Cold | 238 s | 116 s | 2026-10-23 |
| A large Rust app, 4 jobs, about 2,100 tests | Warm | 105 s | 124 s | 2026-10-23 |
| A large Rust app, 4 jobs, about 2,100 tests | Warm | 105 s | 115 s | 2026-10-23 |
| pallets/flask, 9 jobs | Cold | 131 s | about 37 s | 2026-10-23 |
| pallets/flask, 9 jobs | Warm | 38 and 39 s | about 37 s | 2026-10-23 |
The Rust app's gitleaks job failed under act in every round, so its times are to that result. A warm run compiles only the app's own crate. The cold run also installs the toolchain and builds every dependency.
Host mode
On 2026-10-27, shipcheck init wrote host checks for some of the jobs of each repository below, and the rest stayed on GitHub. One repository ran at a time, with a load of 4 to 6 on 16 cores before each run and no other shipcheck run or build. Docker was down, so init kept every Docker job on GitHub. The GitHub column is the longest of those jobs, as the median over the last 10 successful PR heads.
| Repository | Jobs shipcheck ran | Cold | Warm | Cached | Those jobs on GitHub |
|---|---|---|---|---|---|
| shipcheck | 2 of 3 | 134 s | 70 s | 0.36 s | 172 s |
| BurntSushi/ripgrep | 4 of 23 | 30 s | 23 s | 0.48 s | 49.5 s |
| pallets/flask | 1 of 14 | failed every run | failed every run | 18 s | |
| cli/cli | 1 of 10 | 16 s | 15 s | 0.36 s | 31.5 s |
| curl/curl | 1 of 178 | 10 s | 9.7 s | 1.05 s | 20 s |
- flask's pre-commit check failed in the sandbox on a path its cache stored from an earlier run. shipcheck PR 122 lets those runs read it.
- shipcheck's
ci.ymlno longer ran on PRs, so its GitHub time is from push runs on main.
Host mode runs on your machine's OS. On the idle Mac of the act mode table, on 2026-10-23, the large Rust app's tests took 105 s on macOS against 16 s in act's Linux container.
Colima
The job is shipcheck's own ci.yml/test, which runs the fmt check, clippy and cargo test, with engine = "colima", run alone with shipcheck run --job test. Each version ran in its own empty SHIPCHECK_HOME, on the same Apple silicon Mac. These colima runs were measured on 2026-10-30 with the Mac's load between 2.6 and 4.1, so they are a first look. The time is the job's runner time.
| Version | Run | shipcheck | Load at start | Date |
|---|---|---|---|---|
| v0.2.1 | Cold | 179 s | 4.0 | 2026-10-30 |
| v0.2.1 | Warm | 190 s | 2.6 | 2026-10-30 |
| v0.2.4 | Cold, builds the base VM once | 134 s | 2.6 | 2026-10-30 |
| v0.2.4 | Warm, clones the base VM | 85 s | 4.1 | 2026-10-30 |
The same job on GitHub Actions, on ubuntu-latest with rust-cache, took 86 s of runner time, from 13:25:18 to 13:26:44 UTC (run 40881747310 on main, 2026-10-29). v0.2.4 boots each job's VM from a clone of a set-up base, fetches the actions on the Mac, and deletes the VM after the result, which is where the difference comes from. One more v0.2.1 warm run took 493 s while something else ran, and it is left out.
First look, measured on a busy machine
Other benchmarks or agents ran on the Mac during these runs, or the load was not recorded. The shipcheck times are rough.
Host mode
| Repository | Run | shipcheck | GitHub Actions | Date |
|---|---|---|---|---|
| A small Rust crate, fmt, clippy and tests | First run, in the sandbox | 4.9 s | about 20 s | 2026-10-22 |
| A small Rust crate, fmt, clippy and tests | Warm, in the sandbox | 1.9 to 2.3 s | about 20 s | 2026-10-22 |
| expressjs/express, lint and 1,261 tests | Warm, in the sandbox | 3.4 to 6.8 s, one round failed | about 3.5 min | 2026-10-22 |
| Another large Rust app, 5 checks | Warm | 422 s | 413 s | 2026-10-23 |
| Another large Rust app, 5 checks | Warm, after a rebase onto main | 498 s | 413 s | 2026-10-23 |
- The small crate's GitHub time is its Linux leg.
- express's tests were flaky under the load, also outside shipcheck.
- The second Rust app's GitHub time is one run of its CI on main.
Act mode
| Repository | Run | shipcheck | GitHub Actions | Date |
|---|---|---|---|---|
| A large Python app, 1 job, about 2,000 tests | Cold | 716 s, the retry passed | 367 s | 2026-10-21 |
| A large Python app, 1 job, about 2,000 tests | Warm | 468 s | 316 s | 2026-10-21 |
| A large Python app, 1 job, about 2,000 tests | Warm, after setup fixes | 274 to 341 s | 331 to 411 s | 2026-10-22 |
| A large Python app, 1 job, about 2,000 tests | Warm, tests in 6 shards | 162 and 195 s | 345 and 337 s | 2026-10-22 |
| A small Rust crate, fmt, clippy and tests | Cold | 30 s | 38 s | 2026-10-22 |
| A small Rust crate, fmt, clippy and tests | Warm | 7 s | 21 and 33 s | 2026-10-22 |
| pallets/flask, 9 jobs | Cold | 289 s | 36 s | 2026-10-22 |
| pallets/flask, 9 jobs | Warm | 63 and 64 s | 36 s | 2026-10-22 |
| BurntSushi/ripgrep, 8 jobs | Cold | 284 s | 78 s | 2026-10-22 |
| BurntSushi/ripgrep, 8 jobs | Warm | 73 and 84 s | 78 s | 2026-10-22 |
| junegunn/fzf, 1 job | Cold | 462 s | 378 s | 2026-10-22 |
| junegunn/fzf, 1 job | Warm | 381 and 385 s | 378 s | 2026-10-22 |
| expressjs/express, 10 jobs | Cold | 220 s | 148 s, 96 s of it queued | 2026-10-22 |
| colinhacks/zod, 5 jobs | Cold | 651 s | 177 s | 2026-10-22 |
| colinhacks/zod, 5 jobs | Warm | 588 s, one more round failed | 177 s | 2026-10-22 |
| faker-ruby/faker, 6 jobs | Cold | 106 s | 29 s | 2026-10-22 |
| faker-ruby/faker, 6 jobs | Warm | 67 and 68 s | 29 s | 2026-10-22 |
- The Python app lost on pytest. Four tests call an outside API many times, and those calls take longer from the Mac than from GitHub. Sharding split the tests over 6 processes.
- flask and faker finish on GitHub in 30 to 40 s, and starting containers costs more than their tests. On a later version, flask took 38 and 39 s warm in the idle run above.
- zod's package install took 140 s or more on the macOS bind mount, against 12 s on GitHub.
- express's warm runs failed on a port clash between jobs that shared one network.
- The public repositories' GitHub times come from a recent run on their default branch. shipcheck ran the same commit with only the Linux jobs.
On a 1-CPU Linux server
A rented server with 1 vCPU and 3.9 GB of memory, serving a small web app at the same time. It ran one job at a time.
| Repository | Cold | Warm | GitHub Actions | Date |
|---|---|---|---|---|
| A small Rust crate, 1 job | 103 s | 19 and 20 s | 23 s | 2026-10-22 |
| gin-gonic/gin, 11 jobs | 2,579 s | 513 and 519 s | 105 s | 2026-10-22 |
| expressjs/express, 10 jobs | 1,235 s | 318 and 321 s | 148 s, 96 s of it queued | 2026-10-22 |
A job took about as long there as on GitHub's runner. GitHub runs 10 or 11 jobs at once, so a warm run there took about as long as the sum of GitHub's job times.
A tree already tested
| Repository | Mode | shipcheck | GitHub Actions | Date |
|---|---|---|---|---|
| A large Python app | Act | 0.13 s | a full run | 2026-10-22 |
| A small Rust crate and expressjs/express | Host | 0.26 s | a full run | 2026-10-22 |