# Deployment notes — Family Home Lab ## Domains - Public zone `lab.audasmedia.com.au` → `144.6.86.11` (router) → `.35` Caddy. - **No Pi-hole local records needed.** `*.home.lab` is deprecated (not a registered TLD). ## Caddy (.35) 1. SSH: `ssh sam@192.168.20.35` 2. Locate the running Caddy config (Docker container on .35). 3. Append `deploy/caddy/Caddyfile.snippet` contents to the Caddyfile. 4. Reload: `docker exec caddy reload --config /etc/caddy/Caddyfile` 5. Verify Caddy can obtain Let's Encrypt certs (ports 80/443 forwarded on router — already working for omniroute/gitea domains). ## Host (.13) 1. Compose project: `/home/sam/Docker/Containers/family-home-lab/` (created during build). 2. Data: `/mnt/data/family-home-lab/` (garage-data, garage-meta, shared-media, dsh/). 3. Backup: add `/mnt/data/family-home-lab/` to Borg sources in `/etc/nixos/backup.nix`. ## Ports (on .13) | Port | Service | |---|---| | 8500 | Portal (FastAPI) | | 8487 | Photopea (→ container 8887) | | 8083 | Video editor (kdenlive) — 8081 was taken by airflow-webserver | | 8084 | Audio editor (audacity) — 3000 was taken by a NixOS service | | 3900/3902 | Garage (S3/admin) | | 3081–3084 | dsh instances (other agent) | ## Order of operations 1. Build portal + compose stack (this repo) → `docker compose up -d` on .13 2. Apply Caddy snippet on .35 3. Test `https://console.lab.audasmedia.com.au` 4. Deploy tool containers one at a time, verifying each URL 5. dsh agent deploys its instances and coordinates token handoff ## First run (portal) Portal auto-creates the DB tables and, if the users table is empty, seeds the admin from `ADMIN_USERNAME`/`ADMIN_PASSWORD` in `.env` (Sam, by default). ```bash cd /home/sam/Docker/Containers/family-home-lab docker compose up -d # first admin already created on startup; create the rest in the UI at /admin ``` ## Garage provisioning (S3 buckets + access keys) After Garage is up, create buckets/keys from inside the container: ```bash # env for the garage CLI G=('docker compose exec -T garage garage --config /etc/garage.toml') # main service key for the portal KEY_ID=$($G key import --name portal - <<< "$(cat .env | grep S3_ACCESS | cut -d= -f2)") # buckets for b in sam jo harry finn shared-media; do $G bucket create "$b"; done # allow the portal key to access every bucket for b in sam jo harry finn shared-media; do $G bucket allow --read --write "$b" --key portal; done ``` > Symmetry: ensure S3_ACCESS_KEY/S3_SECRET_KEY in `.env` match what `key import` > registers, else portal uploads will 403. ## ON/OFF service-controller (ON_OFF.md) Host-side on/off API on `.13`, port **8443** (chosen because it is already in the NixOS `networking.firewall.allowedTCPPorts` allowlist — 8091/8092 were taken by langgraph-service / photo-dashboard, and 8099 was blocked by the firewall). Files live in `deploy/service-controller/` and copy to `/home/sam/service-controller/` on .13. Install / update: ```bash # on .27 (this repo), then: scp -q deploy/service-controller/service_controller.py \ deploy/service-controller/services.toml \ deploy/service-controller/run.sh \ deploy/service-controller/family-service-controller.service \ sam@192.168.20.13:/home/sam/service-controller/ # on .13: cd /home/sam/service-controller python3 -m venv .venv # first time only ./.venv/bin/pip install -q -r requirements.txt # first time only cp family-service-controller.service ~/.config/systemd/user/ systemctl --user daemon-reload systemctl --user enable --now family-service-controller.service ``` Secrets: - `SC_TOKEN` lives in `/home/sam/.config/environment.d/10-secrets.conf` (git-ignored; run.sh sources it). - The portal gets `SC_TOKEN` from its `.env` on .13 (injected at deploy; never committed). Firewall note: the controller binds `0.0.0.0:8443` and is protected by the token. Binding only to the docker bridge gateway was dropped because this NixOS box drops INPUT from containers to non-allowlisted host listeners; `0.0.0.0` on the already- allowed port 8443 is the workable, token-gated compromise. Portal wiring: `docker-compose.yml` sets `SC_URL=http://host.docker.internal:8443` and `SC_TOKEN=${SC_TOKEN:-}` on the portal service, with `extra_hosts: ["host.docker.internal:192.168.144.1"]` (host-gateway resolves to the DOWN docker0 bridge on this box; pinning to the fhl-net gateway works). Verify: ```bash curl -s http://127.0.0.1:8443/health # {"ok":true,...} curl -s -H "X-Controller-Token: $SC_TOKEN" http://127.0.0.1:8443/services curl -s -X POST -H "X-Controller-Token: $SC_TOKEN" http://127.0.0.1:8443/services/lmms/stop ```