5.3 KiB
5.3 KiB
CannaBuddy server runbook (cannabuddy.eu / 181.214.99.11)
Operational guide for the new production VPS. Secrets live in
passwords.md (this private repo) — this file is the how, not the what.
Decision: start the new cloud from scratch — NO migration of
DB/users/devices/measurements. Members re-register, the board re-flashes.
Only Forgejo + its data migrates from the old Netcup box.
Access (SSH is key-only — password login disabled)
- Claude (automation):
ssh -i ~/.ssh/id_ed25519_cannabuddy root@181.214.99.11 - KJ desktop:
ssh root@181.214.99.11(default key~/.ssh/id_ed25519) - KJ Termius: import
~/.ssh/id_ed25519, host181.214.99.11userroot - Fallback if locked out: the VPS provider's web console (password still set, but SSH password auth is OFF).
- Pipe local scripts to the box without quoting pain:
ssh … 'bash -s' < script.sh
The box
- Debian 12 (bookworm), 4 vCPU (Ryzen 9 7950X), 8 GB RAM, ~99 GB disk.
- IPv4
181.214.99.11, IPv62a0f:5707:aaf1:210c::1. Domain in Cloudflare (proxied = WAF).
Users / who runs what (proper-Linux: nothing app-level as root)
| Service | runs as | unit |
|---|---|---|
| Caddy (reverse proxy/TLS) | caddy |
systemctl … caddy |
| PostgreSQL/TimescaleDB | postgres |
systemctl … postgresql |
| Mosquitto (MQTT) | mosquitto |
systemctl … mosquitto |
| The app (Astro SSR + bridge) | hbapp (uid 1000, home /opt/cannabuddy, login shell for setup) |
pm2 (to become a systemd unit via pm2 startup systemd -u hbapp) |
- Run app commands as the service user:
sudo -u hbapp -H bash -lc '…'. - App code will live in
/opt/cannabuddy/cloud(the cloud repo checkout).
Installed + where
- Caddy 2.11 (apt, cloudsmith repo) — config
/etc/caddy/Caddyfile(mirror indeploy/Caddyfilehere). HTTP/1+2+3, security headers, static CF Origin cert. - Node 22 + pm2 + pnpm (NodeSource apt;
corepack enablegives pnpm). - TimescaleDB 16 (2.27.1) on PostgreSQL 16 (PGDG apt). DB
cannabuddy, rolehbcloud. Tuned bytimescaledb-tune. Listens localhost only. - Mosquitto 2.0.11 (apt) — stopped until
mosquitto-go-auth(Postgres backend) is configured (localhost WS listener:8083, plain:1883for the local bridge). - Hardening:
ufw(allow 22, 80, 443 tcp + 443 udp; default-deny),fail2ban(sshd),unattended-upgrades.
TLS
- Cloudflare Origin Certificate at
/etc/ssl/cannabuddy/origin.{crt,key}— dir750 root:caddy, key640, cert644. Valid to 2041, no renewal. - Cloudflare SSL/TLS mode must be Full (strict).
- Gotcha learned: the cert dir must be group-
caddy+ traversable (750), not just the files.
Domains / Caddy routes (all behind Cloudflare proxy)
| Host | → |
|---|---|
cannabuddy.eu, www |
static landing /var/www/cannabuddy-landing |
app.cannabuddy.eu |
Astro SSR 127.0.0.1:7502 (+ /mqtt → Mosquitto WS :8083) |
admin.cannabuddy.eu |
same app :7502 (lock behind Cloudflare Access later) |
git.cannabuddy.eu |
Forgejo :3000 |
status.cannabuddy.eu |
Uptime Kuma :3001 |
- Tenants are resolved by login (pooled DB +
tenant_id), NOT by subdomain.
Secrets / config
/etc/cannabuddy/cloud.env(root:hbapp640) — holdsDATABASE_URL(+ to add:BETTER_AUTH_SECRET, SendKit key, R2 creds, MQTT auth secret). pm2 ecosystem loads it.- All canonical secret values:
passwords.md(this repo).
Current status (2026-05-30)
Live: hardening · Caddy (HTTPS, landing at https://cannabuddy.eu) · TimescaleDB DB+role · Mosquitto installed · hbapp user. app. 502s until the app deploys.
NEXT steps (resume here)
- Deploy app as
hbapp: clone cloud repo →/opt/cannabuddy/cloud,pnpm install, write env (DATABASE_URL +openssl rand -hex 32for BETTER_AUTH_SECRET + SendKit),pnpm build, run drizzle migrations,pm2 start(cluster) +pm2 startup systemd -u hbapp+pm2 save. →app.cannabuddy.eugreen. - New-architecture code (in the repo):
tenant_idonmeasurements+ Timescale compression policy (≥7d);device_credentialstable;/api/provisionissues per-device opaque tokens + per-tenant device quota; swap Resend → SendKit (add SPF/DKIM/DMARC DNS). - Mosquitto auth: install/build
mosquitto-go-auth, Postgres backend againstdevice_credentials, ACLtele/<mac>/#pub +cmd/<mac>/#sub; enable WS listener127.0.0.1:8083; start mosquitto. - FW relay/bridge (as hbapp, pm2): subscribe
tele/#→ insert into TimescaleDB stampingtenant_id; publishcmd/<mac>/ota+cmd/<mac>/factory-reset. - Forgejo: install on box → copy data dir + DB from Netcup →
git.cannabuddy.eu; verify; then decommission old. - Uptime Kuma →
status.cannabuddy.eu. Backups: nightlypg_dump→ R2 (EU endpoint) + tested restore. - Validate E2E with a test board → tag v1.0.0 → re-flash the real board + give the 2 members the new link.
Handy commands
# service status
systemctl status caddy postgresql mosquitto --no-pager
# reload caddy after editing /etc/caddy/Caddyfile
caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile && systemctl reload caddy
# psql
sudo -u postgres psql -d cannabuddy
# run as the app user
sudo -u hbapp -H bash -lc 'cd /opt/cannabuddy/cloud && pm2 ls'
# firewall
ufw status verbose