source: Klonkt/deploy/DEPLOY.md@ 2dd1dc4

main
Last change on this file since 2dd1dc4 was 2dd1dc4, checked in by Robin <roboburr@…>, 6 weeks ago

Scheid gebruikersdata van code voor self-hosters

Een instance bewaarde zijn database, uploads en .env binnen de checkout. Daardoor
bevatte de codemap levende gebruikersdata: opruimen bij een deploy kon uploads
raken, een back-up moest de data tussen de code vandaan vissen, en een tweede
site vroeg een tweede kopie van alles, inclusief node_modules, die apart
bijgewerkt moest worden.

Nu staat de code in /opt/klonkt en de data per instance in /var/lib/klonkt/<slug>.
De checkout is daarmee wegwerpbaar: weggooien en opnieuw klonen laat elke
instance intact. Een site toevoegen is een map plus een .env, zonder tweede
kopie van de code, en klonkt-update brengt ze in een keer allemaal naar de
nieuwe versie.

De systemd-template draait als de klonkt-gebruiker met ProtectSystem=strict en
ReadWritePaths op alleen de eigen datamap. Een instance kan dus niet in de code
schrijven en niet bij de data van een andere instance, ook niet als er in de app
iets misgaat.

Changed files:
scripts/install.sh

  • KLONKT_DATA_ROOT en KLONKT_SLUG toegevoegd, slug afgeleid van het domein
  • verse installatie zet data in /var/lib/klonkt/<slug> en start klonkt@<slug>
  • bestaande installaties met een .env in de checkout blijven ongemoeid, opnieuw draaien mag nooit een levende database verplaatsen
  • weigert bij een database in de checkout zonder .env, te dubbelzinnig
  • klonkt-update herstart voortaan elke instance, niet alleen klonkt.service

deploy/DEPLOY.md

  • sectie 9b: meerdere zelfstandige Klonkts naast elkaar, met verwijzing
  • onderscheid verduidelijkt met de bestaande multi-tenant sectie, die gaat over sites binnen een instance

New file:
deploy/klonkt@.service

  • systemd template-unit, een service per instance, gedeelde code

scripts/klonkt-migrate-data.sh

  • zet een bestaande installatie om, met --dry-run en een rollback-pad
  • weigert op code zonder src/config/paths.js, anders schrijft de app alsnog naast zijn eigen code

scripts/klonkt-add-instance.sh

  • nieuwe instance: datamap, .env met verse SESSION_SECRET en vrije poort, service en Caddy-blok

deploy/MULTI-INSTANCE.md

  • indeling, eigenaarschap en rechten, migratie, instances toevoegen, updaten, back-up en verwijderen

Nog te doen: dit is getest op de fleet en met een lokale rooktest, maar de
verse-installatiestap zelf is nog niet op een schone VPS gedraaid.

-robo
Co-Authored-By: Claude Opus 5 <noreply@…>

  • Property mode set to 100644
File size: 6.7 KB
Line 
1# Klonkt — Deployment Guide
2
3End-to-end production install on a fresh Ubuntu 22.04 / Debian 12 VPS
4(TransIP, Hetzner, etc.). Assumes a non-root user with sudo.
5
6---
7
8## 0. Prerequisites
9
10- VPS with Ubuntu 22.04 LTS or Debian 12.
11- DNS A/AAAA records for your domain pointing at the server's IP. Wait for
12 propagation (`dig +short YOUR-DOMAIN` should return the right IP) before
13 running certbot.
14- SSH access as a non-root user (e.g. `robin`).
15
16---
17
18## 1. System packages
19
20```bash
21sudo apt update && sudo apt upgrade -y
22sudo apt install -y curl ca-certificates git nginx ufw sqlite3
23```
24
25Install Node.js 20 LTS via NodeSource (don't use the OS-default — it's old):
26
27```bash
28curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
29sudo apt install -y nodejs
30node --version # should print v20.x.x
31```
32
33Install PM2 globally:
34
35```bash
36sudo npm install -g pm2
37```
38
39---
40
41## 2. Firewall
42
43```bash
44sudo ufw allow OpenSSH
45sudo ufw allow 'Nginx Full' # opens 80 + 443
46sudo ufw enable
47```
48
49---
50
51## 3. App user + project clone
52
53```bash
54# As root or via sudo, create a deploy user if you don't already have one
55# (skip if you're already on a non-root user)
56
57# As your user:
58mkdir -p ~/klonkt
59cd ~/klonkt
60# Clone or rsync your code here. e.g. via git:
61# git clone <your-repo> .
62
63npm ci --omit=dev
64```
65
66`ensureLocalHtmx()` in `server.js` will copy the bundled HTMX from
67`node_modules/htmx.org/dist/htmx.min.js` to `src/assets/js/htmx.min.js` on
68first boot. You don't have to do that step manually.
69
70---
71
72## 4. Environment variables
73
74Create `.env` in the project root:
75
76```ini
77NODE_ENV=production
78PORT=3000
79
80# 32+ random hex chars — required, the app refuses to boot without it.
81# Generate: openssl rand -hex 32
82SESSION_SECRET=<paste-strong-random-string>
83
84# Optional: pin the audio HMAC secret instead of letting the app generate one
85# in storage/.audio-secret. Generate the same way as SESSION_SECRET.
86# AUDIO_SECRET=<paste-different-random-string>
87
88# Optional: override storage paths
89# DATABASE_PATH=/home/robin/klonkt/storage/database.sqlite
90# AUDIO_PATH=/home/robin/klonkt/storage/audio
91# AVATAR_PATH=/home/robin/klonkt/storage/media/avatars
92# COVER_PATH=/home/robin/klonkt/storage/media/audio-covers
93# MEDIA_PATH=/home/robin/klonkt/storage/media
94```
95
96`chmod 600 .env` — keep it readable only by your user.
97
98---
99
100## 5. First boot
101
102```bash
103npm run migrate # creates storage/database.sqlite + tables
104```
105
106Then start with PM2:
107
108```bash
109pm2 start ecosystem.config.cjs --env production
110pm2 save
111pm2 startup # follow the printed command to make PM2 survive reboots
112```
113
114Check it's up:
115
116```bash
117curl -I http://127.0.0.1:3000/ # expect 200 / 302
118pm2 logs klonkt # live logs; Ctrl-C to detach
119```
120
121Register your first user (becomes god) by visiting
122`http://YOUR-SERVER-IP:3000/auth/register` BEFORE you point nginx at it
123(or just wait until SSL is up — registration works the same).
124
125---
126
127## 6. nginx + SSL
128
129```bash
130sudo cp deploy/nginx.conf.example /etc/nginx/sites-available/klonkt
131sudo $EDITOR /etc/nginx/sites-available/klonkt # replace <YOUR-DOMAIN>
132sudo ln -s /etc/nginx/sites-available/klonkt /etc/nginx/sites-enabled/
133sudo rm -f /etc/nginx/sites-enabled/default
134sudo nginx -t
135sudo systemctl reload nginx
136```
137
138Install certbot and provision certs:
139
140```bash
141sudo apt install -y certbot python3-certbot-nginx
142sudo mkdir -p /var/www/letsencrypt
143sudo certbot --nginx -d YOUR-DOMAIN -d www.YOUR-DOMAIN
144```
145
146Certbot edits the nginx config in place to wire in the certificate paths.
147Renewals run automatically via the certbot systemd timer; verify with:
148
149```bash
150sudo systemctl status certbot.timer
151```
152
153Now visit `https://YOUR-DOMAIN/`. You should see your site over HTTPS, with
154HSTS active.
155
156---
157
158## 7. Backups
159
160```bash
161chmod +x deploy/backup.sh
162mkdir -p ~/backups/klonkt ~/klonkt/logs
163
164# Test it once
165./deploy/backup.sh
166
167# Schedule nightly at 03:00
168( crontab -l 2>/dev/null ; \
169 echo "0 3 * * * /home/$USER/klonkt/deploy/backup.sh >> /home/$USER/klonkt/logs/backup.log 2>&1" \
170) | crontab -
171
172crontab -l # verify
173```
174
175Restore is a tar -xzf into a clean directory + `npm ci` + start.
176
177---
178
179## 8. Updating
180
181```bash
182cd ~/klonkt
183git pull
184npm ci --omit=dev
185pm2 reload klonkt # zero-downtime within fork mode
186```
187
188The DB schema migrates automatically on boot (`ensureColumn` adds new columns
189idempotently). For destructive changes you'd need to write an explicit
190migration — not yet needed.
191
192---
193
194## 9. Multi-tenant setup
195
196After your first user/site is created (auto on first registration), use
197`/admin/sites` to create more sites. Each site gets its own URL prefix
198(`/sites/<slug>/`) and its own PWA scope, so installing the PWA from one
199site won't navigate into another.
200
201These sites live inside one Klonkt, sharing one database and one process. For
202separate domains with separate databases, see the next section instead.
203
204---
205
206## 9b. Several independent Klonkts on one server
207
208A different question from the one above: not several sites inside one Klonkt,
209but several Klonkts side by side, each with its own domain, database and
210uploads, all sharing a single copy of the code.
211
212That layout keeps user data out of the checkout:
213
214```
215/opt/klonkt/ the code, shared by every instance
216/var/lib/klonkt/<slug>/ one instance: .env, database, uploads
217```
218
219Adding a site is then a directory plus an `.env`, and `klonkt-update` moves all
220of them to the new code in one step. Existing single site installs can be
221converted with `scripts/klonkt-migrate-data.sh`.
222
223See **[MULTI-INSTANCE.md](MULTI-INSTANCE.md)** for the layout, the migration,
224the file ownership and the backup routine.
225
226---
227
228## 10. Troubleshooting
229
230| Symptom | Check |
231|---|---|
232| `❌ FATAL: SESSION_SECRET is required` | `.env` missing or unreadable. `pm2 stop klonkt && pm2 start ecosystem.config.cjs --env production`. |
233| `❌ FATAL: SESSION_SECRET too weak for production` | Make it 32+ chars: `openssl rand -hex 32`. |
234| WS disconnects every minute | Check nginx `proxy_read_timeout` is ≥ 90s in `/ws/` block. |
235| Audio plays but seek stutters | nginx must have `proxy_buffering off` on `/audio/stream/`. |
236| 502 from nginx | `pm2 list` — is the app up? `pm2 logs klonkt --lines 100`. |
237| HTMX 404s on `/assets/js/htmx.min.js` | `ls node_modules/htmx.org/dist/htmx.min.js` — if missing, `npm install htmx.org`. The boot-copy step needs the package. |
238
239---
240
241## 11. What's NOT included
242
243- Email sending (password reset prints the URL to the console / page in
244 non-production). Hook up an SMTP or transactional service when needed.
245- Cluster mode / horizontal scaling. Single fork only — see comments in
246 `ecosystem.config.cjs` for what would have to change first.
247- Off-site backup replication. The local rotation keeps 14 days; copy the
248 tar.gz files off-server with rsync/restic/whatever you prefer.
Note: See TracBrowser for help on using the repository browser.