flowchart TB
subgraph PUBLIC["Public access"]
direction LR
WEB["Browser and API clients"]
MEDIA["Direct media clients"]
DNS["Cloudflare DNS-only LB
returns healthy edge addresses"]
CF["Cloudflare proxied LB
shared pool, equal weights
session affinity"]
PROBE["Cloudflare HTTPS monitor
ingress liveness only
source allowlist + monitor token"]
WEB --> CF
MEDIA -. "DNS lookup" .-> DNS
end
subgraph EDGES["Dedicated entry layer"]
direction LR
subgraph EDGEA["edge-a"]
direction TB
HA["HAProxy HTTPS ingress
validate Host and source
rebuild trusted client headers
normalize paths and apply access rules"]
SELECTA["Service backend
prefer local path
peer as backup + routing cookie"]
GWA["Private service gateway
overlay listener, trusted edges only
local Anubis AND origin must be healthy"]
ANA["Per-service Anubis on loopback
browser challenge or client-compatible policy
application authentication still required"]
HA --> SELECTA
SELECTA -->|"primary"| GWA
GWA --> ANA
end
subgraph EDGEB["edge-b"]
direction TB
HB["HAProxy HTTPS ingress
validate Host and source
rebuild trusted client headers
normalize paths and apply access rules"]
SELECTB["Service backend
prefer local path
peer as backup + routing cookie"]
GWB["Private service gateway
overlay listener, trusted edges only
local Anubis AND origin must be healthy"]
ANB["Per-service Anubis on loopback
browser challenge or client-compatible policy
application authentication still required"]
HB --> SELECTB
SELECTB -->|"primary"| GWB
GWB --> ANB
end
SELECTA -. "per-service backup over encrypted overlay" .-> GWB
SELECTB -. "per-service backup over encrypted overlay" .-> GWA
end
CF -->|"HTTPS to a healthy endpoint"| HA
CF -->|"HTTPS to a healthy endpoint"| HB
MEDIA -->|"direct HTTPS after DNS selection"| HA
MEDIA -->|"direct HTTPS after DNS selection"| HB
PROBE -. "GET /_edge/entry-health: edge-ready" .-> HA
PROBE -. "GET /_edge/entry-health: edge-ready" .-> HB
MESH["Encrypted Tailscale data plane
private routes and least-privilege access
Headscale is the control plane, not an HTTP transit hop"]
ANA --> MESH
ANB --> MESH
subgraph ORIGINS["Shared private origins"]
direction LR
VPS["Private application hosts
applications bound to private listeners"]
ROUTER["Home subnet router
approved route to HTTPS gateway"]
FW["Home firewall
allow HTTPS to gateway"]
NPM["Nginx Proxy Manager
private HTTPS with valid origin certificate"]
APPS["Home applications
native accounts, sessions and tokens"]
ROUTER --> FW --> NPM --> APPS
end
MESH --> VPS
MESH --> ROUTER
subgraph OPS["Deployment and control"]
direction LR
GIT["Git repository
service catalogue + generic templates
no credentials in source"]
PLAN["Semaphore plan task
local inventory, no deployment secrets
validate catalogue + derived rules"]
DEPLOY["Semaphore deployment task
SSH + sudo, serial rollout
validate candidate with selected HAProxy image
check local AND peer paths"]
SECRETS["Separate secret values
DNS API / Anubis signing
monitor / metrics"]
HS["Headscale control host
validate and back up policy candidate
preserve unrelated rules, then reload"]
CERT["Certbot on each edge
DNS-01 certificate issuance and renewal
validate certificate/key and reload HAProxy"]
CFAPI["Cloudflare DNS API
certificate challenge + dedicated edge records
application LB publication managed separately"]
GIT --> PLAN
GIT --> DEPLOY
SECRETS -.-> DEPLOY
DEPLOY -. "delegated policy management" .-> HS
DEPLOY -. "configure local renewal" .-> CERT
CERT -. "DNS-01" .-> CFAPI
end
DEPLOY -. "deploy and verify" .-> HA
DEPLOY -. "deploy and verify" .-> HB
HS -. "control and access policy" .-> MESH
subgraph MONITORING["Monitoring outside the mesh"]
direction LR
PROM["Prometheus
one DNS-only target per edge
verified TLS + authorized source + metrics token"]
GRAF["Grafana
traffic, sessions, service paths
origin probes and backend errors"]
ALERT["Optional alert rules and delivery
must be configured separately"]
PROM --> GRAF
PROM -.-> ALERT
end
PROM -. "HTTPS /_edge/metrics" .-> HA
PROM -. "HTTPS /_edge/metrics" .-> HB
subgraph BOUNDARIES["Failure handling and limits - explanatory notes"]
direction LR
LIMIT_EDGE["Public edge unavailable
Cloudflare selects a healthy edge for new connections"]
LIMIT_PATH["One service path unavailable
HAProxy uses the peer when its path is healthy"]
LIMIT_ORIGIN["Shared origin unavailable
Both edges lose that application
edge redundancy does not replicate origins"]
LIMIT_STREAM["Open video streams and WebSockets
Existing connections do not migrate
clients must reconnect"]
LIMIT_EDGE ~~~ LIMIT_PATH ~~~ LIMIT_ORIGIN ~~~ LIMIT_STREAM
end
%% Invisible positioning only: this is not a data-flow connection.
ORIGINS ~~~ BOUNDARIES
classDef public fill:#233d58,stroke:#7ab5dc,color:#f2f6ff
classDef edge fill:#284c44,stroke:#7bc8a7,color:#f2f6ff
classDef origin fill:#514132,stroke:#d6b48c,color:#f2f6ff
classDef ops fill:#43395c,stroke:#b4a0de,color:#f2f6ff
classDef monitor fill:#34495b,stroke:#9bbdd4,color:#f2f6ff
classDef note fill:#4c303a,stroke:#dd9ba7,color:#fff5f6
class WEB,MEDIA,DNS,CF,PROBE public
class HA,HB,SELECTA,SELECTB,GWA,GWB,ANA,ANB,MESH edge
class VPS,ROUTER,FW,NPM,APPS origin
class GIT,PLAN,DEPLOY,SECRETS,HS,CERT,CFAPI ops
class PROM,GRAF,ALERT monitor
class LIMIT_EDGE,LIMIT_PATH,LIMIT_ORIGIN,LIMIT_STREAM note
style BOUNDARIES fill:#291f2a,stroke:#dd9ba7,color:#fff5f6