--- name: deploy-drinktracker description: Deploy drinktracker to the production LXC and verify it came back healthy. Use when asked to deploy, ship, release, push to prod, or update the running app - and after merging changes that need to go live. Also covers rollback and diagnosing a deploy that did not take effect. --- # Deploying drinktracker Run `./deploy/deploy.sh` from the repo root. It typechecks, pushes to Gitea, rebuilds the image on the LXC, restarts the stack and polls until the app answers. Use `--yes` to skip the confirmation prompt when the user has already approved the deploy. `deploy/README.md` has the full topology table, the one-time sudoers setup, and rollback commands. Read it before improvising — the layout is unusual in ways that waste time. ## Facts that are not discoverable from the repo - **The LXC is `192.168.2.169`.** `drinktracker.tenseconddelay.net` (and `NEXTAUTH_URL`) resolve to the reverse proxy at `192.168.2.172`. SSHing there fails with what looks like a rejected key but is simply the wrong machine. - **The live stack is `/root/drinktracker`.** The checkouts under `/home/drinkadmin/` (`drinktracker`, `drinktracker-latest`) are stale June copies. Confirm with `docker inspect drinktracker-app-1 --format '{{index .Config.Labels "com.docker.compose.project.working_dir"}}'`. - **There is no CI.** No Gitea Actions, no GitHub workflows. `git push` updates the repo and nothing else; the image must be built and the stack restarted separately. If someone reports "I pushed but nothing changed", this is why. - **Access:** `ssh -i ~/.ssh/drinktracker_ed25519 drinkadmin@192.168.2.169`. Root SSH is disabled. `drinkadmin` needs the sudoers drop-in from `deploy/README.md` for the script to work unattended. ## Verifying An unauthenticated request to port 3000 returns **307** (redirect to `/login`) — that is a healthy response, not a failure. The script accepts 200, 302 and 307. Each AI call emits one line: ``` sudo docker logs drinktracker-app-1 2>&1 | grep '\[switchboard\]' ``` `FAILOVER` lines are expected and harmless: the gateway's `:batch` model variants fail on every request and fall back. `CONTEXT OVERFLOW` is worth investigating — it means a request was too large for any model's window and may have been truncated. ## Before deploying schema changes The `migrate` service runs `prisma db push --skip-generate --accept-data-loss` on every start. Removing a field from `schema.prisma` therefore **drops the column and its data** on the next deploy, in both directions — so a rollback past a schema change is destructive too. Dump the database first when the schema changed: ``` sudo docker exec drinktracker-db-1 pg_dump -U drinktracker drinktracker > ~/dt-$(date +%F).sql ``` This is also why `UserPreference.defaultProvider` is still in the schema despite being unused: deleting it would drop the column and break restoring older backups. ## If the app does not come back ``` sudo docker compose -f /root/drinktracker/docker-compose.prod.yml ps sudo docker logs drinktracker-app-1 --tail 100 ``` Most likely causes, in order: the build failed and compose restarted the previous image; `.env.production` is missing a variable the new code reads (compare against `.env.example`); or the `migrate` service exited non-zero and `app` never started, which `ps` will show as `migrate` in a non-zero exited state.