Cluster-A ๐ in cloud (Hetzner control plane) + workers across 3+ home sites ๐ก๐๐ over VPN
- Cluster-B ๐งฑ local cluster at your main home
- Dynamic DNS via Joker.com ๐ to keep each home site reachable
- CloudNativePG ๐ with:
- Primary DB at your home site (Cluster-A)
- Async replicas at Site-2 & Site-3
- Topology-aware reads ๐ (local reads)
- Write routing to primary โ๏ธ
- Local services per home site ๐งฉ
๐ High-Level Architecture
Hetzner Cloud โ๏ธ
โโโโโโโโโโโโโโโโโโโโโโโโโ
โ k3s control-plane ๐ง โ
โโโโโโโโโฌโโโโโโโโโโโโโโโโ
โ Tailscale/WG VPN ๐
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Multi-Site WAN Mesh
๐ก Home Site (Primary DB)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ k3s worker nodes โ
โ Longhorn storage ๐พ โ
โ CNPG primary ๐ โ
โ DDNS updater โป โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ Site-2
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ k3s worker nodes โ
โ CNPG async replica ๐ โ
โ DDNS updater โป โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ Site-3
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ k3s worker nodes โ
โ CNPG async replica ๐ โ
โ DDNS updater โป โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
๐ก Local-only Cluster-B
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ k3s server + workers โ
โ Local apps only ๐ช โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Core Principles
| Feature | Design |
|---|---|
| Cluster-A | Distributed across sites via VPN |
| Cluster-B | Local to main home |
| Networking | Tailscale or WireGuard |
| DNS | Joker.com Dynamic DNS |
| DB | CNPG primary @ home + async replicas at other sites |
| Storage | Longhorn per site only (no cross-WAN replication) |
| Routing | Writes โ primary; Reads โ local |
๐๏ธ Step-by-Step Setup
1๏ธโฃ VPN Everywhere (Tailscale recommended)
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --auth-key <KEY> --hostname <NODE_NAME>
ip -br a | grep tailscale0
Use the Tailscale IP (
100.x.x.x) for all k3s--node-ipvalues.
2๏ธโฃ Joker.com Dynamic DNS ๐
For each site create DNS entries like:
home.example.comsite2.example.comsite3.example.com
Enable Dynamic DNS in Joker control panel (per host).
๐ Site updater (ddclient method)
sudo apt install -y ddclient
/etc/ddclient.conf per site:
daemon=300
use=web, web=svc.joker.com/nic/checkip
protocol=dyndns2
server=svc.joker.com/nic/update?
ssl=yes
login=YOUR_DDNS_USER
password=YOUR_DDNS_PASS
host=home.example.com
Enable:
sudo systemctl enable --now ddclient
3๏ธโฃ Cluster-A: Cloud control plane โ๏ธ
On the Hetzner VM:
TS_IP_CP=<tailscale ip>
curl -sfL https://get.k3s.io | \
INSTALL_K3S_EXEC="--node-ip ${TS_IP_CP} --disable servicelb --disable local-storage" \
sh -
kubectl taint nodes $(hostname) node-role.kubernetes.io/control-plane=true:NoSchedule
4๏ธโฃ Join Worker Nodes at Each Site ๐ก๐๐
On each worker:
TS_IP_NODE=<tailscale-ip>
K3S_URL=https://<tailscale-ip-hetzner>:6443
K3S_TOKEN=$(ssh root@<hetzner> "sudo cat /var/lib/rancher/k3s/server/node-token")
curl -sfL https://get.k3s.io | \
K3S_URL=$K3S_URL K3S_TOKEN=$K3S_TOKEN \
INSTALL_K3S_EXEC="--node-ip ${TS_IP_NODE}" sh -
Label nodes:
kubectl label node node-home-1 site=home topology.kubernetes.io/zone=home
kubectl label node node-site2-1 site=site2 topology.kubernetes.io/zone=site2
kubectl label node node-site3-1 site=site3 topology.kubernetes.io/zone=site3
5๏ธโฃ Install MetalLB at Each Site ๐ฏ
Give each site its own LAN range.
Example:
# home LAN LB range
addresses: ["192.168.10.240-192.168.10.250"]
Repeat for site2/site3.
6๏ธโฃ Install Longhorn ๐ (per site)
kubectl create ns longhorn-system
kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/v1.6.2/deploy/longhorn.yaml
Set replicas only within that site via UI or StorageClass override.
7๏ธโฃ CloudNativePG Setup ๐
๐ก Primary at Home Site
Apply primary manifest (truncated):
spec:
instances: 2
storage: { size: 200Gi, storageClass: longhorn }
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: site
operator: In
values: ["home"]
๐ & ๐ Replica Clusters (async)
Each replica points to home RW service and stays pinned locally.
8๏ธโฃ Topology-Aware Reads ๐
Patch read-only services:
kubectl -n data annotate svc appdb-home-ro service.kubernetes.io/topology-mode=auto
kubectl -n data annotate svc appdb-site2-ro service.kubernetes.io/topology-mode=auto
kubectl -n data annotate svc appdb-site3-ro service.kubernetes.io/topology-mode=auto
9๏ธโฃ App DSNs ๐ฏ
Environment variables:
WRITE_DSN=postgres://...@appdb-home-rw...
READ_DSN=postgres://...@appdb-siteX-ro...
Your application router decides read vs write.
๐ Local-Only Cluster-B at Home ๐งฑ
Install k3s normally.
Optional: run your own CNPG here too.
๐ At This Point You Have:
โ
Global multi-site k3s mesh
โ
Local-only cluster for homelab apps
โ
Joker DDNS for site discovery
โ
VPN-backed private cluster network
โ
CNPG primary + async replicas
โ
Local read affinity
โ
Cloud ingress + local per-site ingress
๐ก Tips
| Need | Solution |
|---|---|
| Local-only site services | MetalLB w/ site pools |
| Public HTTPS | Traefik + cert-manager |
| Zero trust | Tailscale ACLs |
| DR | Promote CNPG replica |
| GitOps | Flux across clusters |