Dockerfile, compose.yml, env template, and runbook for the second-brain web container. Targets herbys-dev (10.0.21.207); Traefik (file-provider on 10.0.11.20) reaches the published host port at 10.0.21.207:8080. Image: - python:3.12-slim + the official uv binary copied from the upstream image, plus apt-installed git + ca-certificates so the Gitea VCS pin for embedding-chunking resolves at build time. - uv sync --frozen --no-dev --no-install-project, source copy, then a second uv sync to install the project itself. Two-step so the lock install layer caches independently of source edits. - No ffmpeg / claude CLI / faster-whisper — web role doesn't need any of them. Extraction runs on the dev host's CLI; transcription on the tower. - Drops to uid 1000 (`app`) before CMD. Uvicorn binds 0.0.0.0:8000 inside the container, with --proxy-headers + --forwarded-allow-ips=* so Traefik's X-Forwarded-* survive. compose.yml: - Joins the existing external `homelab` bridge network so the container reaches homelab-postgres:5432 and ollama:11434 by service DNS. - Publishes the uvicorn port at 10.0.21.207:8080 (LAN IP bound, not 0.0.0.0) for Traefik on the separate VM to reach. NO traefik.* labels — file-provider Traefik can't read them. - env_file: .env (0600, gitignored) — SECOND_BRAIN_DATABASE_URL points at homelab-postgres:5432 (containerised), NOT the host's 127.0.0.1:5433 port-map. - restart: unless-stopped. README.md: - Build + bring-up commands. - SECURITY note: no SSO / no CSRF / mutating endpoints — Traefik route must be LAN-only for now (Travis's call). - The two infra steps Travis applies himself, with ready-to-paste snippets: - Knot DNS: brain.herbylab.dev → 10.0.11.20. - Traefik dynamic config: file-provider router + service block. - Verification checklist for both before-and-after-DNS states. Live-verified on herbys-dev: container Up, dashboard returns 200 with real status counts from petalbrain, settings save round-trip works, no tracebacks in logs.
160 lines
5.5 KiB
Markdown
160 lines
5.5 KiB
Markdown
# second-brain web app — deploy notes
|
|
|
|
Target host: **herbys-dev** (10.0.21.207). Runs the FastAPI + Jinja UI
|
|
under `docker compose`, on the existing `homelab` bridge network so it
|
|
can reach `homelab-postgres:5432` and `ollama:11434` by service DNS.
|
|
|
|
Traefik (file-provider VM at **10.0.11.20**) handles TLS + the public
|
|
hostname `brain.herbylab.dev`. The compose file does NOT carry
|
|
`traefik.*` labels — file-provider Traefik can't see them.
|
|
|
|
---
|
|
|
|
## ⚠ Security note (no SSO)
|
|
|
|
Travis opted **not** to put this behind Authentik for the initial roll-out.
|
|
The web app has **no authentication, no CSRF, no rate limiting**, and it
|
|
exposes mutating endpoints (accept/reject sources, edit pipeline settings).
|
|
|
|
→ The Traefik route should be reachable **from LAN / trusted networks
|
|
only**. If you ever publish this past Cloudflare for off-LAN access, gate
|
|
it behind the Authentik forward-auth middleware first, or add an
|
|
`auth_required` decorator to the FastAPI app.
|
|
|
|
---
|
|
|
|
## 1. One-time setup on herbys-dev
|
|
|
|
```bash
|
|
cd /opt/projects/homelab/second-brain/deploy/web
|
|
|
|
# Real env file — paste the lovebug password.
|
|
cp .env.example .env
|
|
chmod 0600 .env
|
|
$EDITOR .env
|
|
# SECOND_BRAIN_DATABASE_URL must use host=homelab-postgres (NOT 127.0.0.1).
|
|
# Password lives at /opt/backups/postgres-consolidation/credentials.env.
|
|
|
|
# Build + start. Compose will join the existing external `homelab` net.
|
|
docker compose up -d --build
|
|
|
|
# Confirm it's healthy.
|
|
docker compose ps
|
|
docker compose logs --tail 50
|
|
curl -fsS http://10.0.21.207:8080/dashboard | head
|
|
```
|
|
|
|
Day-to-day:
|
|
|
|
```bash
|
|
docker compose logs -f # tail
|
|
docker compose restart # bounce (config reload)
|
|
docker compose pull && docker compose up -d # if image were registry-hosted
|
|
git pull && docker compose up -d --build # build-on-host update path
|
|
```
|
|
|
|
---
|
|
|
|
## 2. INFRA STEPS — Travis applies these manually
|
|
|
|
The compose stack is *up* once `docker compose up -d --build` returns.
|
|
The **public URL** at `https://brain.herbylab.dev` only resolves after
|
|
the two steps below land on their respective infra. They live on
|
|
machines outside this repo's authority (Knot on the DNS box; Traefik on
|
|
10.0.11.20), so this README documents them rather than executing them.
|
|
|
|
### 2a. Knot DNS — A record for `brain.herbylab.dev`
|
|
|
|
Add this to the `herbylab.dev` zone file on the Knot host (file path /
|
|
include layout matches the existing per-service A records — pick the
|
|
spot used for the other `*.herbylab.dev` services):
|
|
|
|
```zone
|
|
brain.herbylab.dev. 300 IN A 10.0.11.20
|
|
```
|
|
|
|
300s TTL matches the existing convention. Reload Knot:
|
|
|
|
```bash
|
|
sudo knotc reload
|
|
dig +short brain.herbylab.dev @<knot-host> # should return 10.0.11.20
|
|
```
|
|
|
|
### 2b. Traefik file-provider router on 10.0.11.20
|
|
|
|
Add the snippet below to the Traefik dynamic config (same file the rest
|
|
of the `*.herbylab.dev` services live in — file-provider config picks
|
|
up changes without a restart):
|
|
|
|
```yaml
|
|
http:
|
|
routers:
|
|
second-brain:
|
|
rule: "Host(`brain.herbylab.dev`)"
|
|
entryPoints:
|
|
- websecure
|
|
tls:
|
|
certResolver: cloudflare
|
|
service: second-brain
|
|
# No middleware on day one. If/when SSO lands, prepend the
|
|
# authentik forward-auth chain here (see other routers for the
|
|
# shape) and remove the LAN restriction.
|
|
|
|
services:
|
|
second-brain:
|
|
loadBalancer:
|
|
servers:
|
|
- url: "http://10.0.21.207:8080"
|
|
passHostHeader: true
|
|
```
|
|
|
|
Verify on the Traefik host:
|
|
|
|
```bash
|
|
# Watch for config-reload events:
|
|
docker logs traefik --tail 20 -f
|
|
# Or hit the API if it's exposed:
|
|
curl -fsS http://localhost:8080/api/http/routers/second-brain@file
|
|
```
|
|
|
|
Once both 2a and 2b are in place, `https://brain.herbylab.dev/dashboard`
|
|
should resolve through Cloudflare → Traefik → 10.0.21.207:8080 → the
|
|
`second-brain-web` container.
|
|
|
|
---
|
|
|
|
## 3. Verification checklist
|
|
|
|
After `docker compose up -d --build`:
|
|
|
|
| Check | Command | Expect |
|
|
|---|---|---|
|
|
| Container up | `docker compose ps` | `second-brain-web` is `Up`, not restarting |
|
|
| Startup log clean | `docker compose logs --tail 50` | uvicorn `Started server process` + `Application startup complete`, no tracebacks |
|
|
| Local-host reachable | `curl -fsS http://10.0.21.207:8080/dashboard \| head -1` | `<!DOCTYPE html>` or similar |
|
|
| DB reachable | `curl -fsS http://10.0.21.207:8080/dashboard` | dashboard renders status counts (proves the homelab-net path to Postgres) |
|
|
| Settings round-trip | open `/settings` in a browser, save | green "Saved." flash + new `updated_at` |
|
|
|
|
After Travis's 2a + 2b infra steps:
|
|
|
|
| Check | Command | Expect |
|
|
|---|---|---|
|
|
| DNS | `dig +short brain.herbylab.dev` | `10.0.11.20` |
|
|
| TLS | `curl -fsS https://brain.herbylab.dev/dashboard \| head -1` | dashboard HTML |
|
|
| Cert | `openssl s_client -connect brain.herbylab.dev:443 -servername brain.herbylab.dev <<<''` | Cloudflare-issued cert |
|
|
|
|
---
|
|
|
|
## 4. Follow-ups (not built this round)
|
|
|
|
- **SSO via Authentik.** Add the existing forward-auth middleware to the
|
|
Traefik router once we want this off-LAN. Authentik lives at
|
|
10.0.21.207:9000 (same host as this container — different port).
|
|
- **CSRF.** Settings save is an HTMX POST with no token; trivial to
|
|
forge from a same-origin browser. Acceptable while LAN-restricted.
|
|
- **Image registry.** Currently build-on-host. Push to Gitea's container
|
|
registry (`gitea.plantbasedsoutherner.com/petal-power/second-brain-web`)
|
|
when there's a second box that needs to pull the image.
|
|
- **Dev-side extraction systemd timer.** Travis is running extraction
|
|
manually for now; the timer/cron path is deferred.
|