ENTH
PART 2 · Installation

Hardware and network: Tailscale + Cloudflare Tunnel

Hardware and network: Tailscale + Cloudflare Tunnel

TL;DR — the 30-second summarytldr

The brain of the system is a Mac Mini M4 Pro with 48GB of memory (14-core CPU, 20-core GPU, unified memory) — the sweet spot: it runs 27B-class models comfortably with embeddings loaded alongside at all times, never touching swap

The iron rule of 48GB: only one big model resident at a time — not two, no matter what

Before installing anything at all, prepare the system's "blood vessels" first — the private network Tailscale (mesh VPN) + the door to the internet Cloudflare Tunnel The idea is simple: every machine talks through a secret tunnel, and opens to the outside world through a single door with a guard checking keys — no ports opened on the router at all

When to usewhen to use

What do you actually need?

PieceRequired?Notes
Mac Mini M4 Pro 48GBRequired (the core)48GB is the sweet spot — 36GB works if you accept context cut to ≤16k, 24GB is not enough
Small VPSRequired (the storefront)4 vCPU / 8GB is enough — ~$10–15/month
NVIDIA GPU machineOptional (image/audio)our i9 + 2× RTX 5060 Ti 16GB is hardware we already had — you can start Mac-only
NAS / always-on Linux machineOptional (automation)a Synology DS725+ running n8n + monitoring
Power-metering smart plugRecommendedto collect your own electricity receipt — ~300 baht each

Do this step as soon as you have 2 machines or more — and it must happen before the VPS install in chapter 3, because compose will call the Mac over its Tailscale IP

  • Tailscale use case: machines in the house talking to each other (VPS -> Mac, NAS -> i9), both on the LAN and from outside — you get a safe IP that looks like 100.x.y.z
  • Cloudflare Tunnel use case: exposing services for outsiders to call over HTTPS (the web chat, the bot API) without anyone seeing our home IP
  • These two work together; they do not replace each other

How-tohow to

The real memory map on our machine (a 27B model in nvfp4 + embeddings):

48 GB total
- 23 GB   main 27B model nvfp4 (context 32768) — resident is bigger than the 18GB file
          because KV cache + runtime buffers live in the same unified memory
- ~1 GB   bge-m3 embeddings (+ nomic), small, kept warm
- ~8 GB   macOS + Ollama runtime + headroom
= ~16 GB left -> memory pressure green, swap = 0

If you respect the "one model at a time" rule, this table stays calm forever

The swap incident that taught us the hard way — once we kept a second (16GB) model resident with a 24-hour keep-alive. The result:

  • memory jumped to 47.7GB + 3.8GB of swap
  • TTFT (time to first character) ballooned from ~0.04 seconds to 28–60 seconds
  • after evicting the second model (keep_alive: 0), at the 27-minute mark swapouts stopped dead

Since that day the whole kit has guardrails wired in: a 24-hour KEEP_ALIVE for one model only, health checks forbidden from sweeping the model back in, and a cron that warms only the main model

if you get stuck anywhere → chapter 9

Step 1 — sign up and install Tailscale on every machine (free for personal use):

  1. Create an account at tailscale.com (Google/GitHub login works)
  2. Install on the Mac: brew install --cask tailscale then log in
  3. Install on the VPS (Debian/Ubuntu): the curl command from tailscale.com/download, then tailscale up
  4. Install on Windows/others as usual, then approve every machine in the admin console
  5. Check that everyone sees each other: tailscale status

Here is the real output from a machine in our fleet (some IP digits hidden for safety):

Machines on the network (real example, some digits hidden) · tailscale status
100.x.y.z desktop windows -100.x.y.z vps-bomllm linux -100.x.y.z nas-ds725 linux -100.x.y.z macmini-ai macOS -# every machine in one mesh = they can talk immediately, no ports needed

Your machines will get their own name+IP — write down the Mac's IP for chapter 3

Step 2 — set up Cloudflare Tunnel for 2 domain names (chat and llm):

  1. Your domain's DNS must be hosted on Cloudflare first (the free plan is enough)
  2. Install cloudflared on the VPS: follow the guide at developers.cloudflare.com/cloudflare-one/connections/connect-networks/
  3. Create the tunnel and point 2 routes: chat.yourdomain.com → localhost:3000 and llm.yourdomain.com → localhost:4000
  4. Enable the Cloudflare Access second gate (email OTP login) for admin pages — see the real screenshot below

if you get stuck anywhere → chapter 9

Config exampleconfig example

Speed actually measured on this machine (the benchmark commands live in benchmarks/ in the repo):

MetricGGUF Q4_K_MMLX nvfp4Difference
Decode speed (5-prompt average)7.85 tok/s30.23 tok/s+285%
TTFT (warm)0.288 s0.040 s7x faster
RAM used18 GB23 GB+5 GB (traded for speed — worth it)
Swap00—

Why MLX is faster: the M4 Pro has ~273 GB/s of memory bandwidth — LLM inference is constantly reading model weights, so the faster the reads, the faster the answers (a regular M4 at 120 GB/s gets roughly half these numbers). See MLX in the glossary

The configuration principle to remember through the whole manual:

# BOMLLM's iron rules for the network
1. every service binds 127.0.0.1 only (except the Tailscale interface)
2. the public has exactly one way in = Cloudflare Tunnel
3. the Mac exposes Ollama to the Tailscale IP only (pf firewall open)
4. SSH uses a non-standard port + key only + ufw DROP by default

Source: docs/security.md — re-checked on every audit round

Pro tip — lessons from the fieldpro tip

★ PRO TIP
Skip the in-machine SSD upgrade pricing — keep the models on an external Thunderbolt SSD and point the OLLAMA_MODELS variable at that volume — saves several thousand baht versus an internal SSD upgrade, and you can move it to another machine
★ PRO TIP
What fits and what does not on 48GB — 27B + embeddings = ✅ daily use | 27B + a 7B vision model = ⚠️ tight, watch the pressure | two 27B models = ❌ swap thrash (been there) | 32B mxfp8 = ❓ loads but leaves no breathing room | 70B in any quant = ❌ do not even think about it
★ PROD LESSON
The Cloudflare Access double gate — all admin pages (like the LiteLLM control panel) get wrapped in another layer of Cloudflare Access — you must pass an email OTP before you even see the original login page, meaning even if the software has a vulnerability the outside world still cannot reach it
★ PRO TIP
Tailscale does not slow you down — latency through Tailscale within one country is ~a few ms — the full LLM signal path (user → VPS → Mac) is still clearly faster than calling an overseas cloud (p50 5.2 s vs 35.1 s in the 50-case test)

FAQfaq

Q: Can I use a Windows PC + GPU instead of a Mac? — it can run LLMs, but a single 16GB card cannot hold a 27B model; you must shrink the model and trade away quality — we mostly use the PC as the "image/audio kitchen" instead (chapter 6)

Q: If the Mac goes down, do the bots die? — no. Tier 2 on the VPS takes over immediately (chapter 1)

For other questions see the FAQ

Q: Is Tailscale really free, and does data leak? — the personal plan is free for 100 devices, the connector is open source, and traffic is WireGuard-encrypted end to end — Tailscale's servers only handle the "matchmaking"; they never read your data

Q: No domain of your own? — a .com domain costs ~300–400 baht a year and Cloudflare's free plan is enough for the tunnel — the best-value spend in the whole system

See alsosee also

Figuresfigures

The Cloudflare Access verification page shown before the real system's admin pages
A real screenshot from the production system — unauthorized visitors see only this and can go no further
Diagram showing the Tailscale and Cloudflare Tunnel lines
Solid lines = the mesh VPN; lines coming in from the internet = through the single tunnel door

Real screen recording for this lesson · click play to hear the summary · not investment advice

▶ Watch the manual video: https://youtu.be/O4hM39cgDCY