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:
1 parent
9c8298df05
commit
4a679fce80
10 files changed
+235
-276
No files matched your search
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
# SearXNG
|
# SearXNG
|
||||||
SEARXNG_SECRET=changeme
|
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)
|
# Gluetun VPN (see https://github.com/qdm12/gluetun-wiki)
|
||||||
# Single-peer WireGuard model. Fill from your provider's WireGuard config.
|
# 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_USER=admin
|
||||||
GRAFANA_ADMIN_PASSWORD=changeme
|
GRAFANA_ADMIN_PASSWORD=changeme
|
||||||
|
|
||||||
# OpenWebUI -> llama.cpp on Dankserver (frank-default-large, port 7770)
|
# Caddy (the homelab's single reverse proxy, on this host)
|
||||||
# 2026-08-17 redo: the llama.cpp box moved from Dankclaw to Dankserver.
|
# bcrypt hash of the homepage/protected-vhost password (caddy hash-password).
|
||||||
OPENAI_API_BASE_URL=http://192.168.0.235:7770/v1
|
# deploy.sh renders it into /home/dank/Containers/caddy/Caddyfile.
|
||||||
OPENAI_API_KEY=not-needed
|
CADDY_BASICAUTH_HASH=changeme
|
||||||
|
|
||||||
# Hermes -> Signal (signal-cli daemon on the host, port 8080)
|
# 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).
|
# E.164 number of the linked Signal account (your primary number, for Note-to-Self).
|
||||||
|
|||||||
+104
-15
@@ -1,12 +1,19 @@
|
|||||||
# Dankclaw Caddyfile
|
# Homelab Caddyfile — the single Caddy for both hosts.
|
||||||
# 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.
|
|
||||||
#
|
#
|
||||||
# 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
|
# 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
|
# (dankclaw/dnsmasq/addn-hosts — every name resolves to 192.168.0.143)
|
||||||
# G36 router (DNS Relay -> statically configured -> 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
|
# 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).
|
# the prefix and adds a wildcard Let's Encrypt cert via DNS-01 (Porkbun).
|
||||||
@@ -30,12 +37,21 @@ http://dankclaw.franksdank.cloud {
|
|||||||
file_server
|
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 {
|
http://search.franksdank.cloud {
|
||||||
basicauth {
|
basicauth {
|
||||||
dank $2a$14$REPLACE_WITH_HASH
|
dank $2a$14$REPLACE_WITH_HASH
|
||||||
}
|
}
|
||||||
reverse_proxy gluetun:8080
|
reverse_proxy 192.168.0.143:8081
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- ComfyUI ---
|
# --- ComfyUI ---
|
||||||
@@ -43,9 +59,7 @@ http://comfyui.franksdank.cloud {
|
|||||||
reverse_proxy comfyui:8188
|
reverse_proxy comfyui:8188
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- OpenWebUI ---
|
# --- OpenWebUI (quadlet on slirp4netns; published 3005) ---
|
||||||
# open-webui is a quadlet container on slirp4netns (isolated), so Caddy cannot
|
|
||||||
# reach it by container name. Proxy to its published LAN port instead.
|
|
||||||
http://chat.franksdank.cloud {
|
http://chat.franksdank.cloud {
|
||||||
basicauth {
|
basicauth {
|
||||||
dank $2a$14$REPLACE_WITH_HASH
|
dank $2a$14$REPLACE_WITH_HASH
|
||||||
@@ -61,9 +75,9 @@ http://hermes.franksdank.cloud {
|
|||||||
reverse_proxy hermes:8090
|
reverse_proxy hermes:8090
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- Gitea (migrated) ---
|
# --- Gitea (quadlet; published 3000) ---
|
||||||
http://gitea.franksdank.cloud {
|
http://gitea.franksdank.cloud {
|
||||||
reverse_proxy gitea-server:3000
|
reverse_proxy 192.168.0.143:3000
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- Grafana ---
|
# --- Grafana ---
|
||||||
@@ -84,8 +98,83 @@ http://prometheus.franksdank.cloud {
|
|||||||
|
|
||||||
# --- Loki ---
|
# --- Loki ---
|
||||||
http://loki.franksdank.cloud {
|
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 {
|
basicauth {
|
||||||
dank $2a$14$REPLACE_WITH_HASH
|
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
@@ -44,23 +44,10 @@ services:
|
|||||||
retries: 5
|
retries: 5
|
||||||
start_period: 180s
|
start_period: 180s
|
||||||
|
|
||||||
# --- Chat UI + agent -----------------------------------------------------
|
# --- Chat UI (open-webui) ------------------------------------------------
|
||||||
openwebui:
|
# Now a boot-persistent Quadlet unit (quadlet, slirp4netns, port 3005), not
|
||||||
image: ghcr.io/open-webui/open-webui:main
|
# a compose service. See open-webui/quadlet/open-webui.container and
|
||||||
container_name: openwebui
|
# ../docs/Dankclaw/OpenWebUI.md.
|
||||||
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
|
|
||||||
|
|
||||||
hermes:
|
hermes:
|
||||||
image: docker.io/nousresearch/hermes-agent:latest
|
image: docker.io/nousresearch/hermes-agent:latest
|
||||||
@@ -107,50 +94,9 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- /home/dank/hermes-data:/data
|
- /home/dank/hermes-data:/data
|
||||||
|
|
||||||
# --- Git hosting (migrated from /opt/gitea) ------------------------------
|
# --- Git hosting (Gitea) -------------------------------------------------
|
||||||
postgres:
|
# Now boot-persistent Quadlet units (private bridge network, DNS enabled),
|
||||||
image: docker.io/library/postgres:16-alpine
|
# not compose services. See gitea/quadlet/ and ../docs/Dankclaw/Gitea.md.
|
||||||
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
|
|
||||||
|
|
||||||
# --- Observability -------------------------------------------------------
|
# --- Observability -------------------------------------------------------
|
||||||
prometheus:
|
prometheus:
|
||||||
@@ -247,7 +193,11 @@ services:
|
|||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 3
|
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:
|
caddy:
|
||||||
image: docker.io/library/caddy:latest
|
image: docker.io/library/caddy:latest
|
||||||
container_name: caddy
|
container_name: caddy
|
||||||
@@ -256,21 +206,20 @@ services:
|
|||||||
- "80:80"
|
- "80:80"
|
||||||
- "443:443"
|
- "443:443"
|
||||||
volumes:
|
volumes:
|
||||||
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
- /home/dank/Containers/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
|
||||||
- /opt/caddy/data:/data
|
- /home/dank/Containers/caddy/data:/data
|
||||||
- /opt/caddy/config:/config
|
- /home/dank/Containers/caddy/config:/config
|
||||||
- ../shared/homepage:/srv/homepage:ro
|
- ../shared/homepage:/srv/homepage:ro
|
||||||
depends_on:
|
depends_on:
|
||||||
- searxng
|
|
||||||
- comfyui
|
- comfyui
|
||||||
- openwebui
|
|
||||||
- hermes
|
- hermes
|
||||||
- gitea
|
|
||||||
- grafana
|
- grafana
|
||||||
- prometheus
|
- prometheus
|
||||||
- loki
|
- loki
|
||||||
healthcheck:
|
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
|
interval: 30s
|
||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 3
|
retries: 3
|
||||||
+26
-22
@@ -1,29 +1,33 @@
|
|||||||
# A records for the flat franksdank.name scheme (dnsmasq addn-hosts format:
|
# A records for the flat franksdank.name scheme (dnsmasq addn-hosts format:
|
||||||
# <IP> <name> [<name> ...]). Every Caddy vhost name has exactly one record,
|
# <IP> <name> [<name> ...]).
|
||||||
# pointing at the host that runs it. Keep in sync with the Caddyfiles
|
|
||||||
# (tests/dnsmasq.bats cross-checks this).
|
|
||||||
#
|
#
|
||||||
# 192.168.0.235 = dankserver (media/library stack)
|
# Caddy is consolidated on dankclaw (192.168.0.143): it fronts every service
|
||||||
# 192.168.0.143 = dankclaw (AI/search/dev stack)
|
# 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 ---
|
# --- Homepage + legacy name ---
|
||||||
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 ---
|
|
||||||
192.168.0.143 dankclaw.franksdank.cloud
|
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 search.franksdank.cloud
|
||||||
192.168.0.143 comfyui.franksdank.cloud
|
192.168.0.143 comfyui.franksdank.cloud
|
||||||
192.168.0.143 chat.franksdank.cloud
|
192.168.0.143 chat.franksdank.cloud
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
@@ -22,8 +22,3 @@ JELLYFIN_PUBLISH_SERVER_URL=http://192.168.0.235:8096
|
|||||||
|
|
||||||
# *arr Stack
|
# *arr Stack
|
||||||
ARR_TZ=America/New_York
|
ARR_TZ=America/New_York
|
||||||
|
|
||||||
# Porkbun API (franksdank.cloud zone). Caddy uses these for DNS-01
|
|
||||||
# Let's Encrypt wildcard cert issuance. Scope the token to this zone only.
|
|
||||||
PORKBUN_API_KEY=changeme
|
|
||||||
PORKBUN_API_SECRET=changeme
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
# Dankserver Caddyfile
|
|
||||||
# Deploy: copy to /etc/caddy/Caddyfile on dankserver (host Caddy service)
|
|
||||||
# See ../docs/Dankserver/ for service documentation.
|
|
||||||
#
|
|
||||||
# Naming: flat franksdank.cloud scheme (see docs/Infrastructure/Domain-DNS.md).
|
|
||||||
# DNS for these names is served by the dnsmasq quadlet on dankclaw
|
|
||||||
# (dankclaw/dnsmasq/addn-hosts) and handed to LAN clients by the SURFboard
|
|
||||||
# G36 router (DNS Relay -> statically configured -> 192.168.0.143).
|
|
||||||
#
|
|
||||||
# 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).
|
|
||||||
|
|
||||||
{
|
|
||||||
admin off
|
|
||||||
email admin@franksdank.cloud
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Bare-IP access (health checks, convenience) ---
|
|
||||||
:80 {
|
|
||||||
redir http://dankserver.franksdank.cloud/ 302
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Homepage (password protected) ---
|
|
||||||
http://dankserver.franksdank.cloud {
|
|
||||||
basicauth {
|
|
||||||
dank $2a$14$REPLACE_WITH_HASH
|
|
||||||
}
|
|
||||||
root * /srv/homepage
|
|
||||||
file_server
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Plex (existing, kept) ---
|
|
||||||
http://plex.franksdank.casa {
|
|
||||||
reverse_proxy localhost:32400
|
|
||||||
}
|
|
||||||
|
|
||||||
http://plex.franksdank.cloud {
|
|
||||||
reverse_proxy localhost:32400
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Calibre-Web (existing, kept) ---
|
|
||||||
http://books.franksdank.cloud {
|
|
||||||
reverse_proxy localhost:8083
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- OpenWebUI (live name from the host Caddyfile; LLM chat UI on this host) ---
|
|
||||||
http://llms.franksdank.cloud {
|
|
||||||
reverse_proxy localhost:3000
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- RomM ---
|
|
||||||
http://romm.franksdank.cloud {
|
|
||||||
basicauth {
|
|
||||||
dank $2a$14$REPLACE_WITH_HASH
|
|
||||||
}
|
|
||||||
reverse_proxy localhost:8085
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Copyparty ---
|
|
||||||
http://files.franksdank.cloud {
|
|
||||||
basicauth {
|
|
||||||
dank $2a$14$REPLACE_WITH_HASH
|
|
||||||
}
|
|
||||||
reverse_proxy localhost:3923
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Jellyfin ---
|
|
||||||
http://jellyfin.franksdank.cloud {
|
|
||||||
reverse_proxy 127.0.0.1:8096
|
|
||||||
|
|
||||||
transport http {
|
|
||||||
versions h2c 1.1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- *arr Stack ---
|
|
||||||
http://radarr.franksdank.cloud {
|
|
||||||
reverse_proxy localhost:7878
|
|
||||||
}
|
|
||||||
|
|
||||||
http://sonarr.franksdank.cloud {
|
|
||||||
reverse_proxy localhost:8989
|
|
||||||
}
|
|
||||||
|
|
||||||
http://lidarr.franksdank.cloud {
|
|
||||||
reverse_proxy localhost:8686
|
|
||||||
}
|
|
||||||
|
|
||||||
http://readarr.franksdank.cloud {
|
|
||||||
reverse_proxy localhost:8787
|
|
||||||
}
|
|
||||||
|
|
||||||
http://prowlarr.franksdank.cloud {
|
|
||||||
reverse_proxy localhost:9696
|
|
||||||
}
|
|
||||||
|
|
||||||
http://qbittorrent.franksdank.cloud {
|
|
||||||
reverse_proxy localhost:8080
|
|
||||||
}
|
|
||||||
|
|
||||||
# --- Optional: Jackett ---
|
|
||||||
# http://jackett.franksdank.cloud {
|
|
||||||
# reverse_proxy localhost:9117
|
|
||||||
# }
|
|
||||||
|
|
||||||
# --- Optional: Whisparr ---
|
|
||||||
# http://whisparr.franksdank.cloud {
|
|
||||||
# reverse_proxy localhost:6969
|
|
||||||
# }
|
|
||||||
+34
-10
@@ -26,17 +26,24 @@ set -euo pipefail
|
|||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
declare -A REMOTE_USER REMOTE_HOST REMOTE_DIR COMPOSE HEALTH_URL
|
declare -A REMOTE_USER REMOTE_HOST REMOTE_DIR COMPOSE HEALTH_URL
|
||||||
|
|
||||||
|
# Caddy is consolidated on dankclaw: it fronts both hosts' services. The
|
||||||
|
# dankserver media stack runs as bare containers (not this repo's compose),
|
||||||
|
# so its deploy is pull-only; health is checked against the shared Caddy.
|
||||||
REMOTE_USER[dankclaw]=dank
|
REMOTE_USER[dankclaw]=dank
|
||||||
REMOTE_HOST[dankclaw]=dankclaw
|
REMOTE_HOST[dankclaw]=dankclaw
|
||||||
REMOTE_DIR[dankclaw]="/home/dank/serverAppConfigs"
|
REMOTE_DIR[dankclaw]="/home/dank/serverAppConfigs"
|
||||||
COMPOSE[dankclaw]="dankclaw/compose.yaml"
|
COMPOSE[dankclaw]="dankclaw/compose.yaml"
|
||||||
HEALTH_URL[dankclaw]="http://127.0.0.1/"
|
HEALTH_URL[dankclaw]="http://127.0.0.1/"
|
||||||
|
COMPOSE_UP[dankclaw]=1
|
||||||
|
COPY_SHARED[dankclaw]=1
|
||||||
|
|
||||||
REMOTE_USER[dankserver]=dank
|
REMOTE_USER[dankserver]=dank
|
||||||
REMOTE_HOST[dankserver]=dankserver
|
REMOTE_HOST[dankserver]=dankserver
|
||||||
REMOTE_DIR[dankserver]="/home/dank/serverAppConfigs"
|
REMOTE_DIR[dankserver]="/home/dank/serverAppConfigs"
|
||||||
COMPOSE[dankserver]="dankserver/compose.yaml"
|
COMPOSE[dankserver]="dankserver/compose.yaml"
|
||||||
HEALTH_URL[dankserver]="http://127.0.0.1/"
|
HEALTH_URL[dankserver]="http://192.168.0.143/"
|
||||||
|
COMPOSE_UP[dankserver]=0
|
||||||
|
COPY_SHARED[dankserver]=0
|
||||||
|
|
||||||
TARGET="${1:-}"
|
TARGET="${1:-}"
|
||||||
DRY_RUN=0
|
DRY_RUN=0
|
||||||
@@ -101,21 +108,38 @@ cd '${REMOTE_DIR[$TARGET]}'
|
|||||||
echo '[2/4] Pulling repo on ${TARGET}...'
|
echo '[2/4] Pulling repo on ${TARGET}...'
|
||||||
git pull origin main
|
git pull origin main
|
||||||
|
|
||||||
echo '[3/4] Copying shared configs into place...'
|
|
||||||
mkdir -p /opt/searxng/config /opt/prometheus/config /opt/loki/config /opt/alloy/config
|
|
||||||
cp shared/searxng/settings.yml /opt/searxng/config/settings.yml
|
|
||||||
cp shared/searxng/limiter.toml /opt/searxng/config/limiter.toml
|
|
||||||
cp shared/prometheus/prometheus.yml /opt/prometheus/config/prometheus.yml
|
|
||||||
cp shared/loki/loki-config.yaml /opt/loki/config/loki-config.yaml
|
|
||||||
cp shared/alloy/config.alloy /opt/alloy/config/config.alloy
|
|
||||||
if [ -f .env ]; then
|
if [ -f .env ]; then
|
||||||
echo ' .env present, using it'
|
echo ' .env present, using it'
|
||||||
else
|
else
|
||||||
echo ' WARN: no .env on target; copy the .env.example and fill it in'
|
echo ' WARN: no .env on target; copy the .env.example and fill it in'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo '[4/4] Restarting stack...'
|
if [ '${COPY_SHARED[$TARGET]}' = '1' ]; then
|
||||||
podman-compose -f '${COMPOSE[$TARGET]}' up -d --pull=missing
|
echo '[3/4] Copying shared configs into place...'
|
||||||
|
mkdir -p /opt/searxng/config /opt/prometheus/config /opt/loki/config /opt/alloy/config
|
||||||
|
cp shared/searxng/settings.yml /opt/searxng/config/settings.yml
|
||||||
|
cp shared/searxng/limiter.toml /opt/searxng/config/limiter.toml
|
||||||
|
cp shared/prometheus/prometheus.yml /opt/prometheus/config/prometheus.yml
|
||||||
|
cp shared/loki/loki-config.yaml /opt/loki/config/loki-config.yaml
|
||||||
|
cp shared/alloy/config.alloy /opt/alloy/config/config.alloy
|
||||||
|
|
||||||
|
echo ' Rendering Caddyfile (basicauth hash from .env)...'
|
||||||
|
set -a; . ./.env; set +a
|
||||||
|
if [ -z \"\${CADDY_BASICAUTH_HASH:-}\" ]; then
|
||||||
|
echo ' WARN: CADDY_BASICAUTH_HASH not set in .env; keeping placeholder'
|
||||||
|
else
|
||||||
|
mkdir -p /home/dank/Containers/caddy
|
||||||
|
sed \"s/REPLACE_WITH_HASH/\${CADDY_BASICAUTH_HASH}/g\" dankclaw/Caddyfile \\
|
||||||
|
> /home/dank/Containers/caddy/Caddyfile
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ '${COMPOSE_UP[$TARGET]}' = '1' ]; then
|
||||||
|
echo '[4/4] Restarting stack...'
|
||||||
|
podman-compose -f '${COMPOSE[$TARGET]}' up -d --pull=missing
|
||||||
|
else
|
||||||
|
echo '[4/4] Skipping compose restart (stack runs as bare containers on this host)'
|
||||||
|
fi
|
||||||
"
|
"
|
||||||
|
|
||||||
if [ "$DRY_RUN" = "1" ]; then
|
if [ "$DRY_RUN" = "1" ]; then
|
||||||
|
|||||||
+37
-23
@@ -1,46 +1,60 @@
|
|||||||
#!/usr/bin/env bats
|
#!/usr/bin/env bats
|
||||||
# Seam: Caddyfile validity.
|
# Seam: Caddyfile validity.
|
||||||
# Each host must have a Caddyfile. Site blocks must reference services by the
|
#
|
||||||
# hostname convention (*.dankserver.local / *.dankclaw.local) and must proxy to
|
# There is exactly one Caddy in the homelab: the container on dankclaw
|
||||||
# reachable upstreams. When caddy(1) exists locally we run `caddy validate`;
|
# (dankclaw/compose.yaml, service 'caddy'). It fronts every service on both
|
||||||
# otherwise we do structural checks.
|
# hosts — local services by container name, dankserver services by published
|
||||||
|
# LAN port. Site blocks use the flat franksdank.cloud / franksdank.casa
|
||||||
|
# convention. The basicauth hash is a placeholder in git; deploy.sh renders
|
||||||
|
# the real hash from the host .env (CADDY_BASICAUTH_HASH).
|
||||||
|
|
||||||
load helpers
|
load helpers
|
||||||
|
|
||||||
@test "a Caddyfile exists for dankserver and dankclaw" {
|
CADDYFILE="$REPO_ROOT/dankclaw/Caddyfile"
|
||||||
[ -f "$REPO_ROOT/dankserver/Caddyfile" ]
|
|
||||||
[ -f "$REPO_ROOT/dankclaw/Caddyfile" ]
|
@test "the Caddyfile lives on dankclaw only (Caddy is consolidated there)" {
|
||||||
|
[ -f "$CADDYFILE" ]
|
||||||
|
[ ! -f "$REPO_ROOT/dankserver/Caddyfile" ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "Caddyfiles use the flat franksdank.cloud hostname convention" {
|
@test "the Caddyfile uses the flat franksdank.cloud hostname convention" {
|
||||||
grep -qE '\.franksdank\.cloud' "$REPO_ROOT/dankserver/Caddyfile"
|
grep -qE '\.franksdank\.cloud' "$CADDYFILE"
|
||||||
grep -qE '\.franksdank\.cloud' "$REPO_ROOT/dankclaw/Caddyfile"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "no .local vhost names remain in either Caddyfile" {
|
@test "no .local vhost names remain in the Caddyfile" {
|
||||||
! grep -qE '^([a-z0-9.-]+\.)?(dankserver|dankclaw)\.local[[:space:]]*\{' \
|
! grep -qE '^([a-z0-9.-]+\.)?(dankserver|dankclaw)\.local[[:space:]]*\{' \
|
||||||
"$REPO_ROOT/dankserver/Caddyfile" "$REPO_ROOT/dankclaw/Caddyfile"
|
"$CADDYFILE"
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "Caddyfiles have balanced braces" {
|
@test "the Caddyfile has balanced braces" {
|
||||||
for f in "$REPO_ROOT/dankserver/Caddyfile" "$REPO_ROOT/dankclaw/Caddyfile"; do
|
|
||||||
local open close
|
local open close
|
||||||
open=$(grep -o '{' "$f" | wc -l)
|
open=$(grep -o '{' "$CADDYFILE" | wc -l)
|
||||||
close=$(grep -o '}' "$f" | wc -l)
|
close=$(grep -o '}' "$CADDYFILE" | wc -l)
|
||||||
[ "$open" -eq "$close" ] || { echo "$f: unbalanced braces ($open vs $close)"; return 1; }
|
[ "$open" -eq "$close" ] || { echo "unbalanced braces ($open vs $close)"; return 1; }
|
||||||
done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "caddy validate passes when caddy is available" {
|
@test "caddy validate passes when caddy is available" {
|
||||||
if command -v caddy >/dev/null 2>&1; then
|
if command -v caddy >/dev/null 2>&1; then
|
||||||
caddy validate --config "$REPO_ROOT/dankserver/Caddyfile" --adapter caddyfile
|
caddy validate --config "$CADDYFILE" --adapter caddyfile
|
||||||
caddy validate --config "$REPO_ROOT/dankclaw/Caddyfile" --adapter caddyfile
|
|
||||||
else
|
else
|
||||||
skip "caddy binary not installed locally"
|
skip "caddy binary not installed locally"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "homepage basic auth exists in the Caddyfiles" {
|
@test "homepage basic auth exists in the Caddyfile" {
|
||||||
grep -qiE 'basicauth|basic_auth' "$REPO_ROOT/dankserver/Caddyfile"
|
grep -qiE 'basicauth|basic_auth' "$CADDYFILE"
|
||||||
grep -qiE 'basicauth|basic_auth' "$REPO_ROOT/dankclaw/Caddyfile"
|
}
|
||||||
|
|
||||||
|
@test "the Caddyfile carries the renderable basicauth placeholder" {
|
||||||
|
grep -q 'REPLACE_WITH_HASH' "$CADDYFILE"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "compose no longer defines the quadlet-replaced services" {
|
||||||
|
# gitea, its postgres, and open-webui run as boot-persistent quadlets on
|
||||||
|
# dankclaw (see dankclaw/gitea/quadlet, dankclaw/open-webui/quadlet).
|
||||||
|
! grep -qE '^\s+(gitea|postgres|openwebui):\s*$' "$REPO_ROOT/dankclaw/compose.yaml"
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "the caddy service healthcheck probes the bare-IP catch-all" {
|
||||||
|
grep -q 'curl.*-sf.*http://localhost:80/' "$REPO_ROOT/dankclaw/compose.yaml"
|
||||||
}
|
}
|
||||||
+11
-11
@@ -84,18 +84,10 @@ record_ip_for() {
|
|||||||
awk -v n="$1" '{ip=$1; for (i=2; i<=NF; i++) if ($i==n) print ip}' "$HOSTS" | head -1
|
awk -v n="$1" '{ip=$1; for (i=2; i<=NF; i++) if ($i==n) print ip}' "$HOSTS" | head -1
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "every dankserver Caddyfile vhost has an A record on 192.168.0.235" {
|
# Caddy is consolidated on dankclaw (192.168.0.143): it fronts every service
|
||||||
local names n ip
|
# on both hosts, so every vhost name resolves to that one IP.
|
||||||
names="$(vhost_names "$REPO_ROOT/dankserver/Caddyfile")"
|
|
||||||
[ -n "$names" ] || { echo "no .franksdank vhosts found in dankserver/Caddyfile"; return 1; }
|
|
||||||
while IFS= read -r n; do
|
|
||||||
[ -z "$n" ] && continue
|
|
||||||
ip="$(record_ip_for "$n")"
|
|
||||||
[ "$ip" = "192.168.0.235" ] || { echo "$n -> '$ip' (want 192.168.0.235)"; return 1; }
|
|
||||||
done <<< "$names"
|
|
||||||
}
|
|
||||||
|
|
||||||
@test "every dankclaw Caddyfile vhost has an A record on 192.168.0.143" {
|
@test "every Caddyfile vhost has an A record on 192.168.0.143" {
|
||||||
local names n ip
|
local names n ip
|
||||||
names="$(vhost_names "$REPO_ROOT/dankclaw/Caddyfile")"
|
names="$(vhost_names "$REPO_ROOT/dankclaw/Caddyfile")"
|
||||||
[ -n "$names" ] || { echo "no .franksdank vhosts found in dankclaw/Caddyfile"; return 1; }
|
[ -n "$names" ] || { echo "no .franksdank vhosts found in dankclaw/Caddyfile"; return 1; }
|
||||||
@@ -106,6 +98,14 @@ record_ip_for() {
|
|||||||
done <<< "$names"
|
done <<< "$names"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "no addn-hosts records point at the old per-host split (192.168.0.235)" {
|
||||||
|
! awk '!/^[[:space:]]*#/ && NF {print $1}' "$HOSTS" | grep -q '^192\.168\.0\.235$'
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "the retired llms name has no A record" {
|
||||||
|
! grep -qE '\billms\.franksdank\.cloud\b' "$HOSTS"
|
||||||
|
}
|
||||||
|
|
||||||
@test "no secret material in the dnsmasq files" {
|
@test "no secret material in the dnsmasq files" {
|
||||||
local f
|
local f
|
||||||
for f in "$DNSMASQ_DIR"/*; do
|
for f in "$DNSMASQ_DIR"/*; do
|
||||||
|
|||||||
Reference in new issue
Block a user