< Back to Blog

Splitting Docker Across Mac + Synology NAS Without Changing DNS

How I moved six production services from a RAM-constrained Mac Mini to a Synology NAS using Cloudflare tunnel ingress-flip, zero DNS changes, and one AVX2 segfault that almost killed the migration.

Splitting Docker Across Mac + Synology NAS Without Changing DNS

Last week I posted about running production Docker apps through Cloudflare Tunnels with no open ports and no static IP. That setup works great until you run out of RAM on the Mac hosting everything.

My Mac Mini was sitting at 14.5 GB of 16 GB used. Twenty, n8n, Outline, Listmonk, Documenso, InvoiceNinja — every service fighting for the same memory. I needed to split the stack across two machines without touching DNS, without changing ports, and without my wife asking why the internet was down.

The answer: Cloudflare tunnel ingress-flip.

The Pattern: Ingress-Flip Migration

Cloudflare tunnels don't bind to a specific machine. They bind to tunnel connectors — lightweight daemons that can run anywhere. When you define an ingress rule connecting a public hostname to a local service, that localhost resolves on whatever machine the connector is running on.

So the migration pattern is:

  1. Spin up a tunnel connector on the Synology NAS
  2. Move a Docker stack to the NAS
  3. Change the ingress rule from localhost:8080 to 192.168.1.50:8080 (or wherever the service now lives)
  4. Done. Zero DNS changes. Zero port forwarding. The public URL never moves.

The Migration in Practice

In one session I moved six services — Documenso, InvoiceNinja, Outline's Redis, Listmonk, and two n8n workers — from the Mac Mini to the Synology. The Mac dropped from 14.5 GB to 9.1 GB. No downtime anyone would notice.

The docker-compose stacks look nearly identical on both machines. The only real difference is the cloudflared tunnel connector running on each host, pointing at the same tunnel UUID but resolving local services differently based on which machine they live on.

Here's the core of the Synology stack:

The Cloudflare tunnel connector on the NAS picks up the new ingress rules and routes traffic to the services now running locally. The public hostname never changes — it was always pointing at the tunnel, not at any specific machine.

The Real Story: The AVX2 Segfault

Most infrastructure content tells you how things are supposed to work. The useful stuff tells you what broke and how you fixed it. Splitting services across hosts is straightforward on paper and full of invisible architecture traps in practice.

The Synology DS920+ uses an Intel Celeron J4125 — Apollo Lake architecture. Documenso's Docker image ships binaries compiled with AVX2 instructions. Apollo Lake doesn't support AVX2. The container started, then immediately segfaulted with no useful error.

This is the kind of problem self-hosters hit constantly and nobody writes about because it's boring to debug.

The Fix: Build From Source Targeting x86-64-v2

The solution required rebuilding the binary from source targeting x86-64-v2, packaging it as a custom Docker layer, and swapping it in.

Here's the Dockerfile override I used:

The key is the CFLAGS and CXXFLAGS environment variables. Setting -march=x86-64-v2 ensures the compiler generates instructions compatible with Apollo Lake's limited instruction set, avoiding AVX2 entirely while still using newer instruction sets that Celeron J4125 does support (SSE4.2, POPCNT, etc.).

Took 90 minutes to debug and fix. The migration itself took 20.

Why This Matters for Self-Hosters

If you're running self-hosted services on a Synology or an old mini PC, you're going to hit the AVX2 wall eventually. Build from source and target your actual CPU architecture. It is the only reliable way.

The broader lesson: when you split services across hosts, the failure modes aren't in the networking — they're in the binaries. Docker images are almost always built for generic x86-64 or newer architectures. Your hardware may not match. Always check CPU instruction support before assuming a container will just work.

The Memory Trade-off

After the migration, the Mac Mini dropped from 14.5 GB to 9.1 GB usage. The Synology DS920+ (with 8 GB RAM) handles the lighter services comfortably. The key insight: not all services need the same resources. Database-heavy workloads like PostgreSQL benefit from being on the machine with more RAM, while stateless services can live anywhere.

I left n8n and TwentyCRM on the Mac Mini since they're memory-intensive during workflow execution. Outline, Listmonk, Documenso, and InvoiceNinja moved to the NAS — they run fine with less RAM and were the biggest consumers of the Mac's 16 GB.


This post is part of my Systems Architecture pillar. I write about self-hosting, automation, and infrastructure that actually works in practice.