Cloudflare Workers Builds vs GitHub Actions — and the Headless Mac Mini That Beats Both on Price
WebChapter 24 of the Ultimate Web Development Series27 minMay 31, 2026Beginner
Ch 21 made a promise and kept it: for a normal-sized web project, CI is free, and you have to work to spend a cent on it. Public repos are unlimited, private repos get a generous free tier, and a solo developer can run the whole checklist locally for nothing.
This chapter is about the one situation where that stops being true:
You're not just deploying a web Worker anymore. You're also building a macOS or iOS app — and the macOS runner bills at 10x. Now "free CI" has a meter on it, and the meter spins fast.
That's the SimpleAppShipper situation exactly: a Cloudflare Worker site (cheap to build) and a notarized Mac app (expensive to build). So this chapter does four concrete things. It walks through Cloudflare Workers Builds as an actual build pipeline — not the two-row table from Ch 21, the real thing. It runs that pipeline head-to-head against GitHub Actions for the identical deploy. It gives you a click-by-click tour of where the money shows up on both dashboards, and how to cap it before it surprises you. And then it makes the argument the title gives away: for anyone building Apple platforms regularly, the cheapest long-term CI is a headless Mac Mini you own — with the break-even math to prove it and a no-monitor setup guide to do it.
Two Build Robots, One Job
Before pricing, get the shapes straight. You're choosing between two different kinds of robot, and they're not really competitors — they're good at different halves of the job.
Cloudflare Workers Builds
GitHub Actions
What it is
A built-in "push → build → deploy my Worker" pipeline
A general-purpose "run any checklist on a VM" engine
Config
A few fields in the Cloudflare dashboard — no YAML
A .github/workflows/*.yml file you write
Runs on
Cloudflare's Linux builders only
Linux, Windows, or macOS runners
Knows your credentials?
Yes — it's your Cloudflare account already
No — you store secrets yourself
Can gate a PR?
No. It deploys what's on the branch
Yes. The green check blocks merge
Can build an iOS/Mac app?
No — Linux can't run Xcode
Yes, on a macOS runner (at 10x)
The last row is the whole chapter in one line. Workers Builds is perfect for the web half of this project and physically cannot touch the Apple half. GitHub Actions can do both — but the moment it touches the Apple half, the price changes character. Hold that thought; we'll come back to it after the tour.
Cloudflare Workers Builds, Properly
Ch 15 deployed the Worker the manual way (npx wrangler deploy) and Ch 21 mentioned Workers Builds in passing. Here's the real feature.
Workers Builds is Cloudflare's git integration for Workers. You connect your GitHub (or GitLab) repository once in the dashboard, and from then on every push to your chosen branch runs a build command on Cloudflare's infrastructure and deploys the result. There is no workflow file — no YAML to write, no CLOUDFLARE_API_TOKEN to generate and paste into GitHub, because Cloudflare already is your account.
Connect your GitHub repo and pick the production branch (e.g. main).
Set three fields:
Field
For this project (Next.js on OpenNext)
Build command
npm run cf:build
Deploy command
npm run cf:deploy (or npx wrangler deploy)
Root directory
website-next (the build lives in a subfolder)
That's it. Push to main, and Cloudflare clones the repo, runs npm ci, runs your build command, runs your deploy command, and your site is live at the edge. The exact commands SimpleAppShipper runs by hand today — npm run cf:build && npm run cf:deploy — become the thing the robot runs for you.
Loading diagram…
Figure 1 — Workers Builds is push-to-deploy with no YAML. You configure three fields once; Cloudflare runs your build + deploy on every push. The only metered part is the middle box: build-minutes.
What a "build-minute" is, and what it costs
A build-minute is wall-clock time Cloudflare's builder spends running your build — clone, npm ci, cf:build, deploy. A Next.js-on-OpenNext build is typically a couple of minutes. The pricing:
Plan
Included build-minutes / month
Concurrent builds
Overage
Free
3,000
1
—
Workers Paid ($5/mo)
6,000
6
$0.005/min
To feel the scale: at ~3 minutes a build, 3,000 free minutes is ~1,000 deploys a month. A one-person project pushing a dozen times a day uses maybe 5% of that. You will not leave the free tier for the web side. Workers Builds is, for practical purposes, free for this project — which is exactly why it stays in the picture even after we buy hardware.
The Same Job on GitHub Actions
Now the head-to-head. Here's the identical deploy — push to main, build, ship the Worker — written as a GitHub Actions workflow. This is the Ch 15 pattern, condensed:
Functionally it lands in the same place as Workers Builds. The differences are all in the cost shape:
It runs on ubuntu-latest — the 1x multiplier, the cheapest runner. A 3-minute build burns 3 minutes of your allowance.
You had to generate a CLOUDFLARE_API_TOKEN and store it as a GitHub secret (Ch 15 walked through this). Workers Builds skips that entirely.
On a public repo this is free and unlimited. On a private repo it draws from your 2,000 free Linux minutes/month — still ~600 builds before you pay a cent.
For web deploys, GitHub Actions on Linux and Workers Builds are both effectively free. So if it were only the web, this would be a coin-flip and Ch 21 already told you to flip it. It isn't only the web.
Head-to-Head: the Web Deploy
Workers Builds
GitHub Actions (Linux)
Setup effort
3 dashboard fields
Write + maintain a YAML file
Secrets to manage
None
CLOUDFLARE_API_TOKEN + account ID
Free tier
3,000 build-min/mo
Unlimited (public) / 2,000 min (private)
Overage
$0.005/min (paid plan)
~$0.008/min (private Linux)
PR merge gate
No
Yes
Realistic monthly cost here
$0
$0
The honest verdict for the web side: use Workers Builds for the deploy (zero secrets, zero YAML, free), and add a tiny GitHub Actions workflow only if you want the PR test-gate. Both cost nothing. This is the "use each for what it's best at" split from Ch 21, applied.
Now the part that actually costs money.
Where the Money Actually Shows Up
You asked where to look — here are the exact paths. Bookmark both. The single biggest reason CI bills surprise people is that nobody ever opened these two pages until the email arrived.
Cloudflare: build-minutes and the bill
What you want to see
Click path
Build minutes used this month
Dashboard → Compute (Workers) → your Worker → Settings → Build (build history + duration per build)
Account-wide usage vs included
Manage Account → Billing → Billing & Usage
Your plan + what's metered
Compute (Workers) → Plans
Each build in the history shows its duration — that's your build-minute meter. Add up a week, multiply by your push rate, compare to 3,000. You'll almost certainly be at a few percent.
GitHub: Actions minutes, by OS
This is the page that matters most, because it's where the 10x hides:
What you want to see
Click path
Actions minutes used this month
Your avatar → Settings → Billing and licensing → Plans and usage → Usage this month → expand Actions
Minutes broken down by runner OS
Same page — Linux / Windows / macOS are listed separately, multiplier already applied
Your hard spending cap
Settings → Billing → Spending limits → Actions
When you open that GitHub usage page on a project that builds macOS, the macOS row is the one that's eating the pie. A 20-minute Mac build shows up as 200 minutes consumed. That's not a bug — it's the multiplier doing exactly what Ch 21 warned about. Which brings us to the thing neither cloud can make cheap.
The macOS Problem Neither Cloud Solves Cheaply
Here's the wall. Cloudflare's builders are Linux — they cannot build a Mac or iOS app at all (no Xcode). So for the Apple half of the project, Workers Builds isn't even an option. That leaves macOS CI, and macOS CI is expensive everywhere, because Apple only licenses macOS to run on Apple hardware. Every provider is renting you a physical Mac.
Option for macOS builds
Free tier
Then…
GitHub Actions — macOS runner
Free minutes count at 10x (2,000 → ~200 real macOS min)
~10x Linux, roughly $0.08/min
Xcode Cloud
~25 compute-hours/mo (with Developer Program)
Paid tiers (~$49.99/mo for 100 hrs, up from there)
Bitrise / Codemagic
Small free tier
Per-minute macOS pricing, similar order
Put real numbers on it. A notarized SimpleAppShipper build — archive, sign, notarize, staple, maybe run tests — is realistically 15–25 minutes of macOS time. Call it 20. At hosted macOS rates (~$0.08/min) that's about $1.60 per build. Now your push rate decides the bill:
Builds / month
macOS minutes
Approx. hosted cost / month
10 (occasional)
200
~$16
40 (a couple/workday)
800
~$64
100 (active, ~5/workday)
2,000
~$160
That ~$160/month for an active iOS dev is the number that makes people go looking for another way. There is one, and it's been sitting under your desk the whole time as an idea: stop renting a Mac by the minute. Buy one once.
Buy a Headless Mac Mini and Own Your Builds
This is the "and save" the title promised. A Mac Mini is the cheapest new Apple Silicon machine Apple sells — an M4 base model is around $599 (16 GB / 256 GB, as of 2026; check current specs). Headless means no monitor, no keyboard, no mouse — it lives on a shelf, plugged into power and ethernet, and you reach it over the network. It becomes your private, unmetered, always-on macOS build server.
The mechanism is the self-hosted runner from Ch 21. You register the Mini with GitHub Actions; GitHub keeps giving you the dashboard, the triggers, the green checkmark, the secrets — all free — but the compute runs on your Mini. No per-minute charge. No 10x multiplier. You change one line in your workflow:
…and the exact same pipeline now runs on hardware you own, for the price of electricity.
The break-even math
A Mini is a one-time cost; hosted minutes are forever. So the question is just when does the line cross. Electricity is almost a rounding error — a Mac Mini idles around 4–7 W and peaks ~30–65 W under a build, so even a few build-hours a day is ~$2–4/month.
Build volume
Hosted macOS / mo
Mini: months to break even
After that
10 builds (light)
~$16
~46 months
~$3/mo electricity
40 builds (steady)
~$64
~10 months
~$3/mo electricity
100 builds (active)
~$160
~4 months
~$3/mo electricity
Xcode Cloud 100-hr tier
~$50 flat
~13 months
~$3/mo electricity
Break-even = $599 ÷ (monthly hosted cost − ~$3 electricity). Light usage never really pays off — if you build a handful of times a month, just use the hosted free tier. The Mini wins decisively the moment you build daily.
Loading diagram…
Figure 2 — For an active iOS dev, a $599 Mini pays for itself in about four months versus hosted macOS minutes, then runs for the cost of a coffee per month. The heavier you build, the faster it crosses.
It's not just a runner — it's a whole Mac you own
The break-even table undersells it, because the Mini isn't a single-purpose runner. The same box does every other expensive-on-rented-hardware job, at no extra cost:
Notarization — xcrun notarytool submit … --wait and xcrun stapler staple, the exact release steps from this project's CLAUDE.md.
TestFlight / App Store uploads — xcrun altool --upload-app or the App Store Connect CLI, on your schedule.
The web deploy too, if you want one box — it can run npm run cf:build && npm run cf:deploy as easily as Cloudflare does (though Workers Builds is free, so you don't have to move it).
An always-on dev server — it's a quiet, low-power Mac that's on 24/7. Kick off long builds remotely, leave them running, check back from your laptop.
A rented macOS runner vanishes the second your job ends. The Mini is yours — between builds it's still a working Mac.
Setting Up the Mini Headless (No Monitor)
You need a screen and keyboard for the first boot only. After that it's fully remote. Plan on 30–40 minutes once.
First boot (one time, with a monitor): create an admin user, join Wi-Fi/ethernet, install Xcode from the App Store, then in Terminal run sudo xcodebuild -license accept and xcodebuild -runFirstLaunch.
Turn on remote access — System Settings → General → Sharing:
Remote Login (SSH) ON → gives you a terminal from your laptop: ssh admin@mini.local.
Screen Sharing ON → gives you the full desktop over VNC when you need the GUI (Xcode signing dialogs occasionally do).
Auto-login — System Settings → Users & Groups → Automatically log in as → [your admin user]. A self-hosted runner needs a logged-in GUI session for code-signing and the Simulator to work, so the Mini must reach the desktop on its own after a reboot.
Never sleep — from SSH: sudo pmset -a sleep 0 disablesleep 1. A sleeping Mini is a runner that misses jobs.
Let codesign run unattended — import your Developer ID cert into the login keychain, then authorize it for non-interactive use: security set-key-partition-list -S apple-tool:,apple: -s -k "<password>" ~/Library/Keychains/login.keychain-db. Without this, signing hangs on an invisible password prompt.
Install the runner as a service — Repo (or org) → Settings → Actions → Runners → New self-hosted runner → macOS. Run the ./config.sh … command it gives you, then make it survive reboots:
./svc.sh install./svc.sh start
Now the runner auto-starts on boot and reconnects to GitHub on its own. Unplug the monitor — you're done.
Point your workflow at it:runs-on: [self-hosted, macOS], push, and watch the job land on your Mini in the Actions tab.
The Cheapest Blend
Don't make this all-or-nothing. The cheapest real-world setup mixes the tools, sending each job to whatever runs it for free or near-free:
Loading diagram…
Figure 3 — The blend that costs ~$3/month all-in: free Workers Builds ships the website, the owned Mac Mini does the expensive Apple builds and notarization, and GitHub Actions on Linux runs the optional PR gate within its free tier. Nobody pays the 10x macOS tax, and nobody writes a deploy YAML they don't need.
Note what you don't move: the web deploy stays on Workers Builds because it's already free and zero-maintenance. You only buy hardware to kill the expensive meter — the macOS one. Moving cheap, working things onto your Mini just to "consolidate" is effort without savings.
What simpleappshipper.com Actually Does
Keeping it honest, same as Chs 15 and 21 did: today this project's deploys are run by hand from a laptop — npm run cf:build && npm run cf:deploy for the site, and the notarize-and-staple sequence from CLAUDE.md for the Mac app. Cost: $0, because the developer, deployer, and tester are one person.
The graduation path is exactly this chapter. The day the web deploy should be automatic, it goes to Workers Builds (free, three fields). The day the Mac release build should be automatic — or the day building it by hand on the main laptop gets annoying enough — it goes to a headless Mac Mini self-hosted runner, which builds, notarizes, and uploads while the laptop stays free for actual work. Neither step adds a hosted-CI bill. That's the point: scaling up the automation here doesn't mean scaling up the spend.
Mental Model — Three Sentences
Workers Builds is the free, no-YAML way to ship the web half of a project, but it runs only on Linux — so it physically cannot build the Apple half.
The Apple half is expensive on every cloud because macOS only runs on Apple hardware, and GitHub's hosted macOS runner bills at 10x — which is the one line item worth engineering around.
A ~$599 headless Mac Mini registered as a self-hosted runner turns that metered macOS cost into a flat ~$3/month of electricity, pays for itself in about four months of active building, and doubles as your notarization and upload box forever after.
Try It Yourself (15 Minutes)
Read your own bills. Open GitHub → Settings → Billing → Plans and usage → Usage this month and look at the Actions breakdown by OS. Then open Cloudflare → Manage Account → Billing & Usage. For most readers both are near zero — that's the point, and now you know where to check.
Cap the downside. In GitHub → Settings → Billing → Spending limits → Actions, confirm the limit is $0 (or a number you chose). You just made a surprise bill impossible.
Wire Workers Builds. If you have a Cloudflare Worker, open it → Settings → Build, connect the repo, set build/deploy commands, and push. Watch it deploy with no YAML and no secret.
Do the math for your push rate. Estimate your macOS builds/month × ~20 min × ~$0.08/min. Divide $599 by that (minus ~$3). If the answer is under a year, a Mini will save you money — and if you build daily, it's not close.
Where This Lands in the Series
This closes the operational-cost arc the series has been building since Ch 15. You can now write a CI pipeline (Ch 15), decide whether you even need hosted CI (Ch 21), read the exact pages where both Cloudflare and GitHub keep the bill, cap that bill so it can't surprise you, and — when Apple builds make the cloud meter spin — replace it with a box you own that pays for itself in a season. The whole "ship it reliably without a surprise invoice" story now has real numbers under it.
The macOS-build economics here are the web-series view of a question the Ship iOS series takes on directly: it goes deeper on code signing (the genuinely hard part — harder than the build), Xcode Cloud vs Bitrise vs Codemagic vs fastlane, and the four ways to build and push to TestFlight from your own Mac for $0. If this chapter convinced you the Mini is worth it, that's the chapter on what to run on it.
Before Part 3, one loose thread from this whole arc is worth pulling on its own. We've talked about running the checklist on a server, on a cheap Mini, on your host — but the fastest, cheapest place of all is the laptop already in front of you. Ch 25 makes that the default: what ci:local actually means, the honest pros and cons of running checks on your own machine, every place you can run them (local and online), and the one rule that keeps the two from ever disagreeing. Then Part 3 opens the modern-frontend track — why almost every team in 2026 reaches for a framework like React or Next.js, and what it solves.
When you're ready to publish your Swift app to the App Store, Simple App Shipper handles metadata, screenshots, TestFlight, and submissions — all in one place.