Files
family_home_lab/deploy/DEPLOYMENT.md

4.6 KiB
Raw Blame History

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> 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).

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:

# 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:

# 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:

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