# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Repository Overview This repository manages a **homelab infrastructure** with self-hosted services running across 3 nodes using a **hybrid orchestration** model: Docker Swarm for core infrastructure services, standalone Docker Compose for isolated services. ### Cluster Architecture **Nodes:** - **hf-pdocker-01** (192.168.86.192) - Swarm Manager, core infrastructure + Frigate NVR - **RESTORED (2026-03-08)** - **hf-pdocker-02** (192.168.86.100) - Standalone Compose services (dev tools, utilities) + **FAILOVER: Traefik, Monitoring, AdGuard** - **bart** (192.168.86.167) - Standalone Compose services (media stack, OpenVAS) **Synology NAS** (192.168.86.30) - Centralized NFS storage for all service data **Access:** Username `glyptic`, password `&Rip8a115` (has sudo access on all nodes) ### Service Distribution ``` hf-pdocker-01 (Manager, 23GB RAM, i7-7700T, NVMe, HD 630 GPU) -- RESTORED (2026-03-08, hardware repaired) ├── [COMPOSE] Frigate NVR (:5000) -- OpenVINO GPU detector, 13 cameras active ├── [COMPOSE] Frigate-Notify v2 (:5199) -- MQTT->AI->Matrix notifications ├── [COMPOSE] Farm Agent (:8889) -- image built locally from Gitea ├── [SWARM] Telegraf (global), Node-exporter (global) ├── [CRON] sync-frigate-to-nas (daily 2am), cleanup-frigate-media (weekly Sun 3am) ├── 5TB USB disk at /mnt/frigate-recordings (Frigate recordings storage) [Failover services remain on hf-pdocker-02/bart until Phase 2] bart (Worker, 15GB RAM, i5-4570T, HDD) -- ALSO HOSTING FAILOVER SERVICES ├── [COMPOSE] Media Stack (Jellyfin, Sonarr, Radarr, SABnzbd, NZBHydra2, Jellyseerr) ├── [COMPOSE] OpenVAS (vulnerability scanner) ├── [COMPOSE] Farm Agent (:8889) ├── [COMPOSE] Watchtower ├── [SWARM] Telegraf (global), Node-exporter (global) ├── [COMPOSE] Home Assistant (:8123) -- FAILOVER from hf-pdocker-01 ├── [COMPOSE] ESPHome (:6052) -- FAILOVER from hf-pdocker-01 ├── [COMPOSE] Mosquitto (:1883, :8883) -- FAILOVER from hf-pdocker-01 ├── [COMPOSE] Synapse/Matrix (:8008) + synapse-db (postgres) + wellknown (:8010) -- FAILOVER from hf-pdocker-01 ├── [COMPOSE] Guacamole (:8199) + guacd + guacamole-db (mysql, LOCAL /var/lib/guacamole-db) -- FAILOVER from hf-pdocker-01 ├── [COMPOSE] AI Agents (margaret, spirit, bob, clippy) -- FAILOVER from hf-pdocker-01 (2026-03-07) hf-pdocker-02 (Worker, 7.6GB RAM, i7-6700T, HDD) -- PRIMARY NODE DURING FAILOVER ├── [COMPOSE] Traefik (reverse proxy, ports 80/443/8090) -- FAILOVER from hf-pdocker-01 ├── [COMPOSE] Monitoring (Grafana :3001, Prometheus :9095, Alertmanager :9093, InfluxDB :8086, Blackbox :9115, matrix-receiver :9094) -- FAILOVER ├── [COMPOSE] AdGuard Home (:3000, :53) -- FAILOVER ├── [COMPOSE] Unifi + unifi-db (MongoDB) ├── [COMPOSE] Gitea + gitea-db (PostgreSQL) ├── [COMPOSE] Semaphore + semaphore-db (MySQL) ├── [COMPOSE] Wiki.js + wikijs-db (PostgreSQL) ├── [COMPOSE] Vaultwarden, Actual Budget, MCPO, Trivy ├── [COMPOSE] Farm Agent (:8889) ├── [COMPOSE] Watchtower ├── [SWARM] Telegraf (global), Node-exporter (global), Portainer Services restored to hf-pdocker-01 (post-recovery): - Frigate NVR (:5000) -- back on hf-pdocker-01 with GPU - Frigate-Notify v2 (:5199) -- deployed 2026-03-08 - Farm Agent (:8889) Services still on failover nodes (not yet returned): - Farm Manager API (not yet migrated back) ``` ## Working with This Repository ### Environment Context You are working in a **WSL 2 environment** on Windows. Docker is NOT directly available in WSL - all Docker operations must be performed by connecting to the remote nodes via SSH. **Critical:** Use the `docker-swarm-ops` agent (via Task tool) for ALL Docker operations. Do not attempt to run docker commands directly in bash - they will fail. ### Storage Architecture All service data lives on the **Synology NAS** via NFS, mounted at `/mnt/docker-data` on all 3 nodes. ``` /mnt/docker-data/ (NAS: 192.168.86.30:/volume1/Docker) ├── configs/ # Application configs ├── databases/ # Database data directories ├── media/ # Frigate recordings ├── traefik/ # Let's Encrypt certs └── compose/ # Standalone compose files per node ├── hf-pdocker-01/{frigate,frigate-notify}/ ├── hf-pdocker-02/{unifi,gitea,semaphore,wikijs,vaultwarden,actual-budget,mcpo,trivy,watchtower}/ └── bart/{media,openvas,watchtower}/ ``` **NFS mount (all nodes):** `192.168.86.30:/volume1/Docker /mnt/docker-data nfs rw,soft,timeo=300,intr,_netdev,nfsvers=3 0 0` **NFS + Databases:** Database containers on NFS require `user: "1024:100"` to avoid `chown` failures from NFS root_squash. UID 1024 is the NAS anonymous user. ### Repository File Structure ``` stacks/ # Swarm stack definitions (deployed on hf-pdocker-01) ├── {service-name}/ │ ├── docker-compose.yml │ └── .env (if needed) compose/ # Standalone Compose definitions (deployed per-node) ├── hf-pdocker-01/ # Frigate ├── hf-pdocker-02/ # Dev tools & utilities │ ├── {service}/docker-compose.yml │ └── {service}/.env └── bart/ # Media stack & OpenVAS ``` ### Deployment Workflow **Swarm services (stacks/):** 1. Edit stack file in `stacks/{service-name}/docker-compose.yml` 2. SCP to hf-pdocker-01 and redeploy: `docker stack deploy -c ` **Standalone Compose services (compose/):** 1. Edit compose file in `compose/{node}/{service}/docker-compose.yml` 2. SCP to NAS path or node, then: `cd /mnt/docker-data/compose/{node}/{service} && docker compose up -d` ### Service Architecture Patterns **Network Patterns:** - **Swarm services**: Overlay networks for inter-service communication - **Compose services**: Bridge networks for service isolation - **Public services** (Traefik): Connect to `traefik-public` overlay + Traefik labels **Database Patterns:** - PostgreSQL: `postgres:15-alpine` or `postgres:16-alpine` - MySQL: `mysql:8.0` (Guacamole, Semaphore) - MongoDB: `mongo:7.0` (Unifi) - **Always** add `com.centurylinklabs.watchtower.enable=false` label - **Always** add `user: "1024:100"` for NFS-backed database volumes **Volume Patterns:** - Application data: `/mnt/docker-data/configs/{service-name}/` - Database data: `/mnt/docker-data/databases/{service-name}-db/` - Traefik certs: `/mnt/docker-data/traefik/letsencrypt/` ## Key Services & Access ### Public Services (via Traefik on hf-pdocker-01) - Matrix: `mess.hobbs.farm` - Guacamole: `solo.hobbs.farm` - Home Assistant: `yoda.hobbs.farm` - Vaultwarden: `vault.hobbs.farm` - Gitea: `git.hobbs.farm` ### Internal Services by Node **hf-pdocker-01 (192.168.86.192):** - Frigate: :5000 - Frigate Alerts (v2): :5199 (web UI + REST API) - Home Assistant: :8123 - ESPHome: :6052 - Mosquitto: :1883 - Grafana: :3001 - AdGuard: :3000 - Traefik Dashboard: :8090 - Farm Manager: :8888 (dashboard + API) - Farm Agent: :8889 **hf-pdocker-02 (192.168.86.100):** - Farm Agent: :8889 - Unifi: :8443 - Gitea: :3003 (web), :2222 (SSH) - Semaphore: :3004 - Wiki.js: :3002 - Vaultwarden: :26226 - Actual Budget: :5006 - MCPO: :8001 - Trivy: :4954 **bart (192.168.86.167):** - Farm Agent: :8889 - Jellyfin: :8096 - Sonarr: :8989 - Radarr: :7878 - SABnzbd: :8082 - NZBHydra2: :5076 - Jellyseerr: :5055 - OpenVAS: :9392 - AI Agents (margaret :18790, spirit :18791, clippy :18792, bob :18800) -- loopback only, failover from hf-pdocker-01 **GPU Machine (192.168.86.196, RTX 3090):** - Ollama: :11434 (vision LLM, llava:13b) - clip-service: :5200 (CLIP embeddings, to be deployed) ### Port Conflict Notes Port conflicts already resolved: - Port 8000: Portainer (MCPO moved to 8001) - Port 3000: AdGuard Home (Gitea moved to 3003) - Port 3001: Grafana (Semaphore moved to 3004) - Port 5000: Frigate uses host networking (registry container stopped) ## Critical Configuration Preservation ### Encryption Keys & Passwords - Semaphore: `SEMAPHORE_ACCESS_KEY_ENCRYPTION` encrypts sensitive data - **Always** check for existing encryption keys before generating new ones ### Database Version Matching Match the PostgreSQL/MySQL version to existing data directory format. Check logs for version mismatch errors. ## Post-Change Checklist (MANDATORY) After ANY infrastructure change (deploy, remove, reconfigure, add service, change ports, modify Traefik routes), you MUST complete ALL of these before reporting done: 1. **CLAUDE.md** — Update service lists, ports, node assignments if changed 2. **MEMORY.md** — Add/update relevant memory entries 3. **Prometheus probes** — Add/remove blackbox targets, hot-reload Prometheus 4. **Wiki** — Launch documentation-keeper agent to update wiki pages (run in background) Do NOT wait for the user to ask. Do NOT skip "because the change was small." Every change gets documented. ## Common Operations ### Deploy/Update a Swarm Service 1. Edit `stacks/{service}/docker-compose.yml` 2. Use docker-swarm-ops agent to SCP and deploy: ```bash docker stack deploy -c ``` ### Deploy/Update a Compose Service 1. Edit `compose/{node}/{service}/docker-compose.yml` 2. Use docker-swarm-ops agent to: ```bash # On the target node: cd /mnt/docker-data/compose/{node}/{service} docker compose up -d ``` ### Troubleshoot ```bash # Swarm services (on hf-pdocker-01): docker service ls docker service logs {stack}_{service} --tail 100 # Compose services (on target node): docker compose -f /mnt/docker-data/compose/{node}/{service}/docker-compose.yml logs --tail 100 docker ps --format "table {{.Names}}\t{{.Status}}" ``` ### Check Cluster Health ```bash # On hf-pdocker-01: docker node ls # Swarm node status docker service ls # Swarm services docker stack ls # Deployed stacks # On each node: docker ps # Local containers (Compose + Swarm) ``` ## Data Management ### Storage All persistent data on NAS at `/mnt/docker-data/`: - `configs/` - Application configurations - `databases/` - Database volumes - `media/` - Frigate recordings - `traefik/` - SSL certificates ### NAS Dependency **Risk**: NAS failure = all services fail (single point of failure) - NFS mount uses `soft,timeo=300` for graceful timeout - Synology should have "Restart automatically after power failure" enabled - Original data backups remain on local `/data/configs/` on hf-pdocker-01 ## Specialized Agents ### docker-swarm-ops **Use for:** All Docker operations (Swarm + Compose) on any node ### vault-credential-manager **Use for:** Storing/retrieving credentials from Vaultwarden ### documentation-keeper **Use for:** Creating/updating documentation in Wiki.js ## Known Issues & Workarounds 1. **NFS root_squash**: Database containers need `user: "1024:100"` to avoid chown failures on NAS volumes 2. **Telegraf Docker Input**: Disabled due to Docker socket permission issues across nodes 3. **Watchtower API Version**: Requires `DOCKER_API_VERSION=1.44` 4. **InfluxDB Connection**: Telegraf may show timeout errors if InfluxDB is not running 5. **Frigate cameras**: Cameras on 192.168.44.x subnet may show RTSP timeouts (check camera network) 6. **Registry container**: Stopped on hf-pdocker-01 to free port 5000 for Frigate