Caching
RunnerHub includes automatic dependency caching to speed up build times. Caching is enabled by default and requires no manual configuration in your YAML.
How Caching Works
Section titled “How Caching Works”RunnerHub automatically detects, restores, and caches dependencies based on lock files:
- Detect — Check for known lock files (Podfile.lock, package-lock.json, etc.)
- Restore — If cache exists with matching hash, download and restore to build directory
- Build — Your pipeline runs with cached dependencies
- Save — New dependencies are cached for future builds
This cycle happens transparently, without any configuration required.
Cached Package Managers
Section titled “Cached Package Managers”RunnerHub automatically detects and caches these dependency managers:
| Tool | Lock File | Cache Key |
|---|---|---|
| CocoaPods | Podfile.lock | SHA-256 hash of lock file |
| Swift Package Manager | Package.resolved | SHA-256 hash of lock file |
| Bundler/Gems | Gemfile.lock | SHA-256 hash of lock file |
| npm | package-lock.json | SHA-256 hash of lock file |
| Yarn | yarn.lock | SHA-256 hash of lock file |
| pnpm | pnpm-lock.yaml | SHA-256 hash of lock file Requires npm install -g pnpm as the first step — pnpm is not pre-installed |
| DerivedData | (auto-detected) | {platform}-v2-{branch} |
| Metro (React Native) | (auto-detected) | {platform}-metro-{branch} |
Cache Keys
Section titled “Cache Keys”Each cache entry has a unique key based on the lock file or build artifacts:
Lock File Caching
Section titled “Lock File Caching”For CocoaPods, SPM, Gems, npm, and Yarn, the cache key is the SHA-256 hash of the lock file content:
Podfile.lock (unchanged) → Same cache key → Cache hit → Restore cached podsPodfile.lock (modified) → Different key → Cache miss → Download fresh podsThis means:
- If
Podfile.lockdoesn’t change between builds, dependencies are instantly restored - If dependencies are updated (Podfile.lock changes), a fresh download occurs and new cache is saved
DerivedData Caching
Section titled “DerivedData Caching”Xcode’s DerivedData is cached with a key based on platform and branch:
Key format: {platform}-v2-{branch}
Examples:- ios-v2-main- ios-v2-feature/auth- macos-v2-developThis allows different branches to maintain separate DerivedData caches, speeding up builds for feature branches without interfering with main branch builds.
Cache Lifecycle
Section titled “Cache Lifecycle”Cache Detection and Restore
Section titled “Cache Detection and Restore”Before your pipeline runs, RunnerHub checks for cached dependencies:
steps: - name: Install pods run: pod install # Uses cached pods if Podfile.lock unchangedTimeline:
- Repository cloned
- Cache checked (lock file hash computed)
- If cache exists and matches → dependencies restored
- If no match → fresh download
Cache Saving
Section titled “Cache Saving”After your pipeline completes successfully, new or updated dependencies are saved:
Build succeeds → New cache saved → Available for next buildBuild fails → Cache NOT saved (only successful builds cache)Cache is stored locally on the agent host machine, so subsequent builds on the same agent are nearly instant.
Cache Eviction
Section titled “Cache Eviction”RunnerHub automatically removes old caches to manage disk space:
| Criterion | Details |
|---|---|
| Age | Caches older than 14 days are deleted |
| Size | Total cache size is limited to 50 GB per agent |
When the 50 GB limit is reached, oldest caches are evicted first.
Zero Configuration Required
Section titled “Zero Configuration Required”No manual cache configuration is needed in your YAML. RunnerHub automatically detects and caches everything:
# This pipeline caches everything automaticallyname: My Pipelineplatform: ios
environment: xcode: "16.4"
triggers: - push
steps: - name: Install dependencies run: pod install # Automatically cached
- name: Install gems run: bundle install # Automatically cached
- name: Build run: fastlane buildCache Errors
Section titled “Cache Errors”If caching fails at any point, it never fails the build:
- Cache restore error? → Fresh download continues, build succeeds
- Cache save error? → Build completes normally, next build gets fresh dependencies
This ensures caching is purely an optimization—never a source of build failures.
Clearing Cache
Section titled “Clearing Cache”To clear cached dependencies for your app:
- Open your app in the RunnerHub dashboard
- Navigate to App → Settings
- Find the Cache section
- Click Clear Cache
This removes all cached dependencies for your app, forcing the next build to download fresh packages. Useful for:
- Troubleshooting cache-related issues
- Forcing a clean build
- Updating to the latest versions of cached dependencies
After clearing, the next build will:
- Download fresh dependencies
- Build normally
- Save new cache for subsequent builds
Common Cache Scenarios
Section titled “Common Cache Scenarios”Scenario 1: Fast Builds with Stable Dependencies
Section titled “Scenario 1: Fast Builds with Stable Dependencies”Your Podfile.lock doesn’t change for several commits:
Build 1: pod install (fresh) → 120 secondsBuild 2: pod install (cached) → 2 seconds (cache hit)Build 3: pod install (cached) → 2 seconds (cache hit)Build 4: Podfile.lock updated pod install (fresh) → 120 secondsBuild 5: pod install (cached) → 2 seconds (cache hit)Scenario 2: Branch-Specific DerivedData
Section titled “Scenario 2: Branch-Specific DerivedData”Feature branch builds maintain separate DerivedData caches:
main branch: ios-v2-main cachedevelop branch: ios-v2-develop cachefeature/auth: ios-v2-feature/auth cacheSwitching between branches doesn’t interfere with each other’s caches.
Scenario 3: Multiple Package Managers
Section titled “Scenario 3: Multiple Package Managers”A typical iOS project with multiple tools:
steps: - name: Install gems (cached) run: bundle install # Gemfile.lock → cache
- name: Install pods (cached) run: pod install # Podfile.lock → cache
- name: Build (cached DerivedData) run: xcodebuild build # Cached DerivedDataAll three caches work independently and simultaneously.
Cache Tips and Best Practices
Section titled “Cache Tips and Best Practices”Commit Lock Files
Always commit lock files to your repository so cache keys remain stable:
# Good: lock files committedPodfile.lock ✓ committedpackage-lock.json ✓ committedyarn.lock ✓ committed
# Bad: lock files not committedPodfile.lock ✗ gitignoredpackage-lock.json ✗ gitignoredWithout committed lock files, cache keys change every build and caching becomes ineffective.
Update Dependencies Intentionally
When you want to update dependencies, update them locally, commit the new lock file, and push:
pod updategit add Podfile.lockgit commit -m "Update CocoaPods dependencies"git pushThe new lock file creates a new cache key, and builds use fresh versions.
Monorepo Considerations
In a monorepo with multiple Podfile.lock files or package-lock.json files, each is cached independently:
repo/├── ios/│ └── Podfile.lock → Cached separately├── backend/│ └── package-lock.json → Cached separately└── shared/ └── Gemfile.lock → Cached separatelyCross-Platform Caching
Different platforms (iOS, macOS) have separate DerivedData caches:
ios-v2-main (iOS builds on main)macos-v2-main (macOS builds on main)Switching platforms doesn’t interfere with each other’s caches.
Debugging Cache Issues
Section titled “Debugging Cache Issues”Cache Not Being Used?
Section titled “Cache Not Being Used?”Check these common issues:
- Lock file not committed — Verify
Podfile.lock(or other lock files) are in your repository - Lock file path — RunnerHub looks for lock files in standard locations (repo root, directories)
- First build — The first build of an app won’t have cache; subsequent builds will
- Cache age — Caches older than 14 days are automatically deleted
Force a Fresh Build
Section titled “Force a Fresh Build”To rebuild without cache:
- Click Clear Cache in your app’s dashboard
- Trigger the next build
- Next build uses fresh dependencies and creates new cache
Verify Cache Hit
Section titled “Verify Cache Hit”Check your job logs for cache-related messages:
[cache] Checking for Podfile.lock...[cache] SHA-256: a1b2c3d4e5f6...[cache] Cache hit! Restoring...[cache] Pods restored in 2sor
[cache] No cache found for this hash[cache] Downloading dependencies...[cache] Pods installed in 120s[cache] Saving new cache...Platform-Specific Cache Examples
Section titled “Platform-Specific Cache Examples”iOS Project with CocoaPods
Section titled “iOS Project with CocoaPods”name: iOS Buildplatform: ios
environment: xcode: "16.4"
triggers: - push
steps: - name: Install pods run: pod install # Cached via Podfile.lock
- name: Build run: fastlane build # Uses cached DerivedDataSwift Package Manager
Section titled “Swift Package Manager”name: macOS Buildplatform: macos
environment: xcode: "16.4"
triggers: - push
steps: - name: Resolve packages run: swift package resolve # Cached via Package.resolved
- name: Build run: swift build # Uses cached DerivedDataNode.js Monorepo (React Native)
Section titled “Node.js Monorepo (React Native)”name: React Native Buildplatform: react-native
environment: node: lts xcode: "16.4" android_sdk: 34
triggers: - push
jobs: build-ios: name: Build iOS steps: - name: Install dependencies run: npm install # Cached via package-lock.json
- name: Install pods run: cd ios && pod install # Cached via Podfile.lock
- name: Build iOS run: xcodebuild -workspace App.xcworkspace -scheme App build # Uses cached DerivedData
build-android: name: Build Android steps: - name: Install dependencies run: npm install # Cache hit (already installed)
- name: Build Android run: cd android && ./gradlew assembleRelease # Cached gradle wrapperCaching behavior:
npm installin the iOS job creates and cachesnode_modulesnpm installin the Android job hits the cache (same lock file)- Metro bundler cache at
node_modules/.cache/metrois cached and restored per platform/branch - Both iOS and Android jobs benefit from the same dependency cache
Next Steps
Section titled “Next Steps”- Learn about Artifacts to collect build outputs
- Configure Timeout & Limits
- Review Environment Variables
- Check the YAML Reference for all options