Runbook: Deploy
Status: Stub — finalized in D1.
Deployment target is open question Q3 in the PRD. Candidates:
Option A — Hetzner sibling to jcortex
- VPS at Hetzner, x86_64, Ubuntu 22.04+
- Alongside voice-assistant (jcortex)
- Caddy for TLS; systemd for process supervision
- Domain:
picortex.globalbr.ai(Cloudflare A record, DNS-only) - Pros: Linux, cheap, full control; cgroups v2 + bubblewrap available
- Cons: one more server to babysit
Option B — Fly.io
- Fly app, multi-region or single
- Persistent volumes for chat home dirs
- Fly handles TLS
- Pros: minimal ops
- Cons: systemd-free (use supercronic), Linux user model is the same, but Fly's per-instance FS is ephemeral unless volume-backed; need to mount volumes for chat homes
Option C — HMA (Mac Mini)
- Jacob's always-on Mac Mini (
sdmm291) - Already runs many services; public access via Cloudflare Tunnel
- Pros: no extra server
- Cons: macOS doesn't have Linux users in the same way;
useradd/chmod 0700model has to use macOS dscl + dseditgroup, more awkward; cgroups v2 unavailable
Working assumption: Option A. Decide in D1.
Deploy script (draft, Option A)
# On laptop:
git push origin main
# On Hetzner VPS as root:
cd /opt/picortex
sudo -u picortex git pull
sudo -u picortex npm ci
sudo -u picortex npm run build
systemctl restart picortex
Systemd unit (draft)
[Unit]
Description=picortex
After=network.target
[Service]
User=picortex
WorkingDirectory=/opt/picortex
Environment=NODE_ENV=production
EnvironmentFile=/opt/picortex/.env
ExecStart=/usr/bin/node dist/server.js
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Caddy config (draft)
picortex.globalbr.ai {
reverse_proxy 127.0.0.1:7823
}
Health checks
https://picortex.globalbr.ai/healthreturns{status:"ok"}- Journalctl
journalctl -u picortex -f bd list --status=openfor blocker tickets
Rollback
git reset --hard <previous-sha> && systemctl restart picortex