Files
drinktracker/.claude/skills/deploy-drinktracker/SKILL.md
JP 2beb2d5d43 Version the deploy skill with the repo
.claude/ was ignored wholesale, which meant the deploy skill lived only
on one machine. Narrow the rule so .claude/skills/ is tracked while
settings.local.json stays local.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-08 17:02:51 +00:00

3.3 KiB

name, description
name description
deploy-drinktracker 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.