CI Cached Indexes (Team Tier)
import { Aside, Tabs, TabItem } from ‘@astrojs/starlight/components’;
On large repos, a fresh forge index in CI can take 60–120 seconds. Team tier includes a remote cache that stores the index after the first build and restores it on subsequent runs. When only a small number of files change between commits, the restore + incremental update is typically under 5 seconds.
How the cache works
Section titled “How the cache works”The cache key is a hash of three inputs:
- The git commit SHA of the repo being indexed
- The Forge binary version
- The hash of
.forge/config.tomland.forge/team.yml
When these three inputs match a stored cache, Forge skips re-indexing entirely and restores the stored index directly. When files change between commits, Forge restores the closest ancestor cache and runs an incremental update — only changed files are re-parsed.
Cache integrity is verified automatically. A corrupted or tampered cache is rejected and Forge falls back to a full index.
Prerequisites
Section titled “Prerequisites”- Team tier license (
forge license --showshould displaytier: team) - Team ID (found in the Forge admin portal at
admin.forge.ironpinelabs.com) FORGE_LICENSE_KEYstored as a CI secret (GitHub Actions) or CI/CD variable (GitLab)
Simple approach: forge ci --team
Section titled “Simple approach: forge ci --team”The simplest way to use remote cache is to pass --team to forge ci. Forge handles upload and restoration automatically:
forge ci --team <team_id> --format github --fail-on-p0No separate forge index step needed. Forge checks the remote cache, restores if available, runs an incremental update if needed, runs health checks, and uploads the updated index back to the cache.
name: Forge Health Check
on: pull_request: branches: [main] push: branches: [main]
jobs: forge-health: runs-on: ubuntu-latest permissions: pull-requests: write checks: write
steps: - uses: actions/checkout@v4 with: fetch-depth: 0
- name: Install Forge run: | curl -fsSL https://downloads.forge.ironpinelabs.com/releases/latest/forge-linux-x86_64 \ -o forge && chmod +x forge && sudo mv forge /usr/local/bin/forge
- name: Run Forge CI with remote cache run: forge ci --team ${{ vars.FORGE_TEAM_ID }} --format github --fail-on-p0 env: FORGE_LICENSE_KEY: ${{ secrets.FORGE_LICENSE_KEY }}Store FORGE_TEAM_ID as a repository variable (not a secret — it’s not sensitive) and FORGE_LICENSE_KEY as a secret.
forge-health: stage: quality image: ubuntu:22.04
before_script: - apt-get update -qq && apt-get install -y -qq curl - curl -fsSL https://downloads.forge.ironpinelabs.com/releases/latest/forge-linux-x86_64 -o /usr/local/bin/forge - chmod +x /usr/local/bin/forge
script: - forge ci --team $FORGE_TEAM_ID --format gitlab --fail-on-p0 | tee forge-report.json - exit ${PIPESTATUS[0]}
artifacts: reports: codequality: forge-report.json when: alwaysSet FORGE_TEAM_ID as a CI/CD variable (unmasked — not sensitive) and FORGE_LICENSE_KEY as a masked variable.
Manual cache control
Section titled “Manual cache control”For more control, use --cache-to and --use-cache explicitly:
Upload after indexing:
forge index . --with-search --with-git \ --cache-to r2://<team_id>/<repo-hash>Restore before indexing:
forge index . --with-search --with-git \ --use-cache r2://<team_id>/<repo-hash><repo-hash> is a stable identifier for the repository — typically $(git remote get-url origin | sha256sum | cut -c1-16) to make it consistent across runners.
Full workflow using explicit cache control:
- name: Restore Forge index from remote cache run: | REPO_HASH=$(git remote get-url origin | sha256sum | cut -c1-16) forge index . --with-search --with-git \ --use-cache r2://${{ vars.FORGE_TEAM_ID }}/${REPO_HASH} || true # || true: if no cache exists (first run), fall through to full index
- name: Full index if cache miss run: forge index . --with-search --with-git
- name: Upload index to remote cache run: | REPO_HASH=$(git remote get-url origin | sha256sum | cut -c1-16) forge index . --cache-to r2://${{ vars.FORGE_TEAM_ID }}/${REPO_HASH}
- name: Run health check run: forge ci --repo . --format github --fail-on-p0Cache hit statistics
Section titled “Cache hit statistics”When --team is used, Forge logs cache performance to stdout:
forge ci: checking remote cache...forge ci: cache HIT (key: abc123def456, age: 4h32m)forge ci: restoring index (187 MB, 2.3s)forge ci: incremental update (14 files changed, 0.8s)forge ci: running health checks...A cold miss (no cache available):
forge ci: checking remote cache...forge ci: cache MISS — full index requiredforge ci: indexing 8,432 files (47.2s)forge ci: uploading to remote cache (187 MB, 3.1s)forge ci: running health checks...Cache retention
Section titled “Cache retention”Remote cache entries expire after 30 days of no access. Active repos (daily CI runs) never expire in practice. Manually purge a cache entry from the admin portal if you need to force a full re-index (e.g., after significant refactoring that breaks incremental updates).
Common pitfalls
Section titled “Common pitfalls”Cache never hits
The cache key includes the Forge binary version. If your CI downloads latest each run and Forge releases a patch, the version changes and every run is a cache miss. Pin to a specific version:
curl -fsSL https://downloads.forge.ironpinelabs.com/releases/v1.4.0/forge-linux-x86_64 \ -o forgeCache hit but index seems stale
Verify that fetch-depth: 0 is set in the checkout step. With shallow clones (fetch-depth: 1), forge index --with-git can’t read full git history, which affects the git activity data in forge_prepare results. It doesn’t affect the cache key — that’s based on commit SHA, not history depth.
“Team not found” error
The team ID in FORGE_TEAM_ID doesn’t match your account. Log in to admin.forge.ironpinelabs.com and copy the team ID exactly as shown.