# Farm Manager — Design Document **Date**: 2026-03-05 **Status**: Approved ## Overview A web-based dashboard for managing Docker services across the 3-node homelab cluster. Provides full control (start/stop/restart/logs/pull) with service grouping for bulk operations. ## Architecture Three components: 1. **Dashboard** — Static HTML/CSS/JS served by the API server. Dark theme, single page with service cards, group management, and slide-out log viewer. 2. **API Server** — Python FastAPI on hf-pdocker-01:8888. Serves frontend, proxies Docker commands to node agents, manages group definitions in JSON. 3. **Node Agent** — Python FastAPI on each node:8889. Mounts Docker socket, exposes REST API for container operations. Auto-detects Swarm vs Compose containers. ``` Browser → Dashboard (hf-pdocker-01:8888) ↓ API Server ↙ ↓ ↘ Agent Agent Agent (01:8889) (02:8889) (bart:8889) ↓ ↓ ↓ Docker Docker Docker Socket Socket Socket ``` ## API Endpoints ### API Server (hf-pdocker-01:8888) | Method | Path | Description | |--------|------|-------------| | GET | `/api/nodes` | List all nodes with health status | | GET | `/api/services` | All containers across all nodes | | POST | `/api/services/{node}/{container}/start` | Start container | | POST | `/api/services/{node}/{container}/stop` | Stop container | | POST | `/api/services/{node}/{container}/restart` | Restart container | | GET | `/api/services/{node}/{container}/logs?tail=100` | Stream logs | | POST | `/api/services/{node}/{container}/pull` | Pull latest image | | GET | `/api/groups` | List all groups | | POST | `/api/groups` | Create group | | PUT | `/api/groups/{id}` | Edit group | | DELETE | `/api/groups/{id}` | Delete group | | POST | `/api/groups/{id}/start` | Start all services in group | | POST | `/api/groups/{id}/stop` | Stop all services in group | | POST | `/api/groups/{id}/restart` | Restart all services in group | ### Node Agent (each node:8889) | Method | Path | Description | |--------|------|-------------| | GET | `/containers` | List all containers with status | | POST | `/containers/{id}/start` | Start | | POST | `/containers/{id}/stop` | Stop | | POST | `/containers/{id}/restart` | Restart | | GET | `/containers/{id}/logs?tail=100` | Get logs | | POST | `/containers/{id}/pull` | Pull latest image | | GET | `/health` | Agent health check | ## Frontend UI - **Sidebar**: Node list with health dots, group list with expand/collapse, "All Services" view, group management button - **Main area**: Grid of service cards (name, status badge, node, image, uptime, action buttons) - **Log viewer**: Slide-out right panel, last 200 lines, auto-scroll - **Group actions**: Toolbar with Start All / Stop All / Restart All, confirmation modal for destructive actions - **Group editor**: Modal with name field and checkbox service list - **Styling**: Dark theme, custom CSS variables, no framework ## Data Storage ### Groups (`/mnt/docker-data/configs/farm-manager/groups.json`) ```json { "groups": [ { "id": "ai-agents", "name": "AI Agents", "services": [ {"node": "hf-pdocker-01", "container": "ai-agents_margaret"}, {"node": "hf-pdocker-01", "container": "ai-agents_spirit"}, {"node": "hf-pdocker-01", "container": "ai-agents_bob"}, {"node": "hf-pdocker-01", "container": "ai-agents_clippy"} ] } ] } ``` ### Node config (`config.json`) ```json { "nodes": [ {"name": "hf-pdocker-01", "host": "192.168.86.192", "agent_port": 8889}, {"name": "hf-pdocker-02", "host": "192.168.86.100", "agent_port": 8889}, {"name": "bart", "host": "192.168.86.167", "agent_port": 8889} ] } ``` ### Default Groups | Group | Services | |-------|----------| | AI Agents | margaret, spirit, bob, clippy | | Smart Home | homeassistant, esphome, mosquitto | | Monitoring | grafana, prometheus, alertmanager, influxdb, blackbox-exporter | | Media | jellyfin, sonarr, radarr, sabnzbd, nzbhydra2, jellyseerr | | Matrix | synapse, synapse-db, wellknown | | Guacamole | guacamole, guacd, guacamole-db | | Dev Tools | gitea, gitea-db, semaphore, semaphore-db, wikijs, wikijs-db | | Security | vaultwarden, trivy, openvas | | Network | traefik, adguard, unifi, unifi-db | | Frigate | frigate, frigate-notify | ## Deployment ### Node Agent (all 3 nodes) - Path: `/mnt/docker-data/compose/{node}/farm-agent/docker-compose.yml` - Image: built once, pushed to registry at 127.0.0.1:5050 - Mounts: `/var/run/docker.sock` - Port: 8889 - mem_limit: 128M ### API Server + Dashboard (hf-pdocker-01) - Path: `/mnt/docker-data/compose/hf-pdocker-01/farm-manager/docker-compose.yml` - Port: 8888 - Config: `/mnt/docker-data/configs/farm-manager/` - mem_limit: 256M ### Swarm Handling Node agent auto-detects Swarm-managed containers and uses `docker service` API for Swarm services, `docker` API for Compose containers. ## Repo - Gitea: `jeremy/farm-manager` (git.hobbs.farm) - Structure: ``` farm-manager/ ├── agent/ │ ├── main.py │ ├── docker_ops.py │ ├── requirements.txt │ └── Dockerfile ├── server/ │ ├── main.py │ ├── routes/ │ ├── static/ │ │ ├── index.html │ │ ├── style.css │ │ └── app.js │ ├── requirements.txt │ └── Dockerfile ├── tests/ │ ├── test_agent.py │ ├── test_server.py │ └── conftest.py ├── config.json ├── groups.json └── README.md ``` ## Testing | Layer | Scope | Tool | |-------|-------|------| | Unit | Agent Docker operations, group CRUD, API proxy logic | pytest + pytest-asyncio | | Integration | API server ↔ mock agent, group persistence | pytest + httpx TestClient | | Contract | Agent response schemas match dashboard expectations | pydantic models | ## Monitoring - Prometheus blackbox probes: `:8888/health` and `:8889/health` on each node - No auth (internal LAN only)