source: Klonkt/scripts/install.sh@ ccaa530

main
Last change on this file since ccaa530 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: 16.2 KB
RevLine 
[83088b66]1#!/usr/bin/env bash
2#
[bb42dfb]3# Klonkt — installer for a Debian/Ubuntu VPS.
4# Installs Node 20, Caddy (automatic HTTPS) and Klonkt as a systemd service.
[83088b66]5#
[bb42dfb]6# Safe on a server that ALREADY runs things: it won't upgrade your system Node,
7# auto-picks a free port, and skips Caddy if a webserver/reverse-proxy is already
8# listening on port 80/443 (you then get instructions to put Klonkt behind your
9# own proxy).
[83088b66]10#
[bb42dfb]11# Usage (as root), non-interactive:
[83088b66]12# curl -fsSL https://raw.githubusercontent.com/roboburr/klonkt/main/scripts/install.sh \
[bb42dfb]13# | sudo bash -s -- --domain klonkt.example.com
14# Or interactively from a downloaded file:
[83088b66]15# sudo bash install.sh
16#
[bb42dfb]17# Re-running on the same server = update (git pull + restart).
18# Fully isolated alternative: Docker (see docker-compose.yml in the repo).
[83088b66]19#
20set -euo pipefail
21
[bb42dfb]22# ── Settings (override via env var or flag) ────────────────────────────────
23KLONKT_REPO="${KLONKT_REPO:-https://github.com/roboburr/klonkt.git}"
[0343a7a]24# `stable` = the release channel: it only moves forward to a version that has been verified,
25# so a self-host auto-update (klonkt-update) never pulls work-in-progress. Use `--branch main`
26# for the bleeding-edge dev branch instead.
[f882048]27KLONKT_BRANCH_SET="${KLONKT_BRANCH:+1}" # channel chosen via env? (empty = no, "1" = yes)
[0343a7a]28KLONKT_BRANCH="${KLONKT_BRANCH:-stable}"
[83088b66]29KLONKT_DIR="${KLONKT_DIR:-/opt/klonkt}"
[2dd1dc4]30# Where instance data lives, one directory per slug. The code in KLONKT_DIR is
31# shared; everything an instance writes stays under here.
32KLONKT_DATA_ROOT="${KLONKT_DATA_ROOT:-/var/lib/klonkt}"
33# Short name for this instance: its directory under the data root and its
34# systemd unit (klonkt@<slug>). Derived from the domain when left empty.
35KLONKT_SLUG="${KLONKT_SLUG:-}"
[83088b66]36KLONKT_USER="${KLONKT_USER:-klonkt}"
37KLONKT_PORT="${KLONKT_PORT:-3000}"
38KLONKT_DOMAIN="${KLONKT_DOMAIN:-}"
39KLONKT_LANG="${KLONKT_DEFAULT_LANG:-}"
40NODE_MAJOR="${NODE_MAJOR:-20}"
[bb42dfb]41NO_CADDY="${KLONKT_NO_CADDY:-}" # set to 1 to NEVER install Caddy (own proxy)
42NODE_FORCE="${NODE_FORCE:-}" # set to 1 to (re)install system Node anyway
[83088b66]43PORT_EXPLICIT=0
[f882048]44BRANCH_EXPLICIT="${KLONKT_BRANCH_SET:-0}" # 1 = operator chose the channel (env or --branch)
[83088b66]45
46while [ $# -gt 0 ]; do
47 case "$1" in
48 --domain) KLONKT_DOMAIN="$2"; shift 2;;
49 --repo) KLONKT_REPO="$2"; shift 2;;
[f882048]50 --branch) KLONKT_BRANCH="$2"; BRANCH_EXPLICIT=1; shift 2;;
[83088b66]51 --dir) KLONKT_DIR="$2"; shift 2;;
52 --port) KLONKT_PORT="$2"; PORT_EXPLICIT=1; shift 2;;
53 --lang) KLONKT_LANG="$2"; shift 2;;
54 --no-caddy) NO_CADDY=1; shift;;
55 --force-node) NODE_FORCE=1; shift;;
56 -h|--help) grep '^#' "$0" | sed 's/^# \{0,1\}//'; exit 0;;
[bb42dfb]57 *) echo "Unknown option: $1" >&2; exit 1;;
[83088b66]58 esac
59done
60
61log() { printf '\n\033[1;33m▸ %s\033[0m\n' "$*"; }
62ok() { printf '\033[1;32m ✓ %s\033[0m\n' "$*"; }
63warn() { printf '\033[1;33m ! %s\033[0m\n' "$*"; }
64die() { printf '\033[1;31m✗ %s\033[0m\n' "$*" >&2; exit 1; }
65as_klonkt() { runuser -u "$KLONKT_USER" -- env HOME="$KLONKT_DIR" "$@"; }
66port_busy() { ss -ltnH 2>/dev/null | awk '{print $4}' | grep -qE "[:.]${1}$"; }
67
[bb42dfb]68[ "$(id -u)" = 0 ] || die "Run this as root (sudo bash install.sh)."
69command -v apt-get >/dev/null || die "Debian/Ubuntu only (apt). On other systems use the Docker route."
[83088b66]70
71if [ -z "$KLONKT_DOMAIN" ]; then
[bb42dfb]72 read -rp "Domain for Klonkt (e.g. klonkt.example.com): " KLONKT_DOMAIN </dev/tty || true
[83088b66]73fi
[bb42dfb]74[ -n "$KLONKT_DOMAIN" ] || die "No domain given (--domain or KLONKT_DOMAIN)."
[83088b66]75case "$KLONKT_REPO" in
[bb42dfb]76 *OWNER/*) die "Set the real repo URL first: --repo https://github.com/<you>/klonkt.git (or KLONKT_REPO=...).";;
[83088b66]77esac
78
79export DEBIAN_FRONTEND=noninteractive
80
[bb42dfb]81# ── Preflight: see what's already running, adapt instead of clobbering ──────
82log "Preflight (what's already running?)…"
[83088b66]83apt-get update -y >/dev/null
84apt-get install -y iproute2 >/dev/null 2>&1 || true
85
[bb42dfb]86# Port: busy? With --port → error. Otherwise auto-pick a free one.
[83088b66]87if port_busy "$KLONKT_PORT"; then
88 if [ "$PORT_EXPLICIT" = 1 ]; then
[bb42dfb]89 die "Port ${KLONKT_PORT} is already in use. Pick a free port with --port."
[83088b66]90 fi
91 picked=""
92 for p in $(seq "$KLONKT_PORT" $((KLONKT_PORT+30))); do
93 port_busy "$p" || { picked="$p"; break; }
94 done
[bb42dfb]95 [ -n "$picked" ] || die "No free port found near ${KLONKT_PORT}. Provide one with --port."
96 warn "port ${KLONKT_PORT} busy → Klonkt uses ${picked}"
[83088b66]97 KLONKT_PORT="$picked"
98else
[bb42dfb]99 ok "port ${KLONKT_PORT} free"
[83088b66]100fi
101
[bb42dfb]102# Webserver on 80/443 that isn't Caddy? → skip Caddy, own-proxy mode.
[83088b66]103FOREIGN_PROXY=0
104if [ -z "$NO_CADDY" ] && command -v ss >/dev/null 2>&1; then
105 if ss -ltnpH 2>/dev/null | grep -E '[:.](80|443) ' | grep -viq 'caddy'; then
106 NO_CADDY=1; FOREIGN_PROXY=1
[bb42dfb]107 warn "something is already listening on port 80/443 (not Caddy) → NOT installing Caddy; you'll get proxy instructions"
[83088b66]108 fi
109fi
110
[bb42dfb]111# ── Node: respect an existing version, don't silently upgrade ──────────────
[83088b66]112log "Node ${NODE_MAJOR}.x…"
113if command -v node >/dev/null 2>&1 && [ -z "$NODE_FORCE" ]; then
114 CUR="$(node -v | sed 's/v//;s/\..*//')"
115 if [ "$CUR" -lt "$NODE_MAJOR" ]; then
[bb42dfb]116 die "Node $(node -v) is already installed on this server; Klonkt needs ≥${NODE_MAJOR}.
117 I will NOT auto-upgrade your system Node — that could break other apps.
118 Options: (a) use the Docker route (own Node, touches nothing), or
119 (b) upgrade Node yourself, or (c) force with NODE_FORCE=1 (at your own risk)."
[83088b66]120 fi
[bb42dfb]121 ok "using existing node $(node -v)"
[83088b66]122else
123 curl -fsSL "https://deb.nodesource.com/setup_${NODE_MAJOR}.x" | bash -
124 apt-get install -y nodejs
[bb42dfb]125 ok "node $(node -v) installed"
[83088b66]126fi
127
[bb42dfb]128log "Other packages…"
[83088b66]129apt-get install -y curl ca-certificates git gnupg openssl build-essential python3
[bb42dfb]130apt-get install -y webp >/dev/null 2>&1 || true # cwebp = image→WebP (optional)
131ok "base packages"
[83088b66]132
133if [ -z "$NO_CADDY" ]; then
134 log "Caddy (reverse proxy + auto-HTTPS)…"
135 if ! command -v caddy >/dev/null 2>&1; then
136 curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
137 curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' > /etc/apt/sources.list.d/caddy-stable.list
138 apt-get update -y
139 apt-get install -y caddy
140 fi
[bb42dfb]141 ok "caddy present"
[83088b66]142fi
143
[bb42dfb]144log "Service user '${KLONKT_USER}'…"
[83088b66]145id -u "$KLONKT_USER" >/dev/null 2>&1 || useradd --system --home-dir "$KLONKT_DIR" --shell /usr/sbin/nologin "$KLONKT_USER"
[bb42dfb]146ok "user"
[83088b66]147
[bb42dfb]148log "Fetching Klonkt source…"
[83088b66]149if [ -d "$KLONKT_DIR/.git" ]; then
150 git -C "$KLONKT_DIR" remote set-url origin "$KLONKT_REPO"
[f882048]151 # Re-run on an EXISTING install: keep the channel this install already tracks — never
152 # silently switch it to the stable default. Only an explicit --branch / KLONKT_BRANCH
153 # overrides; a fresh install (else-branch) uses the stable default.
154 if [ "$BRANCH_EXPLICIT" != "1" ]; then
155 _cur=$(git -C "$KLONKT_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || true)
156 [ -n "$_cur" ] && [ "$_cur" != "HEAD" ] && KLONKT_BRANCH="$_cur"
157 fi
158 log "Channel: $KLONKT_BRANCH"
[83088b66]159 git -C "$KLONKT_DIR" fetch --depth 1 origin "$KLONKT_BRANCH"
[aedc00d]160 # Check out FETCH_HEAD AS the target branch — not `reset --hard origin/$KLONKT_BRANCH`
161 # (a single-branch/shallow clone, or one that started on a different branch like main,
162 # has no origin/<branch> ref → "ambiguous argument 'origin/stable'"), and not a plain
163 # `reset --hard FETCH_HEAD` (that would leave the OLD local branch, e.g. main, pointing at
164 # a stable commit → `git status` reports it as diverged from origin/main). `checkout -f -B`
165 # makes the local branch BE $KLONKT_BRANCH at the fetched tip: robust, forced, no divergence.
166 git -C "$KLONKT_DIR" checkout -qf -B "$KLONKT_BRANCH" FETCH_HEAD
[83088b66]167else
[bb42dfb]168 [ -e "$KLONKT_DIR" ] && [ -n "$(ls -A "$KLONKT_DIR" 2>/dev/null)" ] && die "$KLONKT_DIR already exists and is not a git checkout. Pick --dir, or clean it up."
[83088b66]169 mkdir -p "$KLONKT_DIR"
170 git clone --depth 1 --branch "$KLONKT_BRANCH" "$KLONKT_REPO" "$KLONKT_DIR"
171fi
172chown -R "$KLONKT_USER:$KLONKT_USER" "$KLONKT_DIR"
173ok "code in $KLONKT_DIR"
174
[2dd1dc4]175# --- where this instance keeps its data -------------------------------------
176# New installs put data in /var/lib/klonkt/<slug> so the checkout stays free of
177# user data and can be shared by more instances later. An install that already
178# has its .env inside the checkout is left exactly as it is: re-running the
179# installer must never move a live database. Convert those deliberately with
180# scripts/klonkt-migrate-data.sh.
181if [ -z "$KLONKT_SLUG" ]; then
182 KLONKT_SLUG="$(printf '%s' "${KLONKT_DOMAIN:-default}" | sed 's/^www\.//' | cut -d. -f1 \
183 | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9._-')"
184 [ -n "$KLONKT_SLUG" ] || KLONKT_SLUG=default
185fi
186# A database in the checkout but no .env is too ambiguous to guess at: refuse,
187# rather than start a fresh empty instance beside data nobody is reading.
188if [ ! -f "$KLONKT_DIR/.env" ] && [ -f "$KLONKT_DIR/storage/database.sqlite" ]; then
189 die "found $KLONKT_DIR/storage/database.sqlite but no .env next to it.
190 Put the .env back and re-run, or move the old storage/ aside first."
191fi
192if [ -f "$KLONKT_DIR/.env" ]; then
193 LAYOUT=legacy
194 ENV="$KLONKT_DIR/.env"
195 DATA_DIR="$KLONKT_DIR/storage"
196 SERVICE="klonkt"
197 mkdir -p "$DATA_DIR/media" "$DATA_DIR/audio"
198 chown -R "$KLONKT_USER:$KLONKT_USER" "$DATA_DIR"
199 ok "existing layout kept (data inside $KLONKT_DIR; split it with scripts/klonkt-migrate-data.sh)"
200else
201 LAYOUT=split
202 DATA_DIR="$KLONKT_DATA_ROOT/$KLONKT_SLUG"
203 ENV="$DATA_DIR/.env"
204 SERVICE="klonkt@${KLONKT_SLUG}"
205 mkdir -p "$DATA_DIR/media" "$DATA_DIR/audio"
206 chown -R "$KLONKT_USER:$KLONKT_USER" "$DATA_DIR"
207 chmod 750 "$DATA_DIR"
208 ok "data in $DATA_DIR (instance '$KLONKT_SLUG')"
209fi
210
[bb42dfb]211log "Installing dependencies (npm ci)…"
[83088b66]212as_klonkt bash -c "cd '$KLONKT_DIR' && npm ci --omit=dev"
213ok "node_modules"
214
215log ".env…"
216if [ ! -f "$ENV" ]; then
217 SECRET="$(openssl rand -hex 32)"
218 {
219 echo "NODE_ENV=production"
220 echo "PORT=${KLONKT_PORT}"
[f99bbe8]221 # Bind to loopback only: Caddy (this host) reaches it; the internet cannot
222 # hit the app directly on its port, bypassing HTTPS.
223 echo "HOST=127.0.0.1"
[83088b66]224 echo "SESSION_SECRET=${SECRET}"
[2dd1dc4]225 # Absolute, so the app does not depend on its working directory and the
226 # data can sit outside the checkout. Media subdirectories (avatars,
227 # post-images, ...) follow MEDIA_PATH by themselves.
228 echo "DATABASE_PATH=${DATA_DIR}/database.sqlite"
229 echo "MEDIA_PATH=${DATA_DIR}/media"
230 echo "AUDIO_PATH=${DATA_DIR}/audio"
[83088b66]231 echo "PUBLIC_BASE_URL=https://${KLONKT_DOMAIN}"
232 [ -n "$KLONKT_LANG" ] && echo "KLONKT_DEFAULT_LANG=${KLONKT_LANG}"
233 } > "$ENV"
234 chown "$KLONKT_USER:$KLONKT_USER" "$ENV"; chmod 600 "$ENV"
[f99bbe8]235 ok "new .env (random SESSION_SECRET, app bound to 127.0.0.1)"
[83088b66]236else
[bb42dfb]237 # sync the port in an existing .env with the chosen port
[83088b66]238 if grep -q '^PORT=' "$ENV"; then sed -i "s/^PORT=.*/PORT=${KLONKT_PORT}/" "$ENV"; fi
[f99bbe8]239 # harden older installs: bind to loopback if not already configured
240 grep -q '^HOST=' "$ENV" || echo "HOST=127.0.0.1" >> "$ENV"
241 ok "kept existing .env (port synced, bound to 127.0.0.1)"
[83088b66]242fi
243
[bb42dfb]244log "systemd service…"
[83088b66]245NODE_BIN="$(command -v node)"
[2dd1dc4]246if [ "$LAYOUT" = split ]; then
247 # One template, one service per instance. Adding a site later is a data
248 # directory plus an .env, with no second copy of the code.
249 sed -e "s#^User=klonkt\$#User=${KLONKT_USER}#" \
250 -e "s#^Group=klonkt\$#Group=${KLONKT_USER}#" \
251 -e "s#^WorkingDirectory=/opt/klonkt\$#WorkingDirectory=${KLONKT_DIR}#" \
252 -e "s#^EnvironmentFile=/var/lib/klonkt/%i/.env\$#EnvironmentFile=${KLONKT_DATA_ROOT}/%i/.env#" \
253 -e "s#^ReadWritePaths=/var/lib/klonkt/%i\$#ReadWritePaths=${KLONKT_DATA_ROOT}/%i#" \
254 -e "s#^ExecStart=/usr/bin/node src/server.js\$#ExecStart=${NODE_BIN} src/server.js#" \
255 "$KLONKT_DIR/deploy/klonkt@.service" > /etc/systemd/system/klonkt@.service
256 chmod 0644 /etc/systemd/system/klonkt@.service
257 systemctl daemon-reload
258 systemctl enable --now "klonkt@${KLONKT_SLUG}"
259 ok "klonkt@${KLONKT_SLUG} running on 127.0.0.1:${KLONKT_PORT}"
260else
261 cat > /etc/systemd/system/klonkt.service <<EOF
[83088b66]262[Unit]
263Description=Klonkt
264After=network-online.target
265Wants=network-online.target
266
267[Service]
268Type=simple
269User=${KLONKT_USER}
270WorkingDirectory=${KLONKT_DIR}
271ExecStart=${NODE_BIN} src/server.js
272Environment=NODE_ENV=production
273Restart=always
274RestartSec=3
275NoNewPrivileges=true
276ProtectSystem=full
277PrivateTmp=true
278
279[Install]
280WantedBy=multi-user.target
281EOF
[2dd1dc4]282 systemctl daemon-reload
283 systemctl enable --now klonkt
284 ok "klonkt.service running on 127.0.0.1:${KLONKT_PORT}"
285fi
[83088b66]286
287if [ -z "$NO_CADDY" ]; then
[bb42dfb]288 log "Caddy config for ${KLONKT_DOMAIN}…"
[83088b66]289 CADDY=/etc/caddy/Caddyfile
290 SITE_BLOCK="${KLONKT_DOMAIN} {
291 reverse_proxy 127.0.0.1:${KLONKT_PORT}
292 encode gzip zstd
293}"
294 touch "$CADDY"
295 if grep -q '/usr/share/caddy' "$CADDY"; then
296 cp "$CADDY" "${CADDY}.bak.$(date +%s)"
297 printf '%s\n' "$SITE_BLOCK" > "$CADDY"
298 elif ! grep -q "^${KLONKT_DOMAIN} {" "$CADDY"; then
299 printf '\n%s\n' "$SITE_BLOCK" >> "$CADDY"
300 fi
[bb42dfb]301 caddy validate --config "$CADDY" --adapter caddyfile >/dev/null 2>&1 || die "Caddy config invalid — check $CADDY"
[83088b66]302 systemctl reload caddy 2>/dev/null || systemctl restart caddy
[bb42dfb]303 ok "caddy serving ${KLONKT_DOMAIN}"
[83088b66]304fi
305
[bb42dfb]306log "Update command 'klonkt-update'…"
[83088b66]307cat > /usr/local/bin/klonkt-update <<EOF
308#!/usr/bin/env bash
309set -euo pipefail
310D="${KLONKT_DIR}"
311B=\$(runuser -u ${KLONKT_USER} -- git -C "\$D" rev-parse HEAD 2>/dev/null || true)
312runuser -u ${KLONKT_USER} -- git -C "\$D" fetch --depth 1 origin ${KLONKT_BRANCH}
[aedc00d]313runuser -u ${KLONKT_USER} -- git -C "\$D" checkout -qf -B ${KLONKT_BRANCH} FETCH_HEAD
[83088b66]314A=\$(runuser -u ${KLONKT_USER} -- git -C "\$D" rev-parse HEAD)
[09a2061]315if [ "\$B" = "\$A" ]; then
316 echo "Klonkt is already up to date (\$A) — nothing to do."
317 exit 0
318fi
[83088b66]319if ! runuser -u ${KLONKT_USER} -- git -C "\$D" diff --quiet "\$B" "\$A" -- package-lock.json 2>/dev/null; then
320 runuser -u ${KLONKT_USER} -- env HOME="\$D" bash -c "cd '\$D' && npm ci --omit=dev"
321fi
[2dd1dc4]322# Restart every instance. Each directory under the data root with an .env is one
323# instance sharing this checkout. An install that has not been split yet has no
324# such directories and still runs the single klonkt.service.
325N=0
326for d in ${KLONKT_DATA_ROOT}/*/; do
327 [ -f "\$d/.env" ] || continue
328 s=\$(basename "\$d")
329 systemctl restart "klonkt@\$s" && N=\$((N+1))
330done
331if [ "\$N" = 0 ]; then
332 systemctl restart klonkt
333 echo "Klonkt updated (\$A) + restarted."
334else
335 echo "Klonkt updated (\$A) + restarted \$N instance(s)."
336fi
[83088b66]337EOF
338chmod +x /usr/local/bin/klonkt-update
339ok "klonkt-update"
340
341echo
342echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
[bb42dfb]343echo " Klonkt is running! 🎉"
[83088b66]344echo
345if [ -n "$NO_CADDY" ]; then
[bb42dfb]346 echo " Klonkt listens on: http://127.0.0.1:${KLONKT_PORT}"
[83088b66]347 if [ "$FOREIGN_PROXY" = 1 ]; then
[bb42dfb]348 echo " A webserver is already running on 80/443 — put Klonkt behind it."
[83088b66]349 fi
[bb42dfb]350 echo " Example nginx:"
[83088b66]351 echo " location / { proxy_pass http://127.0.0.1:${KLONKT_PORT}; proxy_set_header Host \$host;"
352 echo " proxy_set_header X-Forwarded-Proto \$scheme; }"
[bb42dfb]353 echo " Example Caddy:"
[83088b66]354 echo " ${KLONKT_DOMAIN} { reverse_proxy 127.0.0.1:${KLONKT_PORT} }"
355else
[bb42dfb]356 echo " • Open your site: https://${KLONKT_DOMAIN}"
[83088b66]357fi
[bb42dfb]358echo " • First run: go to /auth/register and create your admin account."
[83088b66]359echo
[2dd1dc4]360echo " Manage: systemctl status ${SERVICE} · journalctl -u ${SERVICE} -f · klonkt-update"
[bb42dfb]361echo " Lost password: cd ${KLONKT_DIR} && runuser -u ${KLONKT_USER} -- env HOME=${KLONKT_DIR} npm run reset-admin"
[2dd1dc4]362if [ "$LAYOUT" = split ]; then
363 echo
364 echo " Code: ${KLONKT_DIR} shared, nothing of yours lives here"
365 echo " Data: ${DATA_DIR} database, uploads and .env — back up this one"
366 echo " Another site on this server, sharing the same code:"
367 echo " sudo bash ${KLONKT_DIR}/scripts/klonkt-add-instance.sh <slug> <domain>"
368fi
[83088b66]369echo
[bb42dfb]370echo " DNS: make sure A + AAAA of ${KLONKT_DOMAIN} point to this server."
[83088b66]371echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
Note: See TracBrowser for help on using the repository browser.