DevOps / CI·CD April 20, 2026

2026-04-20 Ruby Bundler & CocoaPods Deterministic CI on Leased Apple Silicon Cloud Mac

MacXCode Engineering Team April 20, 2026 ~15 min read

Teams that rent Mac mini M4 or Mac Studio builders over SSH for iOS CI still ship plenty of CocoaPods codebases. The failure mode is never “Pods are bad”—it is non-deterministic Ruby toolchains: one job uses the system pod, another uses a Homebrew upgrade from Tuesday, and a third uses whatever gem install cocoapods left in ~/.gem. This 2026-04-20 runbook standardizes Bundler-managed CocoaPods on leased Apple Silicon hosts across HK / JP / KR / SG / US, aligns cache directories with per-job workspaces, and keeps NVMe predictable on multi-tenant machines. Cross-read SwiftPM vs CocoaPods trade-offs, CLT vs full Xcode, and parallel xcodebuild lanes so dependency resolution and compile steps stay in sync.

Why Bundler Still Matters on Headless Builders

CocoaPods is a Ruby program. The pod executable you invoke is only as stable as the gem set behind it. Bundler pins that set via Gemfile.lock, which is exactly what you want when twelve product teams share one cloud Mac fleet. Without Bundler, “green on my branch” often means “whoever logged in last upgraded CocoaPods globally.”

  • Reproducibility — same Gemfile.lock → same resolver behavior across regions.
  • Auditability — security can diff gem versions between releases.
  • Isolationbundle install --path vendor/bundle keeps gems inside the workspace, not in shared home directories.

Ruby Toolchain Matrix (3.1 vs 3.2 vs 3.3)

Apple Silicon macOS images often ship a recent system Ruby; some teams add rbenv or asdf. Pick one policy per fleet and encode it in your infra repo. Document the exact ruby -v next to xcodebuild -version in your build metadata.

Approach Pros Risks
System Ruby + Bundler Fast bootstrap on fresh SSH hosts OS upgrades can bump Ruby; pin with plist or CI env
rbenv per user Exact patch levels launchd jobs must source rbenv shims explicitly
asdf with .tool-versions Single file for Ruby + Node + others Longer first-run install time

Gemfile & Lockfile Discipline

Commit both Gemfile and Gemfile.lock. Pin cocoapods to a known-good minor, and add plugins (e.g., cocoapods-acknowledgements) explicitly—do not rely on “whatever the latest plugin resolves to.” For CI, prefer:

bundle config set --local deployment 'true' bundle config set --local path 'vendor/bundle' bundle install --jobs 4 --retry 3

Tip: run bundle check before pod install to fail fast when someone forgot to update Gemfile.lock after editing the Gemfile.

bundle exec pod install (and When to Add --deployment)

Always invoke bundle exec pod install (or bundle exec pod install --deployment when you want Bundler to enforce lockfile strictly). Never call bare pod in production pipelines unless you enjoy Friday-night nondeterminism. For CI caches, pass a deterministic CP_HOME_DIR or use CocoaPods’ recommended cache env vars scoped under your job directory.

Shared-host warning: if two jobs write to the same ~/Library/Caches/CocoaPods without namespacing, you can get partial downloads—namespace caches per $CI_JOB_ID or per repo clone path.

Caches, Pods/, and Workspace Layout

Keep Pods/ and *.xcworkspace generation inside the cloned repo path for that build. On ephemeral workspaces, you may cache vendor/bundle and CocoaPods specs cache between runs only when checksums match Gemfile.lock + Podfile.lock. When they do not, invalidate aggressively—stale specs caches cause obscure resolver errors that look like networking issues.

NVMe & Multi-Tenant Hygiene

CocoaPods downloads can add hundreds of megabytes per install; monorepos with multiple Podfiles multiply that cost. On 512 GB shared hosts, pair resolver runs with the disk guidance from simulator & archive cleanup. If you routinely exceed 70% disk usage during pod steps, split lanes or move to a 1 TB / 2 TB node via pricing instead of endlessly pruning mid-pipeline.

Regional Builders: CDN & Latency

Specs CDN access from Singapore or Tokyo is usually excellent, but corporate proxies or TLS inspection can add retries. Bake bundle install retry flags and consider a mirror or caching proxy if your security team requires it—document the exception per region so HK and US East behave consistently.

If you are migrating lanes to SwiftPM, compare cache sizes and lock semantics with SwiftPM resolve & cache. For signing after Pods resolve, revisit automatic vs manual signing guidance in your existing codesign articles—Bundler does not replace provisioning profiles.

FAQ: Bundler + CocoaPods on Cloud Macs

Question Practical answer
Can I use Homebrew CocoaPods? Avoid for CI—prefer Bundler-managed gems so upgrades are code-reviewed, not accidental package bumps.
What about Bundler 2 vs 1? Standardize on one major; mismatch between dev laptops and CI is a frequent “works locally” source.
Should CI commit Pods/? Team policy—either commit for hermetic builds or generate fresh each time; do not half-commit.

Bottom line: treat CocoaPods like any other compiler toolchain—pin versions, scope caches, and always go through bundle exec on shared SSH builders.

Lease dedicated Apple Silicon CI Macs

SSH-first · HK · JP · KR · SG · US