Speed up runs
Speed has cold and warm times against GitHub's hosted runners, with the runs where GitHub was faster. The settings below go in shipcheck.toml on main.
Use host mode
runner = "host" runs the checks natively, with no container to start.
- Each check's clone starts from a template warmed from your checkout, so a built
target/ornode_modulesis already there. Keep your checkout built. - After each hourly warm, the next run rebuilds what embeds your checkout's path, such as cargo's workspace crates. Later runs reuse the run's own build.
- A Python
.venvis not shared. Putuv syncin the check, and it rebuilds from uv's cache in seconds. - The tool caches start empty, so the first run downloads dependencies once.
- Host mode runs on this machine's OS. A large Rust app's tests took 105 s on macOS against 16 s in act's Linux container, so a suite that is slow on your OS can be faster in act mode.
The sections below are for act mode.
Shard pytest
shard_tests = true
shipcheck splits the pytest step of each act job into parallel pytest processes in the job's container, as many as the Docker engine's CPUs and memory allow at 1.5 GiB each, at most 6. In host mode it splits the pytest command of each check the same way, as many as the machine's CPUs, at most 6. shard_tests = 4 fixes the count. Each process collects the whole suite and runs every N-th test. shipcheck checks that the shards together ran every collected test once and all passed. Anything else, a failing test included, runs the job again whole, and that result counts. Your command, lockfile and dependencies stay as they are, and nothing is installed.
The step must be one line in bash, such as pytest -v, uv run pytest, poetry run pytest or python -m pytest, with no ;, &&, pipes, redirects, $ or pytest-xdist flags. A host check can run other commands around it with &&, such as uv sync && uv run pytest -v, and shipcheck splits only the pytest part. A check with two pytest commands is not split. shipcheck doctor names the workflows and host checks that have one. Several agents at once each run their own shards, so on a busy machine a fixed shard_tests = 3 keeps the total lower.
The cost: a test that fails only after another test's leftovers can pass sharded, since the two may run in different processes. GitHub's serial run on main still catches it. shipcheck init turns sharding on when it writes act mode. Set shard_tests = false if a pass must mean the suite passed in order.
Give Docker more memory
Sharding runs out of memory before CPUs. shipcheck counts 1.5 GiB for each pytest process, so Docker Desktop's default 8 GB VM fits 5 shards. Parallel jobs share the same memory: every job starts at once unless max_parallel caps them, and Rust or JVM builds side by side can swap or be killed for lack of memory. Raise it in Docker Desktop, Settings, Resources, Memory, such as 16 GB on a Mac with 32 GB or more. More memory does not speed up one serial job. shipcheck doctor says when Docker has under 8 GB or under a third of the Mac's memory.
Let jobs download
network = "internet"
With the default none, a job reaches nothing, so steps that install packages or toolchains fail. With internet they work. A job then reaches the local network, and on a Mac with Docker Desktop also services on the Mac's 127.0.0.1 through host.docker.internal, see the limits. Turn it on only for code you trust as much as your own.
Keep caches between runs
act.cache_paths keeps paths in the job container in a Docker volume per repository: /home/runner/.npm, bun's cache, /home/runner/.cache, which holds uv and pip, and the per-job tool homes in /home/runner/.shipcheck. Add the cache of another tool if its lock lives inside the path. Each job's checkout is also a copy-on-write clone of the last one, build output included, so target/ or node_modules start warm.
Pick a Docker engine
Use Docker Desktop, the engine the act mode trials and benchmarks ran on. OrbStack and colima also ran act jobs, with limits: OrbStack sometimes showed a job the files of the previous run at the same path, which shipcheck now fails, and colima shares only your home directory, so the repository and SHIPCHECK_HOME must be under it. See install. This is colima as the Docker engine, not engine = "colima", whose tests and times are in engines and speed. Give the engine's VM enough CPUs and memory for max_parallel jobs at once, and for the shards of each.