Caddy consolidation: single Caddy on dankclaw fronts both hosts

Per user direction, Caddy runs only on dankclaw. It fronts every
service on both hosts: local services by container name, quadlet
services by published LAN port, dankserver services by published LAN
port (192.168.0.235:PORT).

- dankclaw/Caddyfile: merged vhost list (was: two Caddyfiles, one per
  host). gitea's single vhost moves in from the retired gitea-caddy
  quadlet (option B). Upstreams fixed to live reality: gitea :3000,
  search :8081 (gluetun), chat :3005 (open-webui quadlet), media
  services via 192.168.0.235. Retired: llms (open-webui on dankserver
  is removed; chat is its successor), the dankserver homepage (302 to
  the shared one).
- dankserver/Caddyfile, dankclaw/gitea/Caddyfile: deleted (git rm).
- dnsmasq addn-hosts: every name -> 192.168.0.143 (the one Caddy
  host); no .235 records, no llms.
- compose.yaml: removed the openwebui/postgres/gitea services (they
  are quadlets now); caddy service mounts the rendered Caddyfile from
  /home/dank/Containers/caddy, healthcheck probes the bare-IP
  catch-all (302).
- deploy.sh: per-target logic (dankserver = pull-only, health via the
  shared Caddy at 192.168.0.143); renders CADDY_BASICAUTH_HASH from
  the host .env into the mounted Caddyfile.
- .env.example: CADDY_BASICAUTH_HASH on dankclaw; Porkbun block
  dropped from dankserver (its Caddy is gone); SEARXNG_BASE_URL to
  the flat name; stale OPENAI_* block dropped (quadlet is
  self-contained).
- tests: caddyfile.bats now asserts the single-Caddyfile invariant;
  dnsmasq.bats cross-checks vhosts -> .143 and the retired names.
This commit is contained in:
Frank committed 2026-08-21 21:44:18 -07:00
1 parent 9c8298df05
commit 4a679fce80
10 files changed
+236 -277

No files matched your search

+5 -5
View File
@@ -3,7 +3,7 @@
# SearXNG
SEARXNG_SECRET=changeme
SEARXNG_BASE_URL=http://search.dankclaw.local/
SEARXNG_BASE_URL=http://search.franksdank.cloud/
# Gluetun VPN (see https://github.com/qdm12/gluetun-wiki)
# Single-peer WireGuard model. Fill from your provider's WireGuard config.
@@ -24,10 +24,10 @@ GITEA_DB_PASSWORD=changeme
GRAFANA_ADMIN_USER=admin
GRAFANA_ADMIN_PASSWORD=changeme
# OpenWebUI -> llama.cpp on Dankserver (frank-default-large, port 7770)
# 2026-08-17 redo: the llama.cpp box moved from Dankclaw to Dankserver.
OPENAI_API_BASE_URL=http://192.168.0.235:7770/v1
OPENAI_API_KEY=not-needed
# Caddy (the homelab's single reverse proxy, on this host)
# bcrypt hash of the homepage/protected-vhost password (caddy hash-password).
# deploy.sh renders it into /home/dank/Containers/caddy/Caddyfile.
CADDY_BASICAUTH_HASH=changeme
# Hermes -> Signal (signal-cli daemon on the host, port 8080)
# E.164 number of the linked Signal account (your primary number, for Note-to-Self).
+104 -15
View File
@@ -1,12 +1,19 @@
# Dankclaw Caddyfile
# Reverse proxy for the Dankclaw stack. Caddy runs as a container on the
# podman network; upstreams are container names. Homepage is password
# protected. Replace the bcrypt hashes below (caddy hash-password) at deploy.
# Homelab Caddyfile — the single Caddy for both hosts.
#
# Naming: flat franksdank.cloud scheme (see docs/Infrastructure/Domain-DNS.md).
# Caddy runs as a container on dankclaw (192.168.0.143, this repo's
# compose.yaml service 'caddy') and fronts every service on both hosts:
# - dankclaw services: by container name (compose network) or by the
# published LAN port (quadlet services on isolated networks)
# - dankserver services (192.168.0.235): by published LAN port
#
# Naming: flat franksdank.cloud scheme (see docs/Infrastructure/LAN-DNS.md).
# DNS for these names is served by the dnsmasq quadlet on this host
# (dankclaw/dnsmasq/addn-hosts) and handed to LAN clients by the SURFboard
# G36 router (DNS Relay -> statically configured -> 192.168.0.143).
# (dankclaw/dnsmasq/addn-hosts — every name resolves to 192.168.0.143)
# and handed to LAN clients by the SURFboard G36 router.
#
# The basicauth hash below is a placeholder. deploy.sh renders the real
# hash (CADDY_BASICAUTH_HASH from the host .env, git-ignored) into
# /home/dank/Containers/caddy/Caddyfile, which the container mounts.
#
# Phase 1 uses explicit http:// site addresses (no TLS yet). Phase 2 drops
# the prefix and adds a wildcard Let's Encrypt cert via DNS-01 (Porkbun).
@@ -30,12 +37,21 @@ http://dankclaw.franksdank.cloud {
file_server
}
# --- SearXNG (behind the Gluetun tunnel) ---
# --- Legacy name: the media host has no Caddy of its own anymore ---
http://dankserver.franksdank.cloud {
redir http://dankclaw.franksdank.cloud/ 302
}
# ═══════════════════════════════════════════════════════════════
# Services on this host (dankclaw, 192.168.0.143)
# ═══════════════════════════════════════════════════════════════
# --- SearXNG (quadlet; behind the Gluetun tunnel, published 8081) ---
http://search.franksdank.cloud {
basicauth {
dank $2a$14$REPLACE_WITH_HASH
}
reverse_proxy gluetun:8080
reverse_proxy 192.168.0.143:8081
}
# --- ComfyUI ---
@@ -43,9 +59,7 @@ http://comfyui.franksdank.cloud {
reverse_proxy comfyui:8188
}
# --- OpenWebUI ---
# open-webui is a quadlet container on slirp4netns (isolated), so Caddy cannot
# reach it by container name. Proxy to its published LAN port instead.
# --- OpenWebUI (quadlet on slirp4netns; published 3005) ---
http://chat.franksdank.cloud {
basicauth {
dank $2a$14$REPLACE_WITH_HASH
@@ -61,9 +75,9 @@ http://hermes.franksdank.cloud {
reverse_proxy hermes:8090
}
# --- Gitea (migrated) ---
# --- Gitea (quadlet; published 3000) ---
http://gitea.franksdank.cloud {
reverse_proxy gitea-server:3000
reverse_proxy 192.168.0.143:3000
}
# --- Grafana ---
@@ -84,8 +98,83 @@ http://prometheus.franksdank.cloud {
# --- Loki ---
http://loki.franksdank.cloud {
reverse_proxy loki:3100
}
# ═══════════════════════════════════════════════════════════════
# Services on dankserver (192.168.0.235) — published LAN ports
# ═══════════════════════════════════════════════════════════════
# --- Plex (existing, kept) ---
http://plex.franksdank.casa {
reverse_proxy 192.168.0.235:32400
}
http://plex.franksdank.cloud {
reverse_proxy 192.168.0.235:32400
}
# --- Calibre-Web ---
http://books.franksdank.cloud {
reverse_proxy 192.168.0.235:8083
}
# --- RomM ---
http://romm.franksdank.cloud {
basicauth {
dank $2a$14$REPLACE_WITH_HASH
}
reverse_proxy loki:3100
reverse_proxy 192.168.0.235:8085
}
# --- Copyparty ---
http://files.franksdank.cloud {
basicauth {
dank $2a$14$REPLACE_WITH_HASH
}
reverse_proxy 192.168.0.235:3923
}
# --- Jellyfin ---
http://jellyfin.franksdank.cloud {
reverse_proxy 192.168.0.235:8096
transport http {
versions h2c 1.1
}
}
# --- *arr Stack ---
http://radarr.franksdank.cloud {
reverse_proxy 192.168.0.235:7878
}
http://sonarr.franksdank.cloud {
reverse_proxy 192.168.0.235:8989
}
http://lidarr.franksdank.cloud {
reverse_proxy 192.168.0.235:8686
}
http://readarr.franksdank.cloud {
reverse_proxy 192.168.0.235:8787
}
http://prowlarr.franksdank.cloud {
reverse_proxy 192.168.0.235:9696
}
http://qbittorrent.franksdank.cloud {
reverse_proxy 192.168.0.235:8080
}
# --- Optional: Jackett ---
# http://jackett.franksdank.cloud {
# reverse_proxy 192.168.0.235:9117
# }
# --- Optional: Whisparr ---
# http://whisparr.franksdank.cloud {
# reverse_proxy 192.168.0.235:6969
# }
+18 -69
View File
@@ -44,23 +44,10 @@ services:
retries: 5
start_period: 180s
# --- Chat UI + agent -----------------------------------------------------
openwebui:
image: ghcr.io/open-webui/open-webui:main
container_name: openwebui
restart: unless-stopped
environment:
OPENAI_API_BASE_URL: "${OPENAI_API_BASE_URL}"
OPENAI_API_KEY: "${OPENAI_API_KEY}"
ports:
- "3000:8080"
volumes:
- /opt/openwebui/data:/app/backend/data
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
# --- Chat UI (open-webui) ------------------------------------------------
# Now a boot-persistent Quadlet unit (quadlet, slirp4netns, port 3005), not
# a compose service. See open-webui/quadlet/open-webui.container and
# ../docs/Dankclaw/OpenWebUI.md.
hermes:
image: docker.io/nousresearch/hermes-agent:latest
@@ -107,50 +94,9 @@ services:
volumes:
- /home/dank/hermes-data:/data
# --- Git hosting (migrated from /opt/gitea) ------------------------------
postgres:
image: docker.io/library/postgres:16-alpine
container_name: gitea-postgres
restart: unless-stopped
environment:
POSTGRES_USER: gitea
POSTGRES_PASSWORD: "${GITEA_DB_PASSWORD}"
POSTGRES_DB: gitea
volumes:
- /opt/gitea/data/gitea-postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U gitea"]
interval: 30s
timeout: 10s
retries: 3
gitea:
image: docker.io/gitea/gitea:latest
container_name: gitea-server
restart: unless-stopped
environment:
DATABASE_TYPE: postgres
DATABASE_HOST: gitea-postgres
DATABASE_NAME: gitea
DATABASE_USER: gitea
DATABASE_PASSWD: "${GITEA_DB_PASSWORD}"
DATABASE_PORT: "5432"
USER_UID: "1000"
USER_GID: "1000"
ports:
- "3001:3000"
- "2222:22"
volumes:
- /opt/gitea/data/gitea-gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
depends_on:
- postgres
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/"]
interval: 30s
timeout: 10s
retries: 3
# --- Git hosting (Gitea) -------------------------------------------------
# Now boot-persistent Quadlet units (private bridge network, DNS enabled),
# not compose services. See gitea/quadlet/ and ../docs/Dankclaw/Gitea.md.
# --- Observability -------------------------------------------------------
prometheus:
@@ -247,7 +193,11 @@ services:
timeout: 10s
retries: 3
# --- Reverse proxy + homepage -------------------------------------------
# --- Reverse proxy + homepage (the homelab's single Caddy) ---------------
# The Caddyfile mounted here is the RENDERED copy (basicauth hash filled in
# from the host .env by scripts/deploy.sh); the repo copy carries the
# REPLACE_WITH_HASH placeholder. Certs/config live under
# /home/dank/Containers/caddy (rootless-writable).
caddy:
image: docker.io/library/caddy:latest
container_name: caddy
@@ -256,21 +206,20 @@ services:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- /opt/caddy/data:/data
- /opt/caddy/config:/config
- /home/dank/Containers/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- /home/dank/Containers/caddy/data:/data
- /home/dank/Containers/caddy/config:/config
- ../shared/homepage:/srv/homepage:ro
depends_on:
- searxng
- comfyui
- openwebui
- hermes
- gitea
- grafana
- prometheus
- loki
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:80/"]
# The bare-IP catch-all answers 302 (curl -f passes on <400); this
# proves Caddy itself is serving without touching any upstream.
test: ["CMD", "curl", "-sf", "-o", "/dev/null", "http://localhost:80/"]
interval: 30s
timeout: 10s
retries: 3
+26 -22
View File
@@ -1,29 +1,33 @@
# A records for the flat franksdank.name scheme (dnsmasq addn-hosts format:
# <IP> <name> [<name> ...]). Every Caddy vhost name has exactly one record,
# pointing at the host that runs it. Keep in sync with the Caddyfiles
# (tests/dnsmasq.bats cross-checks this).
# <IP> <name> [<name> ...]).
#
# 192.168.0.235 = dankserver (media/library stack)
# 192.168.0.143 = dankclaw (AI/search/dev stack)
# Caddy is consolidated on dankclaw (192.168.0.143): it fronts every service
# on both hosts, so every name resolves to that one IP. Keep in sync with the
# Caddyfile (tests/dnsmasq.bats cross-checks this).
#
# 192.168.0.143 = dankclaw (runs the Caddy container + the AI/dev stack)
# 192.168.0.235 = dankserver (media stack; reached by Caddy via published
# LAN ports — no DNS records point here)
# --- dankserver services ---
192.168.0.235 dankserver.franksdank.cloud
192.168.0.235 plex.franksdank.cloud
192.168.0.235 plex.franksdank.casa
192.168.0.235 books.franksdank.cloud
192.168.0.235 llms.franksdank.cloud
192.168.0.235 romm.franksdank.cloud
192.168.0.235 files.franksdank.cloud
192.168.0.235 jellyfin.franksdank.cloud
192.168.0.235 radarr.franksdank.cloud
192.168.0.235 sonarr.franksdank.cloud
192.168.0.235 lidarr.franksdank.cloud
192.168.0.235 readarr.franksdank.cloud
192.168.0.235 prowlarr.franksdank.cloud
192.168.0.235 qbittorrent.franksdank.cloud
# --- dankclaw services ---
# --- Homepage + legacy name ---
192.168.0.143 dankclaw.franksdank.cloud
192.168.0.143 dankserver.franksdank.cloud
# --- Media services (dankserver, proxied) ---
192.168.0.143 plex.franksdank.cloud
192.168.0.143 plex.franksdank.casa
192.168.0.143 books.franksdank.cloud
192.168.0.143 romm.franksdank.cloud
192.168.0.143 files.franksdank.cloud
192.168.0.143 jellyfin.franksdank.cloud
192.168.0.143 radarr.franksdank.cloud
192.168.0.143 sonarr.franksdank.cloud
192.168.0.143 lidarr.franksdank.cloud
192.168.0.143 readarr.franksdank.cloud
192.168.0.143 prowlarr.franksdank.cloud
192.168.0.143 qbittorrent.franksdank.cloud
# --- AI / search / dev services (dankclaw, local) ---
192.168.0.143 search.franksdank.cloud
192.168.0.143 comfyui.franksdank.cloud
192.168.0.143 chat.franksdank.cloud
-7
View File
@@ -1,7 +0,0 @@
# Gitea stack Caddyfile (dedicated Caddy container, quadlet-managed).
# Lives on the host at /opt/gitea/data/caddy/Caddyfile.
# Upstream is the quadlet container name gitea-server (port 3000 inside).
gitea.franksdank.cloud {
reverse_proxy gitea-server:3000
}