Files
hobbs-iac/CLAUDE.md
T
jeremy de1d9e1d93 infra: full IaC state after hf-pdocker-01 rebuild (2026-03-08)
Replaces initial Nomad/Consul/Terraform skeleton with current Docker-based
infrastructure across all three nodes (hf-pdocker-01, hf-pdocker-02, bart).

Key changes:
- CLAUDE.md: updated service distribution reflecting hf-pdocker-01 restored
- docs/plans/2026-03-08-hf-pdocker-01-rebuild.md: new rebuild plan doc
- compose/bart/: ai-agents, guacamole, matrix, media, openvas, smarthome, watchtower
- compose/hf-pdocker-02/: traefik, monitoring, adguard, gitea, semaphore, wikijs,
  unifi, vaultwarden, actual-budget, mcpo, netclaw, trivy, watchtower, n8n
- compose/hf-pdocker-01/: frigate, frigate-notify, farm-agent
- stacks/: all Swarm stack definitions (monitoring, traefik, smarthome, matrix,
  guacamole, media, ai-agents, frigate, gitea, semaphore, wikijs, unifi, etc.)
- .gitignore: exclude .env credential files and acme.json certs
- .env.example: placeholder credential files for all services
- .claude/agents/: gitea-backup and other infrastructure agent configs
- scripts/: frigate media cleanup and NAS sync scripts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 18:51:25 -05:00

11 KiB

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 <file> <stack-name>

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:
    docker stack deploy -c <compose-file> <stack-name>
    

Deploy/Update a Compose Service

  1. Edit compose/{node}/{service}/docker-compose.yml
  2. Use docker-swarm-ops agent to:
    # On the target node:
    cd /mnt/docker-data/compose/{node}/{service}
    docker compose up -d
    

Troubleshoot

# 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

# 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