# Deploying drinktracker ```bash ./deploy/deploy.sh # typecheck, push, build on the LXC, restart, verify ./deploy/deploy.sh --yes # skip the confirmation prompt ./deploy/deploy.sh --push # also publish the image to the Gitea registry ``` ## Where things actually live | | | |---|---| | Production LXC | `192.168.2.169` (hostname `drinktracker`) | | Deploy user | `drinkadmin`, key `~/.ssh/drinktracker_ed25519` | | Live compose dir | `/root/drinktracker` | | Image tag | `192.168.2.140:3000/jpscott84/drinktracker:latest` | | App port | 3000 (host network), unauthenticated requests return 307 → `/login` | Two traps worth knowing: **`drinktracker.tenseconddelay.net` is the reverse proxy (192.168.2.172), not the container.** `NEXTAUTH_URL` points at the public name, so resolving it and SSHing there fails confusingly — the key looks rejected because it's a different machine. **The two checkouts under `/home/drinkadmin/` are stale.** Only `/root/drinktracker` is live; that's what the running container's compose labels point to. ## How it works The image is built **on the LXC** and tagged with the registry name that `docker-compose.prod.yml` already references. Compose finds it locally and never pulls, so no registry credentials are needed on either machine. `--push` publishes it as well, which is only useful if something else pulls that tag. There is no CI, so **`git push` on its own deploys nothing** — it only updates the Gitea repo. The build and restart have to happen separately, which is what this script is for. ## One-time setup The script needs passwordless sudo on the LXC, because the deployment lives under `/root`. On the container as root: ```bash echo 'drinkadmin ALL=(root) NOPASSWD: ALL' > /etc/sudoers.d/drinkadmin-deploy chmod 440 /etc/sudoers.d/drinkadmin-deploy visudo -c ``` This grants less than it appears to: `drinkadmin` is already in the `docker` group, and anyone who can talk to the Docker socket can mount the host filesystem into a privileged container, so they are root-equivalent already. If you'd rather not, drop the sudoers file and run the deploy steps by hand from a root shell. **Alternative worth considering:** move the deployment out of `/root` into a `drinkadmin`-owned directory and skip sudo entirely. Keep the directory basename `drinktracker` so the compose project name — and therefore the existing container and volume names — stay the same. `.env.production` must be copied across intact or Postgres will reject the app's credentials, since the volume was initialised with the current password. ## Verifying by hand ```bash ssh -i ~/.ssh/drinktracker_ed25519 drinkadmin@192.168.2.169 sudo docker compose -f /root/drinktracker/docker-compose.prod.yml ps sudo docker logs drinktracker-app-1 --tail 50 sudo docker logs drinktracker-app-1 2>&1 | grep '\[switchboard\]' # AI routing + cost per call ``` Each AI call logs one `[switchboard] feature=… model=… cost=… latency_ms=…` line. `FAILOVER` warnings are expected — the gateway's `:batch` model variants currently fail on every request and fall back. ## Rollback ```bash git revert && ./deploy/deploy.sh ``` Or directly on the LXC, without touching git history: ```bash sudo git -C /root/drinktracker reset --hard cd /root/drinktracker sudo docker build -t 192.168.2.140:3000/jpscott84/drinktracker:latest . sudo docker compose -f docker-compose.prod.yml up -d --no-build ``` Note the `migrate` service runs `prisma db push --accept-data-loss` on every start, so a rollback that removes a column will drop it and its data. Take a database dump first if the schema changed.