This document describes Local CI’s caching and performance features in detail: what each cache does, how it works, how to configure it, and how to verify it is being used.
See also: User Guide for installation and day-to-day usage; Design Guide and Preparation and Plan for development and implementation plan.
Local CI speeds up repeated runs by caching:
- Compilation output (ccache) so unchanged source files are not recompiled.
- Boost superproject so the repo is cloned/updated once and reused.
- B2 build tree (per-job) so Boost.Build (b2) only rebuilds what changed.
- CMake configuration (per job + input digest) so configure is skipped when inputs are unchanged.
- APT package archives so the “Install packages” step reuses downloaded
.debfiles.
With caches warm, full Linux CI can complete in under ~2 minutes and incremental builds in under ~30 seconds.
┌─────────────────────────────────────────────────────────────────┐
│ MCP / CLI │
└─────────────────────┬───────────────────────────────────────────┘
│
┌─────────────────────▼───────────────────────────────────────────┐
│ Job Executor (act) │
│ Linux Base Images │
└─────────────────────┬───────────────────────────────────────────┘
│
┌─────────────┼─────────────┐
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Build │ │ Boost │ │ CMake │
│ Artifact │ │ Dependency │ │ Config │
│ Cache │ │ Cache │ │ Cache │
│ (ccache) │ │ + b2-source │ │ (per-job) │
└───────────────┘ └───────────────┘ └───────────────┘
│ │ │
└─────────────────┼─────────────────┘
▼
┌─────────────────┐
│ Host cache dirs │
│ (~/.localci/ │
│ cache/...) │
└─────────────────┘
Cache directories on the host are bind-mounted into the job container so that compilers, B2, CMake, and apt see persistent data across runs.
Stores compiled object files and metadata so that if you re-run the same job without changing source code, the compiler step is skipped or greatly reduced. This applies to both B2 and CMake builds.
- A single shared directory (e.g.
~/.localci/cache/ccache) is mounted into every job container. - Local CI sets
CCACHE_DIRand wraps compilers with ccache:CC=ccache gcc,CXX=ccache g++(or the matrix compiler). B2 and CMake then call these wrappers and ccache stores/retrieves results by content hash. - First run: compilations run as usual and populate the cache. Later runs: cache hits for unchanged files, so only changed files (and their dependents) are recompiled.
In .localci.yml:
cache:
enabled: true
ccache:
enabled: true
max_size: "5G" # ccache size limit
compress: true # CCACHE_COMPRESS
dir: ~/.localci/cache/ccache # optional; default under cache.directory- Disable for a run:
localci run --no-cache - View stats:
localci cache stats(runsccache -son the configured dir) - Clear:
localci cache clear --target ccache(or--target all)
After a run, localci cache stats shows hit/miss counts. After an incremental change, you should see many cache hits and only a few compilations in the B2/CMake logs.
Keeps a single clone of the Boost superproject (e.g. develop or master) on the host. Jobs use this instead of cloning Boost inside the container every time, and they can update it incrementally with git fetch + git reset.
- Local CI (or you via
localci cache update) clones Boost once into e.g.~/.localci/cache/boostand runsgit submodule update --init --recursive. The workflow is patched so that whenBOOST_ROOTis set (the mount path), the “Clone Boost” step is skipped and a “Use cached Boost” step createsboost-sourcefrom the cache. - The cache is branch-specific: one directory per branch (e.g.
develop). Refreshing is done withgit fetchandgit reset --hard origin/<branch>so you get the latest commits without a full clone.
cache:
boost:
enabled: true
dir: ~/.localci/cache/boost
branch: develop
shallow: true
remote: https://github.com/boostorg/boost.git
build_dir: true # enables b2-source per-job cache (see below)- Refresh Boost only:
localci cache update(no need to run full CI) - Clear:
localci cache clear --target boost
Persists the per-job boost-root tree (Boost source plus B2’s bin.v2 and the b2 binary) so that B2 sees stable timestamps and only rebuilds files that actually changed. This is the main enabler of incremental B2 builds.
- For each matrix job, Local CI reserves a directory like
~/.localci/cache/b2-source/<job_matrix_key>and mount it atLOCALCI_B2_SOURCE_DIRin the container. - The workflow is patched: instead of always doing
cp -rL boost-source boost-root, on cache hit the job uses the existing tree (onlylibs/<module>is cleared and replaced with the current repo copy), and restores file timestamps for capy sources from a saved snapshot so B2’s change detection is correct. On first run it does the usual copy and seeds the cache. - B2 bootstrap is also short-circuited when the cached tree already contains the
b2binary, saving ~20s per job.
Controlled by cache.boost.build_dir (default true). When true, the b2-source paths are derived from cache.directory and the job/matrix key.
- Clear:
localci cache clear --target b2-source
- Run with verbose output:
localci run -v, or open the job log from the run. - In the “Boost B2 Workflow” step, B2 prints one line per compilation (e.g.
compile.c++ ... thread_name.o). After a one-file change you should see only one or a few such lines; a full rebuild shows many. - Count compilations:
grep -c "compile.c++" <path-to-job-log>. A small number (e.g. 1–3) means incremental; 50+ suggests a larger or full rebuild. - The B2 step for an incremental change should finish in well under 30 seconds.
Stores the CMake build directory (configure + build tree) per job and per “input digest.” When CMakeLists.txt, toolchain, compiler, or BOOST_ROOT do not change, the workflow can reuse the same directory and skip configure.
- Local CI computes a digest from configured inputs (e.g.
CMakeLists.txt,cmake/*.cmake, compiler, BOOST_ROOT). The cache path is~/.localci/cache/cmake/<job_matrix_key>_<digest>. - The directory is bind-mounted and
LOCALCI_CMAKE_CACHE_DIRis set. The workflow (or cmake-workflow action) should use this as the build directory when set; then configure runs only when the digest changes (e.g. after editing CMakeLists.txt or changing compiler).
cache:
cmake:
enabled: true
dir: ~/.localci/cache/cmake
inputs: [CMakeLists.txt, cmake/*.cmake] # optional; used for digest- Clear:
localci cache clear --target cmake
Speeds up the workflow’s “Install packages” step (e.g. apt-get install or the package-install action) by reusing downloaded .deb files across runs instead of downloading them again in every container.
- A per-job directory (e.g.
~/.localci/cache/apt/<job_matrix_key>) is bind-mounted over the container’s/var/cache/apt/archives. The first run downloads packages into this dir; subsequent runs for the same job use the same dir, soapt-get installfinds the packages locally and runs much faster.
cache:
apt:
enabled: true
dir: ~/.localci/cache/apt- Clear:
localci cache clear --target apt
- Cache root:
~/.localci/cache/(orcache.directoryin.localci.yml). - Subdirectories:
ccache/— shared compilation cache.boost/— Boost superproject clone (one branch at a time).b2-source/<job_matrix_key>/— per-job boost-root + bin.v2.cmake/<job_matrix_key>_<input_digest>/— per-job, input-keyed CMake build dir.apt/<job_matrix_key>/— per-job APT archives.
- In the container: ccache, boost, b2-source, and cmake are under
/tmp/localci-cache/; apt is mounted at/var/cache/apt/archives. Environment variables set by Local CI:CCACHE_DIR,CCACHE_MAXSIZE,CCACHE_COMPRESS,BOOST_ROOT,LOCALCI_B2_SOURCE_DIR,LOCALCI_CMAKE_CACHE_DIR.
- Disable all caches for a run:
localci run --no-cache - Override cache root:
localci run --cache-dir /path/to/cache - Clear one or all:
localci cache clear --target ccache|boost|cmake|b2-source|apt|all(optionally with--yesto skip confirmation)
| Metric | Target |
|---|---|
| Full Linux CI | < 2 minutes (with warm cache) |
| Incremental build | < 30 seconds |
| Single job | < 15 seconds (warm cache) |
| Cache hit rate | > 90% after warm-up |
- Implementation plan and issue breakdown: Preparation and Plan.
- Architecture and MCP: Design Guide.
- Config reference and troubleshooting: User Guide.