Skip to content

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)

  1. Caddy reverse proxy for *.home domains
  2. Caddy runs on macvlan (192.168.100.10) — gets its own LAN IP
  3. AdGuard DNS rewrites *.home → Caddy IP
  4. Automatic HTTPS via Tailscale certificates
  5. Simplifies service access: grafana.home instead of IP:port

  6. Direct port access as fallback

  7. Services also expose ports directly for debugging and Tailscale access
  8. Tailscale WireGuard encryption ensures security on network layer

  9. Port conflicts với Unraid nginx

  10. Unraid nginx chiếm port 80/443 trên Tailscale IP (100.68.251.84)
  11. Caddy avoids this by using macvlan with its own IP
  12. 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-proxy bridges services on the same server for inter-service communication (e.g., Gitea → Redis, Grafana → Loki)
  • Bridge network observability for monitoring stack (otel-collector + exporters)

Adding a New Service (Unraid)

  1. Create stacks/{category}/{service}/compose.yaml with exposed port
  2. Deploy via Komodo GitOps (git push → sync → confirm → deploy)

Adding a Public Service

  1. Add Cloudflare A record (proxied/orange cloud) for the subdomain
  2. 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_PROFILES env var does NOT work — use multi-file overlay instead
  • Must RunSync before DeployStack after TOML changes
  • Periphery v1.19.4 runs in its own container with embedded docker compose

Full docs: stacks/monitoring/otel-collector/README.md