A pattern is shipping production websites today that would have sounded ridiculous in 2020. A single Apple Silicon machine, sitting in a residential Las Vegas office, serving traffic to the public internet for three brand domains, behind one Cloudflare tunnel.
It works. It works well enough that I have stopped paying for the cloud server that used to do this job. The remaining trade-offs are real but small, and the upside is the kind of operational independence that's hard to overstate.
This is the pattern, what it actually does, and where the gotchas are.
The Setup In Plain Terms
One Mac runs three local Next.js processes on three different ports. Each process serves one of three brand sites. A single Cloudflare tunnel — created with cloudflared, named once, started once — connects to Cloudflare's edge and accepts inbound traffic for all six hostnames (three apex domains, three www. variants).
When traffic arrives at https://hellcatblondie.io, Cloudflare's edge terminates TLS, looks at the Host header, matches it against the tunnel's ingress rules, and forwards the request to http://localhost:3001 on the Mac. The Next.js process responds. The response goes back through the tunnel, out the edge, to the user.
That is the entire architecture. There is no reverse proxy on the Mac. There is no Docker, no Kubernetes, no nginx config to babysit. There is one daemon (cloudflared) running as a launchd service, one config file with the routing rules, and three Next.js processes.
The Config File
The whole routing logic lives in a single YAML file under ~/.cloudflared/config.yml:
tunnel: <tunnel-id>
credentials-file: /Users/dajaistewart/.cloudflared/<tunnel-id>.json
ingress:
- hostname: dajai.io
service: http://localhost:3002
- hostname: hellcatblondie.io
service: http://localhost:3001
- hostname: sovereignagiasi.com
service: http://localhost:3003
- service: http_status:404
That is the entire config. Adding a fourth domain is two more lines. Moving a domain to a different port is a single edit and a daemon reload.
Compare that to nginx with three server blocks, three SSL cert renewals, and a separate process supervisor. The Cloudflare tunnel pattern collapses the entire reverse-proxy surface into a config file.
What Cloudflare Does That You Don't Have To
A short list of things you don't have to set up:
-
TLS certificates. Cloudflare terminates TLS at the edge using its own certs. You never touch a cert renewal. Local services talk plain HTTP to the tunnel; the public internet sees HTTPS.
-
DDoS protection. Cloudflare's edge absorbs attack traffic before it reaches the tunnel, which means before it reaches your Mac. You don't have to think about it.
-
CDN. Static assets are cached at the edge globally. The Mac in Las Vegas only serves cache misses and dynamic pages. A page-cached blog post served from Cloudflare's Tokyo POP feels native to a user in Tokyo even though the origin is on a desk in the desert.
-
WAF rules. Cloudflare's free tier ships with sensible default WAF rules. You can layer custom rules on top via the dashboard.
-
Health checking. The tunnel daemon detects when the local service is down and Cloudflare returns a friendly error page instead of a connection failure.
You are not running a server. You are running an application, and Cloudflare is running the server for you.
The Trade-Offs
Honesty section. There are real trade-offs.
Cloudflare is a dependency. Sovereign-purist would say a Cloudflare-fronted local Mac is not sovereign. They would be partially right. You depend on Cloudflare for TLS, edge routing, and CDN. Cloudflare can change pricing. Cloudflare can decide they don't like your content. The mitigation is that the application code, the data, and the runtime all live on hardware you own — so a Cloudflare problem is a routing problem, solvable in a few hours by fronting with a different CDN or going direct.
Latency is bounded by your residential connection. Cloudflare's edge is fast. Your home upload bandwidth is probably not. For a blog or a marketing site, this doesn't matter — Cloudflare caches almost everything. For a real-time API serving sustained traffic, it matters. Know what your application actually needs.
Cold starts. If your Mac sleeps, the tunnel disconnects. Disable sleep on the host machine. (pmset -a sleep 0 on macOS.) Or run the host on UPS-backed power and accept the occasional sleep window. This is a one-time setup gotcha that takes ten minutes to fix and never comes back.
Process supervision. The tunnel restarts itself, but the Next.js processes don't. You need launchd plists with KeepAlive=true to make sure the local services stay alive. This is a configuration cost, not a runtime cost. Once it is set up it just runs.
The Per-Host Routing Trick
The pattern's killer feature is one binary, three brands. The three Next.js processes I run are actually the same Next.js binary, started three times with different PORT env vars. Each process loads the same code, but middleware in the application reads the Host header and serves brand-appropriate content per request.
That means a code change to fix a bug on hellcatblondie.io is, by definition, also fixing the same bug on dajai.io and sovereignagiasi.com. There is no per-brand fork. There is one application that knows about three brands.
This pattern is what makes the Mac-as-production-server feasible at the brand-count we run. If each brand needed its own separate codebase and deploy pipeline, the operational cost would explode. With shared code and host-aware behavior, three brands cost barely more than one.
When This Pattern Doesn't Work
Be honest with yourself about the workload.
If your site needs to handle thousands of requests per second, host it somewhere with thousands of CPUs. The Mac is good for hundreds of concurrent users, easily. It is not good for tens of thousands.
If your site needs strong uptime guarantees with PagerDuty escalation paths, host it where the SLAs are. The Mac on your desk has no SLA. It has whatever uptime you give it through good launchd configs and a stable home internet connection.
If you do not own the hardware, this pattern is not available to you. Renting a Mac in a colo is technically possible but defeats the purpose. The whole point is that the hardware is yours.
What This Pattern Buys You
Three things, all of them more valuable than the cost savings.
Total deploy control. A code change is a git push to a local repo, a npm run build, and a launchd kickstart. End to end, sub-60 seconds. No CI pipeline. No staging environment. No deploy tooling. The whole feedback loop is local.
Cost stability. The Cloudflare tunnel is free. The Mac is a one-time hardware cost. The electricity is residential rates. There is no monthly cloud bill that grows when traffic grows. The cost curve is flat.
Sovereign defaults. You own the hardware. You own the application. You own the data. You depend on Cloudflare for one specific thing (edge routing) which is replaceable in a single config change if you ever need to. The dependency surface is the smallest it can be while still being publicly reachable.
That sovereignty is the actual product. The cost savings are a side effect.
The Pattern, Distilled
If you want to run a small portfolio of production websites from your own hardware in 2026, the pattern is:
- One Mac, sleep disabled, on stable power.
- One Cloudflare tunnel, configured via a single YAML file with per-hostname ingress rules.
- One Next.js application (or whatever framework), started multiple times under launchd with different ports and different env vars per brand.
- Host-aware middleware in the application so a single codebase can serve multiple brands per request.
- Launchd plists with
KeepAlive=truefor every long-running process so the Mac auto-recovers from any process crash.
That is the entire stack. It is small enough to fit in a single afternoon's work to set up, and small enough to fit in a single brain to maintain.
Most of the cloud-native infrastructure people pay for is solving problems most projects do not actually have. This pattern gives you the things you do need (HTTPS, CDN, DDoS protection, public reachability) and skips the things you don't (orchestration, Docker registries, deploy pipelines, multi-region replication).
It is not the right pattern for every project. It is the right pattern for more projects than the cloud-native crowd would tell you.
FAQ
What if my Mac dies?
Restore from a Time Machine backup onto a new Mac, point the cloudflared tunnel to it, and you are live again. The tunnel is bound to credentials, not to a specific machine. Recovery is hours, not days, if you have backups in place.
Can I run this on a Linux box instead?
Yes. The same pattern works with any host that can run cloudflared. The reason I run it on Mac is the same reason I run my AI inference on Mac — Apple Silicon is the right desktop-computer trade-off in 2026 for power, quietness, and tooling quality. A Linux box in a closet is the right answer if you want maximum performance per dollar.
How do I monitor it?
Cloudflare's dashboard shows tunnel uptime and traffic per hostname. For application-level monitoring, log to a file with pino or any log library, tail the logs, and pipe critical errors to a notification channel of your choice. You don't need observability infrastructure for a small portfolio.
What about backups?
The Mac is on Time Machine. The application code is in git, pushed to a remote regularly. The user-generated data (blog posts, customer records) is in a SQLite file that gets snapshotted to a separate disk hourly. Three layers of backup. Recovery time is bounded.