Three of my websites are live on the public internet right now, and none of them are hosted anywhere. There's no VPS, no Vercel project, no cloud box with a monthly bill. All three are served by a single Mac Studio sitting in my room, exposed to the world through one Cloudflare tunnel. Here's the whole architecture, because it's simpler than the cloud version and it costs nothing.
One codebase, three tenants
The first decision that makes this work: the three domains are not three codebases. They're one multi-tenant Next.js app that I run three times on three different ports.
The same repository boots as three separate processes — one on :3001, one on :3002, one on :3003 — and each process serves a different brand. The application reads which host it's being asked for and renders that tenant. Sharing one codebase across all three means a fix to the blog renderer, the SEO scaffolding, or the layout ships to every site the moment I rebuild. I am not maintaining three near-identical projects that drift apart over time. I'm maintaining one, wearing three faces.
That's a deliberate tradeoff. Multi-tenant means a bad deploy can take down all three at once, so the build has to be clean before it ships. But the maintenance savings are enormous — three brands, one place to fix a bug, one dependency tree to keep patched.
launchd owns the processes
Each of those three Next.js processes is a launchd job. Not a terminal I left open, not a screen session, not pm2 — a proper macOS service with a plist, RunAtLoad, and KeepAlive.
That distinction is the difference between a hobby and infrastructure. If a process crashes, launchd respawns it. If the Mac reboots, all three sites come straight back up with no manual step from me. I don't wake up to a dead site because a terminal closed. The operating system is the supervisor, and the operating system doesn't forget.
I lean on this same discipline everywhere in my stack — the platform's own init system keeps services alive rather than a third-party process manager stacked on top. On a Mac that means launchd, full stop. It already ships built for exactly this job, so I use it instead of adding a moving part I'd have to babysit.
The tunnel is the only thing facing the internet
Here's the piece people expect to be hard and isn't. My home network has no open inbound ports. My router isn't forwarding anything. You cannot reach the Mac Studio directly, and that's the point.
Instead I run a single Cloudflare tunnel. It makes an outbound connection from the Mac to Cloudflare's edge and holds it open. Public traffic hits Cloudflare, and Cloudflare pushes it back down through that already-established tunnel to my machine. Nothing on my end is listening for the public internet to knock. There's no port to scan, no inbound firewall rule, no exposed IP. The attack surface of "a server on my home network" essentially disappears, because from the outside there is no server — there's a tunnel that only my machine dialed out to open.
One tunnel handles all three domains. The tunnel's config maps each hostname — apex domains and their subdomains — to the right local port. domain-one.com routes to :3001, domain-two.com to :3002, domain-three.com to :3003, and any subdomains slot in the same way. Cloudflare also terminates TLS at its edge, so every site gets a valid certificate without me ever running certbot or renewing anything by hand.
Why a Mac Studio is a legitimate server
The instinct is that "real" hosting has to live in a datacenter. For my traffic profile, that instinct is wrong, and the Mac Studio is genuinely the right machine.
Apple Silicon's unified memory and idle power draw make it a quiet, capable always-on server. It sips power sitting there serving pages, it doesn't need a rack or a cooling plan, and it's the same machine that runs the rest of my local stack. Co-locating the web fleet with everything else it talks to means those calls happen over localhost or the LAN instead of crossing the public internet to a cloud box and back. Lower latency, and no data leaving the building unless it has to.
The honest limitation: this is not a build for a viral spike to a million concurrent users. If one of these sites blew up overnight, a single home machine would be the ceiling. But that's a problem I'd love to have, and Cloudflare's edge caching absorbs a surprising amount of read-heavy traffic before anything ever reaches the Mac. For three brand sites with real but human-scale traffic, one Mac Studio behind a cached edge is not a compromise — it's the correct amount of machine.
What this buys me
Add it up and the pattern is: one codebase, three launchd-supervised processes, one outbound tunnel, zero hosting bill.
I own the whole path from source to served page. When I want to change something, I edit the repo, rebuild, and launchd is already running the new code. When I want a fourth site, it's a fourth port and one more line in the tunnel config — not a new hosting account. Nothing about my sites lives on infrastructure I rent and can be cut off from. The domains point at Cloudflare, Cloudflare points at a tunnel, and the tunnel points at a machine I can reach out and touch. That's the entire stack, and it's more resilient and far cheaper than the cloud version it replaced.
FAQ
Can you really run production websites from a home Mac?
Yes. A Mac Studio on Apple Silicon is a quiet, low-power, always-on machine that serves web traffic comfortably at human scale. The limitation is peak concurrency — a single home box isn't built for a million-user spike — but Cloudflare's edge caching absorbs most read-heavy traffic before it reaches the Mac, so for brand sites with real traffic it's genuinely sufficient.
How do three domains run from one codebase?
It's a multi-tenant Next.js app run as three separate processes on three ports. Each process serves a different brand, and the app renders the right tenant based on the host it's asked for. One repository means a bug fix or feature ships to all three sites on the next rebuild, instead of maintaining three drifting copies.
Why use a Cloudflare tunnel instead of opening a port?
The tunnel makes an outbound connection from the Mac to Cloudflare and holds it open, so there are no inbound ports, no router forwarding, and no exposed IP on the home network. Public traffic reaches Cloudflare and is pushed back down the existing tunnel. There's nothing to port-scan, and Cloudflare terminates TLS at its edge so certificates are automatic.
Why launchd instead of pm2 or Docker?
launchd is macOS's native init system, built to keep services alive and restart them on boot. Each Next.js process is a launchd job with KeepAlive and RunAtLoad, so crashes respawn and reboots bring everything back with no manual step. Using the platform's own supervisor means one fewer moving part than bolting on a third-party process manager.