infra: hf-pdocker-01 Phase 2 migration — all services returned from failover

Node hf-pdocker-01 hardware fully repaired and rebuilt (2026-03-08). This
commit captures the completed Phase 2 migration where all failover services
are returned from hf-pdocker-02/bart back to hf-pdocker-01 as their permanent
home.

New compose stacks for hf-pdocker-01:
- adguard: AdGuard Home DNS/ad-blocking (:3000, :53)
- traefik: Reverse proxy with TLS (:80/:443/:8090), .env.example included
- monitoring: Grafana, Prometheus, Alertmanager, InfluxDB, Blackbox, matrix-receiver
- smarthome: Home Assistant (:8123), ESPHome (:6052), Mosquitto (:1883/:8883)
- matrix: Synapse homeserver (:8008) + synapse-db + well-known (:8010)
- guacamole: Guacamole (:8199) + guacd + guacamole-db (local MySQL)
- farm-manager: Farm Manager API (:8888)

Updated compose stacks for hf-pdocker-01:
- frigate-notify: MQTT/Matrix hosts reverted from bart (.167) to hf-pdocker-01 (.192)

Documentation:
- docs/plans/2026-03-08-hf-pdocker-01-phase2-migration.md (new)
- CLAUDE.md: Full service distribution update — failover labels removed,
  hf-pdocker-02/bart cleaned up to reflect only permanent services,
  NetClaw/Aaron added to hf-pdocker-02, DNSStubListener fix noted

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-08 19:57:03 -05:00
parent 26d559feb5
commit cd37bb9858
10 changed files with 555 additions and 37 deletions
@@ -0,0 +1,26 @@
services:
adguardhome:
image: adguard/adguardhome:latest
container_name: adguardhome
restart: unless-stopped
volumes:
- /mnt/docker-data/configs/adguardhome/work:/opt/adguardhome/work
- /mnt/docker-data/configs/adguardhome/conf:/opt/adguardhome/conf
ports:
- "53:53/tcp"
- "53:53/udp"
- "3000:3000/tcp"
- "853:853/tcp"
mem_limit: 256M
memswap_limit: 256M
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://localhost:3000/ || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
@@ -0,0 +1,25 @@
services:
farm-manager:
image: farm-manager:latest
container_name: farm-manager
restart: unless-stopped
ports:
- "8888:8888"
environment:
- CONFIG_PATH=/app/config.json
- GROUPS_PATH=/data/groups.json
volumes:
- /mnt/docker-data/configs/farm-manager:/data
mem_limit: 256M
memswap_limit: 256M
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:8888/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
@@ -9,9 +9,9 @@ services:
environment:
- TZ=America/Chicago
- FRIGATE_URL=http://localhost:5000
- MQTT_HOST=192.168.86.167
- MQTT_HOST=192.168.86.192
- MQTT_PORT=1883
- MATRIX_HOMESERVER=http://192.168.86.167:8008
- MATRIX_HOMESERVER=http://192.168.86.192:8008
- CLIP_SERVICE_URL=http://192.168.86.196:5200
- OLLAMA_URL=http://192.168.86.196:11434
- OLLAMA_MODEL=llava:13b
@@ -0,0 +1,82 @@
services:
guacamole:
image: guacamole/guacamole:latest
container_name: guacamole
restart: unless-stopped
environment:
- GUACD_HOSTNAME=guacd
- MYSQL_HOSTNAME=guacamole-db
- MYSQL_DATABASE=guacamole_db
- MYSQL_USER=guacamole_user
- MYSQL_PASSWORD=guacamole_pass
ports:
- "8199:8080"
networks:
- guacamole-internal
depends_on:
guacamole-db:
condition: service_healthy
mem_limit: 512M
memswap_limit: 512M
healthcheck:
test: ["CMD-SHELL", "curl -f http://localhost:8080/guacamole/ || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
guacd:
image: guacamole/guacd:latest
container_name: guacd
restart: unless-stopped
networks:
- guacamole-internal
mem_limit: 512M
memswap_limit: 512M
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
guacamole-db:
image: mysql:8.0
container_name: guacamole-db
user: "1024:100"
restart: unless-stopped
command: --innodb-use-native-aio=0 --innodb-buffer-pool-size=128M
environment:
- MYSQL_ROOT_PASSWORD=guacamole_root
- MYSQL_DATABASE=guacamole_db
- MYSQL_USER=guacamole_user
- MYSQL_PASSWORD=guacamole_pass
volumes:
# NFS cannot be used for MySQL due to persistent NLM lock issues (ibdata1, dblwr files)
# guacamole-db uses LOCAL storage on hf-pdocker-01
- /var/lib/guacamole-db:/var/lib/mysql
networks:
- guacamole-internal
mem_limit: 512M
memswap_limit: 512M
labels:
- "com.centurylinklabs.watchtower.enable=false"
healthcheck:
test: ["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 --silent"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
networks:
guacamole-internal:
driver: bridge
@@ -0,0 +1,86 @@
services:
synapse:
image: matrixdotorg/synapse:latest
container_name: synapse
restart: unless-stopped
environment:
- SYNAPSE_SERVER_NAME=mess.hobbs.farm
- SYNAPSE_REPORT_STATS=no
volumes:
- /mnt/docker-data/configs/matrix/data:/data
ports:
- "8008:8008"
networks:
- matrix-internal
depends_on:
synapse-db:
condition: service_healthy
mem_limit: 1G
memswap_limit: 1G
healthcheck:
test: ["CMD-SHELL", "curl -fSs http://localhost:8008/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
wellknown:
image: nginx:alpine
container_name: matrix-wellknown
restart: unless-stopped
volumes:
- /mnt/docker-data/configs/matrix/wellknown:/usr/share/nginx/html/.well-known/matrix:ro
ports:
- "8010:80"
mem_limit: 64M
memswap_limit: 64M
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://localhost:80/ || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
synapse-db:
image: postgres:15-alpine
container_name: synapse-db
user: "1024:100"
restart: unless-stopped
environment:
- POSTGRES_USER=synapse
- POSTGRES_PASSWORD=synapse
- POSTGRES_DB=synapse
- POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
volumes:
- /mnt/docker-data/databases/matrix-db:/var/lib/postgresql/data
networks:
- matrix-internal
mem_limit: 256M
memswap_limit: 256M
labels:
- "com.centurylinklabs.watchtower.enable=false"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U synapse -d synapse"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
networks:
matrix-internal:
driver: bridge
@@ -0,0 +1,153 @@
networks:
monitoring-net:
name: monitoring-net
services:
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: unless-stopped
user: "104"
networks:
- monitoring-net
ports:
- "3001:3000"
volumes:
- /mnt/docker-data/configs/grafana:/var/lib/grafana
mem_limit: 512M
memswap_limit: 512M
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://localhost:3000/api/health || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: unless-stopped
networks:
- monitoring-net
ports:
- "9095:9090"
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/usr/share/prometheus/console_libraries'
- '--web.console.templates=/usr/share/prometheus/consoles'
- '--web.enable-lifecycle'
volumes:
- /mnt/docker-data/configs/prometheus:/etc/prometheus
mem_limit: 512M
memswap_limit: 512M
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://localhost:9090/-/healthy || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
alertmanager:
image: prom/alertmanager:latest
container_name: alertmanager
restart: unless-stopped
networks:
- monitoring-net
ports:
- "9093:9093"
command:
- '--config.file=/etc/alertmanager/alertmanager.yml'
- '--storage.path=/alertmanager'
volumes:
- /mnt/docker-data/configs/alertmanager:/etc/alertmanager
- alertmanager-data:/alertmanager
mem_limit: 256M
memswap_limit: 256M
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://localhost:9093/-/healthy || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
influxdb:
image: influxdb:1.8-alpine
container_name: influxdb
restart: unless-stopped
networks:
- monitoring-net
ports:
- "8086:8086"
volumes:
- /mnt/docker-data/configs/influxdb:/var/lib/influxdb
mem_limit: 1G
memswap_limit: 1G
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://localhost:8086/ping || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
matrix-alertmanager-receiver:
image: metio/matrix-alertmanager-receiver:2026.2.25
container_name: matrix-alertmanager-receiver
restart: unless-stopped
networks:
- monitoring-net
ports:
- "9094:9094"
volumes:
- /mnt/docker-data/configs/alertmanager/matrix-receiver-config.yml:/config/config.yml:ro
command: ["--config-path", "/config/config.yml"]
mem_limit: 128M
memswap_limit: 128M
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
blackbox-exporter:
image: prom/blackbox-exporter:latest
container_name: blackbox-exporter
restart: unless-stopped
networks:
- monitoring-net
ports:
- "9115:9115"
command:
- '--config.file=/config/blackbox.yml'
volumes:
- /mnt/docker-data/configs/blackbox:/config:ro
mem_limit: 128M
memswap_limit: 128M
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"
volumes:
alertmanager-data:
@@ -0,0 +1,3 @@
# Cloudflare DNS API token for Let's Encrypt DNS challenge
# Actual value stored in Vaultwarden as "Traefik CF_DNS_API_TOKEN"
CF_DNS_API_TOKEN=your-cloudflare-api-token-here
@@ -0,0 +1,39 @@
services:
traefik:
image: traefik:v3.6
container_name: traefik
restart: unless-stopped
command:
- "--api.insecure=true"
- "--providers.file.directory=/config/dynamic"
- "--providers.file.watch=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--certificatesresolvers.letsencrypt.acme.dnschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare"
- "--certificatesresolvers.letsencrypt.acme.email=jeremydanielhobbs@gmail.com"
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
environment:
- CF_DNS_API_TOKEN=${CF_DNS_API_TOKEN}
ports:
- "80:80"
- "443:443"
- "8090:8080"
volumes:
- /mnt/docker-data/traefik/letsencrypt:/letsencrypt
- /mnt/docker-data/traefik/dynamic:/config/dynamic:ro
mem_limit: 512M
memswap_limit: 512M
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://localhost:8080/api/overview || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
logging:
driver: "json-file"
options:
max-size: "50m"
max-file: "3"