Change HEALTH_TARGET_ADDRESS to ProtonVPN API endpoints so TLS startup
check doesn't fail against cloudflare.com/github.com (rejected from
ProtonVPN exit IPs). Rename deprecated DNS_ADDRESS to
DNS_UPSTREAM_PLAIN_ADDRESSES in fornax-workers.yaml.
DoT (port 853) to 1.1.1.1 through ProtonVPN VPN tunnel gets TCP RST,
causing gluetun healthcheck to fail (can't resolve github.com /
cloudflare.com). k8s coredns at 10.43.0.10 is reachable via eth0
within FIREWALL_OUTBOUND_SUBNETS, bypassing the VPN for DNS while
letting all other traffic tunnel correctly.
Also rename VPN_ENDPOINT_IP/PORT to WIREGUARD_ENDPOINT_IP/PORT to
suppress gluetun deprecation warnings.
gluetun's bundled protonvpn server list has stale IPs for US-TX#179
(37.19.200.26) and US-TX#220 (95.173.217.2). The WIREGUARD_ENDPOINT_IP
env var is a filter not an override, so there's no way to redirect
protonvpn provider to a new IP.
Switch to custom WireGuard provider with hardcoded endpoint IPs from
fresh ProtonVPN configs (95.173.217.29 / 146.70.58.130). Add
DNS_KEEP_NAMESERVER=on so gluetun leaves k8s DNS intact instead of
routing DNS through its own proxy (which breaks in-cluster).
Port forwarding is not available with custom provider; will restore
once gluetun releases an updated server list image.
gluetun rejects VPN_ENDPOINT_PORT when SERVER_NAMES is used (server
selection mode), and warns that VPN_ENDPOINT_IP is deprecated in
favour of WIREGUARD_ENDPOINT_IP. Use only WIREGUARD_ENDPOINT_IP;
port 51820 is ProtonVPN's default and doesn't need to be set.
ProtonVPN migrated US-TX#179 (37.19.200.26 → 95.173.217.29) and
US-TX#220 (95.173.217.2 → 146.70.58.130). gluetun's bundled server
list still has the old dead IPs. Override via VPN_ENDPOINT_IP and
VPN_ENDPOINT_PORT so gluetun uses the correct endpoints while keeping
the protonvpn provider (and port forwarding) intact.
Avoids gluetun's ProtonVPN server name lookup entirely. All WireGuard
parameters (endpoint, peer key, client address, keepalive) come directly
from the downloaded ProtonVPN config.
US-TX#179 and US-TX#220 were failing — WireGuard handshake completing
but no traffic passing (silent drop). Switching both workers to US-IL#267
and pinning the endpoint IP and peer public key directly to avoid relying
on gluetun's built-in server list lookup.
Python 3.14 raises PatternError for backslash sequences like \P in
re.sub replacement strings. Switch to lambda replacements which bypass
that interpretation. Also pin to python:3.13-alpine to avoid future
surprises from pulling :3-alpine.
The /api/ui path is now routed to the fornax-ui nginx pod (which proxies
internally to the coordinator) rather than directly to the coordinator.
This fixes the browser fetch being blocked by Cloudflare Access.
Both coordinator and UI deployments get a restartedAt bump to pull the
new images after CI builds.
- fornax service: targetPort 8080→3000 (Express listens on 3000, not 8080)
This was silently dropping all Radarr/Sonarr requests
- coordinator deployment: add QBT_WORKER_ADDRS, QBT_USER, QBT_PASS
so /api/ui/state can query real worker qBittorrent instances
- remove Python coordinator ConfigMap and Deployment (superseded)
- Fix liveness/readiness probe paths from /api/v2/app/version (qBittorrent)
to /health and /health/ready (Fornax Express server). Pod was restarting
every ~30s due to probe failures against wrong endpoint.
- Add fornax-ui deployment and service for React dashboard
(image: fornax/ui:latest, nginx on port 80)
- Split ingress: /api and /health → coordinator:3000, / → ui:80
- Update kustomization to include UI resources
- Worker deployment: add COORDINATOR_URL and WORKER_ID env vars to
portforward-helper; webhook calls coordinator /api/v2/workers/:id/port-lease
after each NAT-PMP renewal so coordinator tracks current forwarded port
- Worker overlays: patch WORKER_ID env var to actual worker ID (tx253/tx34)
- Grafana: fornax-grafana Argo CD app pointing to k8s/fornax/grafana/
ConfigMap with grafana_dashboard=1 label for auto-discovery by sidecar;
7 panels covering bandwidth, active torrents, worker health, request rate,
and error rate
Register voidstash.xyz as the private media stack domain.
- DNS delegated from Porkbun to Cloudflare nameservers
- stash.voidstash.xyz and whisparr.voidstash.xyz CNAME to legion tunnel
- Tunnel ingress rules added for both subdomains → Traefik
Switch qbt-config-patch from busybox/sed to python:3-alpine so we can
cleanly handle INI keys with backslashes. Now seeds both password hash
and high-throughput defaults (3000 max connections, 50 active downloads,
unlimited rate) on fresh PVC deployments. Existing configs are updated
in-place; API-applied values are preserved on restart.
Sonarr and Radarr service settings live in settings.json (not the SQLite
database). Extend the restore.js initContainer to seed both services on
first boot or after a PVC wipe, matching current live config.
LocalhostAuthEnabled=false isn't honored in this qBT version. Instead, initContainer
stamps the PBKDF2 hash for admin:adminadmin before startup so all three pods have
consistent credentials. portforward-helper and coordinator restored to cookie-based
SID auth.
Adds qbt-config-patch initContainer that sets WebUI\LocalhostAuthEnabled=false
in qBittorrent.conf before the main containers start. Prevents qBittorrent from
overwriting the setting (previously edited at runtime, lost on pod restart).
LocalhostAuthEnabled=false set in all three qbt configs. portforward-helper and
coordinator now call the API directly without auth — no more ban risk from failed
login attempts.
- portforward-helper now reads /tmp/gluetun/forwarded_port via shared emptyDir
volume instead of polling gluetun HTTP API (which returned Unauthorized)
- Main qbt: SERVER_NAMES=US-IL#1 (valid entry in gluetun server list)
- Both manifests cleaned up
- Main qbt: switch from pinned US-IL#149 (no PF) to SERVER_REGIONS=Illinois so
gluetun picks any IL server with port forwarding enabled
- Fornax workers: portforward-helper 32Mi→96Mi to stop OOMKill