VPSSpark Blog
← Back to Dev Diary

2026 short-cycle self-hosted Git (Gitea/Forgejo), a light VPS control plane, and per-day cloud Mac for native iOS builds

Server Notes · 2026.05.07 · ~5 min read

Developer workspace: self-hosted Git, VPS webhooks, cloud Mac iOS CI

Teams that want data residency and predictable Git costs increasingly run Gitea or Forgejo on a small VPS while still needing native Xcode for iOS. The practical 2026 pattern is a split plane: the VPS hosts repositories, issues, and webhook ingress; a per-day cloud Mac (or a tagged pool slot) runs archive, tests, and signing. That keeps the control plane cheap and always on while you pay for Apple hardware only when merges actually need it.

2
Planes: VPS control · Mac execution
1
Deploy token per narrow scope
TLS
Webhook verify + replay defense

Why split the VPS and the Mac?

Running the forge on a 1–2 vCPU VPS is enough for HTTP Git, API, and Actions-style hooks if you offload heavy work. iOS builds want fast SSD, RAM for Swift and the linker, and a stable Xcode minor. Colocating everything on one Mac works for a single office; for short release cycles and contractors, isolating who can trigger which runner matters more than saving one hop of latency. The VPS receives push events, enqueues a job record, and calls your executor with a signed one-time token—similar public-ingress lessons appear in our Linux VPS webhook and health-check matrix FAQ, even though the payload there is chat rather than Xcode.

Webhooks, minimal tokens, and replay

Configure repository webhooks to hit only an HTTPS endpoint on the VPS or a reverse proxy in front of it. Verify the shared secret or signature header Gitea/Forgejo sends, reject old delivery IDs if the platform exposes them, and rate-limit per repository. For Git fetch inside the Mac job, prefer a read-only deploy key or scoped personal access token limited to that repo—never reuse an admin token on runners. For cross-repo modules, mirror internal dependencies on the same forge or use a second narrowly scoped credential. Rotate after every contractor offboarding.

Reference flow (logical)
# 1) Developer pushes to Forgejo/Gitea on VPS
# 2) Webhook POST → queue service (verify HMAC, idempotency key)
# 3) Queue issues job token → cloud Mac agent polls or SSH pull
# 4) Agent: git fetch (deploy key) → xcodebuild/archive → upload IPA/logs
# 5) Status API callback with commit SHA + artifact URL
Do not merge planes by accident
If the Mac agent stores a forge admin token to "speed up" API calls, any compromised build script becomes a full org takeover. Keep forge admin sessions off runners; use short-lived job tokens instead.

Enterprise resource pool isolation (decision matrix)

Use the matrix below when more than one product line or vendor shares Mac capacity. For buy-vs-lease and region concurrency tags at scale, extend the same thinking with our enterprise Mac runner pool decision matrix.

Isolation need VPS / forge side Cloud Mac side
Single mobile team One org, branch protection, shared webhook secret One dedicated runner user; shared DerivedData policy
Multi-team same company Separate Gitea orgs or Forgejo projects; distinct webhook URLs Runner labels per team; no shared Keychain unlock across labels
Vendor + internal mix Read-only mirroring fork; vendors never get main webhook Ephemeral per-job keychain; per-day Mac rental windows
Compliance-heavy Audit log export to WORM bucket; IP allow list on admin UI Separate Apple IDs / signing where policy requires; wipe session disk

Per-day cloud Mac versus a small always-on pool

Per-day (or per-sprint) rental fits teams whose App Store or TestFlight cadence is uneven: you spin up a known-good image, drain the merge queue, then release the machine. You trade slightly higher marginal cost per build minute for zero idle metal bill. A small pool of one or two tagged runners makes sense when trunk stays hot—queue depth is predictable, and you want signing identities or DerivedData warm between jobs.

Hybrid setups are common: the VPS webhook always writes to the same queue table; a cron on quiet days scales pool size to zero and lets only manual “release lane” jobs acquire a day pass. Document which branches may trigger Mac jobs (for example release/* and protected main) so feature branches cannot exhaust the pool with accidental loops. Pair that policy with branch protection on the forge so webhooks fire only after review, not on every draft push.

Network-wise, measure RTT from the Mac to the VPS Git endpoint separately from RTT to Apple notary services. A slow self-hosted Git path dominates small commits; caching Git LFS blobs on the VPS or a sidecar object store often beats widening the Mac uplink alone.

FAQ: Gitea vs Forgejo, cost spikes, and when to add a second Mac

Which forge in 2026? Feature sets converge; choose based on governance (FOSS policy), plugin compatibility, and your comfort with upstream release cadence. Either supports the split-plane model.

Why per-day Mac instead of always-on? Short cycles often mean a burst of release branches followed by quiet days. Paying for idle Metal nodes burns budget without improving queue depth.

When do I add another executor? When p95 queue wait exceeds your merge train SLA twice in a week, or when signing and UI tests must run in parallel on different Xcode pins.

Operational habit
Log webhook delivery latency separately from build time. A spike in the first often means TLS renewal or DNS on the VPS, not a slow compiler on the Mac.

On a cloud Mac mini, the execution plane stays honest

The VPS can stay tiny because it never runs xcodebuild. The heavy lane belongs on real Apple Silicon: unified memory keeps Swift and the linker fed, Gatekeeper and SIP reduce tampering risk versus ad-hoc Windows VMs, and macOS stability fits unattended CI. A Mac mini class box idles around 4W, so keeping a warm profile for nightly jobs is far cheaper than mis-sizing a 24/7 VM pretending to be a Mac.

Native Homebrew, SSH, and pinned Xcode images match what Gitea Actions or your custom agent expect—no fragile emulation layer between your webhook and the archive that ships to TestFlight.

If you are wiring Forgejo webhooks to real iOS builds, VPSSpark cloud Mac mini M4 is a practical place to land the execution planeexplore plans now and keep your self-hosted Git on the VPS where it belongs.

Limited offer

Hook self-hosted Git—run real iOS builds on cloud Mac

Forgejo on a VPS · Xcode on Apple Silicon · Day passes or steady runners

Back to home
Limited offer See plans now