Network Architecture & Infrastructure¶
Network topology, server access patterns, Tailscale, Unraid API, and monitoring.
Source: extracted from AGENTS.md
Network Architecture¶
graph TD
CLIENT["Tailscale Client (phone, laptop)
All traffic via WireGuard encrypted tunnel"]
CLIENT -->|http://100.68.251.84:3005| GITEA[Gitea]
CLIENT -->|http://100.68.251.84:3002| GRAFANA[Grafana]
CLIENT -->|http://100.68.251.84:2283| IMMICH[Immich]
NOTE["Public internet: 100.68.251.84 → CGNAT range → unreachable"]
Services are accessed via Caddy reverse proxy (*.home domains) or directly via Tailscale IP + exposed port for debugging.
Why this architecture (design decisions)¶
- Caddy reverse proxy for
*.homedomains - Caddy runs on macvlan (
192.168.100.10) — gets its own LAN IP - AdGuard DNS rewrites
*.home→ Caddy IP - Automatic HTTPS via Tailscale certificates
-
Simplifies service access:
grafana.homeinstead ofIP:port -
Direct port access as fallback
- Services also expose ports directly for debugging and Tailscale access
-
Tailscale WireGuard encryption ensures security on network layer
-
Port conflicts với Unraid nginx
- Unraid nginx chiếm port 80/443 trên Tailscale IP (
100.68.251.84) - Caddy avoids this by using macvlan with its own IP
- Services phải dùng port khác (3005, 3002, 2283, etc.)
Components¶
| Component | Port | Purpose |
|---|---|---|
| Caddy | 80, 443 | Reverse proxy (*.home → services) |
| Unraid nginx | 80, 443 | Unraid GUI (keep untouched) |
Networks¶
- External network
reverse-proxybridges services on the same server for inter-service communication (e.g., Gitea → Redis, Grafana → Loki) - Bridge network
observabilityfor monitoring stack (otel-collector + exporters)
Adding a New Service (Unraid)¶
- Create
stacks/{category}/{service}/compose.yamlwith exposed port - Deploy via Komodo GitOps (git push → sync → confirm → deploy)
Adding a Public Service¶
- Add Cloudflare A record (proxied/orange cloud) for the subdomain
- Add Cloudflare tunnel ingress hoặc expose port
Multi-Server¶
Each server has its own set of services with direct port access:
unraid:
100.68.251.84:<service-port> (direct access)
development:
100.126.172.96:<service-port> (direct access)
Tailscale API Key Management¶
API key dùng để automate Tailscale config.
Admin Console: https://login.tailscale.com/admin/settings/keys
API endpoint: https://api.tailscale.com/api/v2/
Key storage: Komodo Variable TS_API_KEY (use km update variable TS_API_KEY "<key>")
Key types:
| Type | Reuse | Use case |
|---|---|---|
| User key | Expires | Manual operations, testing |
| API key | Long-lived | Automation, scripts, CI/CD |
Security notes:
- API key has full admin access to tailnet
- KHÔNG commit key vào git — store trong Komodo Variables
Unraid GraphQL API¶
Unraid OS 7.x có GraphQL API cho system management.
Endpoint: https://unraid.tail1137c.ts.net/graphql (Tailscale cert auto-issued)
Auth header: x-api-key: <key>
Key storage: Komodo Variable UNRAID_API_KEY
Key management: Settings → Management Access → API Keys (WebGUI)
Available schemas: System info, Array management, Docker containers, Remote access
Usage examples:
# System info
curl -sk "https://unraid.tail1137c.ts.net/graphql" \
-H "x-api-key: $UNRAID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ info { os { platform distro release uptime } cpu { manufacturer brand cores threads } } }"}'
# List Docker containers
curl -sk "https://unraid.tail1137c.ts.net/graphql" \
-H "x-api-key: $UNRAID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ docker { containers { id names state status } } }"}'
# Array status
curl -sk "https://unraid.tail1137c.ts.net/graphql" \
-H "x-api-key: $UNRAID_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ array { state capacity { disks { free used total } } disks { name size status temp } } }"}'
Full docs: https://docs.unraid.net/API/how-to-use-the-api/
Volume Mounts¶
- NFS:
unraid:/mnt/user/appdata/{service}/ - Local:
/opt/appdata/{service}/
Monitoring Stack¶
Single OTEL collector on Unraid with Prometheus scrape hub. Multi-file compose overlay:
compose.yaml # base: otel-collector (all servers)
compose.exporters.yaml # overlay: adguard/logporter/mktxp (Unraid only)
| Stack | Server | file_paths | Purpose |
|---|---|---|---|
otel-collector-unraid |
unraid | both files | OTLP + Prometheus scrape |
otel-collector-development |
development | compose.yaml only | OTLP receiver only |
Key Komodo quirks:
COMPOSE_PROFILESenv var does NOT work — use multi-file overlay instead- Must
RunSyncbeforeDeployStackafter TOML changes - Periphery v1.19.4 runs in its own container with embedded docker compose
Full docs: stacks/monitoring/otel-collector/README.md