Index: scripts/install.sh
===================================================================
--- scripts/install.sh	(revision f8820484d6ba9a506b8ec384fa3a6d745c4fddfb)
+++ scripts/install.sh	(revision 2dd1dc40db2f7dab2c095eeb309e5aaa02369b4b)
@@ -28,4 +28,10 @@
 KLONKT_BRANCH="${KLONKT_BRANCH:-stable}"
 KLONKT_DIR="${KLONKT_DIR:-/opt/klonkt}"
+# Where instance data lives, one directory per slug. The code in KLONKT_DIR is
+# shared; everything an instance writes stays under here.
+KLONKT_DATA_ROOT="${KLONKT_DATA_ROOT:-/var/lib/klonkt}"
+# Short name for this instance: its directory under the data root and its
+# systemd unit (klonkt@<slug>). Derived from the domain when left empty.
+KLONKT_SLUG="${KLONKT_SLUG:-}"
 KLONKT_USER="${KLONKT_USER:-klonkt}"
 KLONKT_PORT="${KLONKT_PORT:-3000}"
@@ -164,7 +170,42 @@
   git clone --depth 1 --branch "$KLONKT_BRANCH" "$KLONKT_REPO" "$KLONKT_DIR"
 fi
-mkdir -p "$KLONKT_DIR/storage/media" "$KLONKT_DIR/storage/audio"
 chown -R "$KLONKT_USER:$KLONKT_USER" "$KLONKT_DIR"
 ok "code in $KLONKT_DIR"
+
+# --- where this instance keeps its data -------------------------------------
+# New installs put data in /var/lib/klonkt/<slug> so the checkout stays free of
+# user data and can be shared by more instances later. An install that already
+# has its .env inside the checkout is left exactly as it is: re-running the
+# installer must never move a live database. Convert those deliberately with
+# scripts/klonkt-migrate-data.sh.
+if [ -z "$KLONKT_SLUG" ]; then
+  KLONKT_SLUG="$(printf '%s' "${KLONKT_DOMAIN:-default}" | sed 's/^www\.//' | cut -d. -f1 \
+                 | tr '[:upper:]' '[:lower:]' | tr -cd 'a-z0-9._-')"
+  [ -n "$KLONKT_SLUG" ] || KLONKT_SLUG=default
+fi
+# A database in the checkout but no .env is too ambiguous to guess at: refuse,
+# rather than start a fresh empty instance beside data nobody is reading.
+if [ ! -f "$KLONKT_DIR/.env" ] && [ -f "$KLONKT_DIR/storage/database.sqlite" ]; then
+  die "found $KLONKT_DIR/storage/database.sqlite but no .env next to it.
+   Put the .env back and re-run, or move the old storage/ aside first."
+fi
+if [ -f "$KLONKT_DIR/.env" ]; then
+  LAYOUT=legacy
+  ENV="$KLONKT_DIR/.env"
+  DATA_DIR="$KLONKT_DIR/storage"
+  SERVICE="klonkt"
+  mkdir -p "$DATA_DIR/media" "$DATA_DIR/audio"
+  chown -R "$KLONKT_USER:$KLONKT_USER" "$DATA_DIR"
+  ok "existing layout kept (data inside $KLONKT_DIR; split it with scripts/klonkt-migrate-data.sh)"
+else
+  LAYOUT=split
+  DATA_DIR="$KLONKT_DATA_ROOT/$KLONKT_SLUG"
+  ENV="$DATA_DIR/.env"
+  SERVICE="klonkt@${KLONKT_SLUG}"
+  mkdir -p "$DATA_DIR/media" "$DATA_DIR/audio"
+  chown -R "$KLONKT_USER:$KLONKT_USER" "$DATA_DIR"
+  chmod 750 "$DATA_DIR"
+  ok "data in $DATA_DIR (instance '$KLONKT_SLUG')"
+fi
 
 log "Installing dependencies (npm ci)…"
@@ -173,5 +214,4 @@
 
 log ".env…"
-ENV="$KLONKT_DIR/.env"
 if [ ! -f "$ENV" ]; then
   SECRET="$(openssl rand -hex 32)"
@@ -183,7 +223,10 @@
     echo "HOST=127.0.0.1"
     echo "SESSION_SECRET=${SECRET}"
-    echo "DATABASE_PATH=./storage/database.sqlite"
-    echo "MEDIA_PATH=./storage/media"
-    echo "AUDIO_PATH=./storage/audio"
+    # Absolute, so the app does not depend on its working directory and the
+    # data can sit outside the checkout. Media subdirectories (avatars,
+    # post-images, ...) follow MEDIA_PATH by themselves.
+    echo "DATABASE_PATH=${DATA_DIR}/database.sqlite"
+    echo "MEDIA_PATH=${DATA_DIR}/media"
+    echo "AUDIO_PATH=${DATA_DIR}/audio"
     echo "PUBLIC_BASE_URL=https://${KLONKT_DOMAIN}"
     [ -n "$KLONKT_LANG" ] && echo "KLONKT_DEFAULT_LANG=${KLONKT_LANG}"
@@ -201,5 +244,20 @@
 log "systemd service…"
 NODE_BIN="$(command -v node)"
-cat > /etc/systemd/system/klonkt.service <<EOF
+if [ "$LAYOUT" = split ]; then
+  # One template, one service per instance. Adding a site later is a data
+  # directory plus an .env, with no second copy of the code.
+  sed -e "s#^User=klonkt\$#User=${KLONKT_USER}#" \
+      -e "s#^Group=klonkt\$#Group=${KLONKT_USER}#" \
+      -e "s#^WorkingDirectory=/opt/klonkt\$#WorkingDirectory=${KLONKT_DIR}#" \
+      -e "s#^EnvironmentFile=/var/lib/klonkt/%i/.env\$#EnvironmentFile=${KLONKT_DATA_ROOT}/%i/.env#" \
+      -e "s#^ReadWritePaths=/var/lib/klonkt/%i\$#ReadWritePaths=${KLONKT_DATA_ROOT}/%i#" \
+      -e "s#^ExecStart=/usr/bin/node src/server.js\$#ExecStart=${NODE_BIN} src/server.js#" \
+      "$KLONKT_DIR/deploy/klonkt@.service" > /etc/systemd/system/klonkt@.service
+  chmod 0644 /etc/systemd/system/klonkt@.service
+  systemctl daemon-reload
+  systemctl enable --now "klonkt@${KLONKT_SLUG}"
+  ok "klonkt@${KLONKT_SLUG} running on 127.0.0.1:${KLONKT_PORT}"
+else
+  cat > /etc/systemd/system/klonkt.service <<EOF
 [Unit]
 Description=Klonkt
@@ -222,7 +280,8 @@
 WantedBy=multi-user.target
 EOF
-systemctl daemon-reload
-systemctl enable --now klonkt
-ok "klonkt.service running on 127.0.0.1:${KLONKT_PORT}"
+  systemctl daemon-reload
+  systemctl enable --now klonkt
+  ok "klonkt.service running on 127.0.0.1:${KLONKT_PORT}"
+fi
 
 if [ -z "$NO_CADDY" ]; then
@@ -261,6 +320,19 @@
   runuser -u ${KLONKT_USER} -- env HOME="\$D" bash -c "cd '\$D' && npm ci --omit=dev"
 fi
-systemctl restart klonkt
-echo "Klonkt updated (\$A) + restarted."
+# Restart every instance. Each directory under the data root with an .env is one
+# instance sharing this checkout. An install that has not been split yet has no
+# such directories and still runs the single klonkt.service.
+N=0
+for d in ${KLONKT_DATA_ROOT}/*/; do
+  [ -f "\$d/.env" ] || continue
+  s=\$(basename "\$d")
+  systemctl restart "klonkt@\$s" && N=\$((N+1))
+done
+if [ "\$N" = 0 ]; then
+  systemctl restart klonkt
+  echo "Klonkt updated (\$A) + restarted."
+else
+  echo "Klonkt updated (\$A) + restarted \$N instance(s)."
+fi
 EOF
 chmod +x /usr/local/bin/klonkt-update
@@ -286,6 +358,13 @@
 echo "  • First run:       go to /auth/register and create your admin account."
 echo
-echo "  Manage:  systemctl status klonkt · journalctl -u klonkt -f · klonkt-update"
+echo "  Manage:  systemctl status ${SERVICE} · journalctl -u ${SERVICE} -f · klonkt-update"
 echo "  Lost password: cd ${KLONKT_DIR} && runuser -u ${KLONKT_USER} -- env HOME=${KLONKT_DIR} npm run reset-admin"
+if [ "$LAYOUT" = split ]; then
+  echo
+  echo "  Code:  ${KLONKT_DIR}          shared, nothing of yours lives here"
+  echo "  Data:  ${DATA_DIR}   database, uploads and .env — back up this one"
+  echo "  Another site on this server, sharing the same code:"
+  echo "      sudo bash ${KLONKT_DIR}/scripts/klonkt-add-instance.sh <slug> <domain>"
+fi
 echo
 echo "  DNS: make sure A + AAAA of ${KLONKT_DOMAIN} point to this server."
Index: scripts/klonkt-add-instance.sh
===================================================================
--- scripts/klonkt-add-instance.sh	(revision 2dd1dc40db2f7dab2c095eeb309e5aaa02369b4b)
+++ scripts/klonkt-add-instance.sh	(revision 2dd1dc40db2f7dab2c095eeb309e5aaa02369b4b)
@@ -0,0 +1,134 @@
+#!/usr/bin/env bash
+#
+# Add a Klonkt instance. An instance is a data directory and an .env file; the
+# code in /opt/klonkt is shared with every other instance and is not copied.
+#
+#     sudo bash scripts/klonkt-add-instance.sh <slug> <domain> [port]
+#     sudo bash scripts/klonkt-add-instance.sh blog blog.example.com --no-caddy
+#
+# Leave the port out and a free one is chosen. Run klonkt-update once and every
+# instance on the machine moves to the new code together.
+
+set -euo pipefail
+
+KLONKT_DIR="${KLONKT_DIR:-/opt/klonkt}"
+KLONKT_USER="${KLONKT_USER:-klonkt}"
+DATA_ROOT="${KLONKT_DATA_ROOT:-/var/lib/klonkt}"
+NO_CADDY="${KLONKT_NO_CADDY:-}"
+LANG_DEFAULT="${KLONKT_DEFAULT_LANG:-}"
+
+SLUG=""; DOMAIN=""; PORT=""
+for arg in "$@"; do
+  case "$arg" in
+    --no-caddy) NO_CADDY=1 ;;
+    -*) echo "unknown option: $arg" >&2; exit 2 ;;
+    *) if   [ -z "$SLUG" ];   then SLUG="$arg"
+       elif [ -z "$DOMAIN" ]; then DOMAIN="$arg"
+       elif [ -z "$PORT" ];   then PORT="$arg"
+       fi ;;
+  esac
+done
+
+say()  { printf '  %s\n' "$*"; }
+step() { printf '\n== %s\n' "$*"; }
+die()  { printf '\nERROR: %s\n' "$*" >&2; exit 1; }
+
+[ "$(id -u)" = 0 ] || die "run this as root (sudo)."
+[ -n "$SLUG" ] && [ -n "$DOMAIN" ] || die "usage: $0 <slug> <domain> [port] [--no-caddy]"
+[[ "$SLUG" =~ ^[a-z0-9][a-z0-9._-]*$ ]] || die "slug must be lowercase letters, digits, dot, dash or underscore."
+
+DATA_DIR="$DATA_ROOT/$SLUG"
+ENV_FILE="$DATA_DIR/.env"
+
+step "Preflight"
+[ -d "$KLONKT_DIR" ] || die "no shared code at $KLONKT_DIR. Install Klonkt first."
+[ -f "$KLONKT_DIR/src/config/paths.js" ] || die \
+  "this build is too old to share one checkout between instances.
+   Update first: klonkt-update"
+id -u "$KLONKT_USER" >/dev/null 2>&1 || die "user $KLONKT_USER does not exist"
+[ -e "$DATA_DIR" ] && die "$DATA_DIR already exists. Pick another slug."
+[ -f /etc/systemd/system/klonkt@.service ] || {
+  [ -f "$KLONKT_DIR/deploy/klonkt@.service" ] || die "missing $KLONKT_DIR/deploy/klonkt@.service"
+  install -m 0644 "$KLONKT_DIR/deploy/klonkt@.service" /etc/systemd/system/klonkt@.service
+  systemctl daemon-reload
+  say "installed the systemd template (first instance on this machine)"
+}
+
+# Pick a port nobody is listening on and no other instance has claimed.
+if [ -z "$PORT" ]; then
+  for p in $(seq 3000 3099); do
+    grep -rqs "^PORT=${p}$" "$DATA_ROOT"/*/.env && continue
+    ss -ltnH "sport = :$p" 2>/dev/null | grep -q . && continue
+    PORT="$p"; break
+  done
+  [ -n "$PORT" ] || die "no free port found in 3000-3099; pass one explicitly."
+fi
+say "slug $SLUG, domain $DOMAIN, port $PORT"
+
+step "Creating $DATA_DIR"
+mkdir -p "$DATA_DIR"
+
+step "Writing .env"
+SECRET="$(openssl rand -hex 32)"
+{
+  echo "NODE_ENV=production"
+  echo "PORT=${PORT}"
+  # Loopback only: the reverse proxy reaches it, the internet cannot bypass HTTPS.
+  echo "HOST=127.0.0.1"
+  echo "SESSION_SECRET=${SECRET}"
+  echo "PUBLIC_BASE_URL=https://${DOMAIN}"
+  echo "DATABASE_PATH=${DATA_DIR}/database.sqlite"
+  echo "MEDIA_PATH=${DATA_DIR}/media"
+  echo "AUDIO_PATH=${DATA_DIR}/audio"
+  [ -n "$LANG_DEFAULT" ] && echo "KLONKT_DEFAULT_LANG=${LANG_DEFAULT}"
+} > "$ENV_FILE"
+say "random SESSION_SECRET, data paths under $DATA_DIR"
+
+step "Ownership and permissions"
+chown -R "$KLONKT_USER:$KLONKT_USER" "$DATA_DIR"
+chmod 750 "$DATA_DIR"
+chmod 600 "$ENV_FILE"
+say "owned by $KLONKT_USER; .env readable only by that user"
+
+step "Starting klonkt@$SLUG"
+systemctl enable --now "klonkt@$SLUG"
+sleep 3
+systemctl is-active --quiet "klonkt@$SLUG" || {
+  echo; journalctl -u "klonkt@$SLUG" -n 30 --no-pager || true
+  die "klonkt@$SLUG did not start."
+}
+curl -fsS --max-time 8 -o /dev/null "http://127.0.0.1:${PORT}/" \
+  && say "responding on 127.0.0.1:${PORT}" \
+  || say "WARNING: no answer yet on 127.0.0.1:${PORT}; check journalctl -u klonkt@$SLUG -f"
+
+if [ -z "$NO_CADDY" ] && command -v caddy >/dev/null 2>&1; then
+  step "Caddy"
+  CADDY=/etc/caddy/Caddyfile
+  if grep -q "^${DOMAIN} {" "$CADDY" 2>/dev/null; then
+    say "a block for ${DOMAIN} already exists, left untouched"
+  else
+    cp "$CADDY" "${CADDY}.bak.$(date +%s)" 2>/dev/null || true
+    printf '\n%s {\n    reverse_proxy 127.0.0.1:%s\n    encode gzip zstd\n}\n' "$DOMAIN" "$PORT" >> "$CADDY"
+    caddy validate --config "$CADDY" --adapter caddyfile >/dev/null 2>&1 \
+      || die "Caddy config invalid after adding ${DOMAIN} — check $CADDY (a .bak was made)"
+    systemctl reload caddy 2>/dev/null || systemctl restart caddy
+    say "serving ${DOMAIN}"
+  fi
+fi
+
+cat <<EOF
+
+Instance ready.
+
+  data   $DATA_DIR
+  unit   klonkt@$SLUG
+  port   127.0.0.1:$PORT
+  code   $KLONKT_DIR  (shared with every other instance)
+
+Next: open https://${DOMAIN}/auth/register and create the admin account.
+
+  status   systemctl status klonkt@$SLUG
+  logs     journalctl -u klonkt@$SLUG -f
+  update   klonkt-update          # updates the code once, restarts all instances
+  backup   $DATA_DIR              # this directory is the whole instance
+EOF
Index: scripts/klonkt-migrate-data.sh
===================================================================
--- scripts/klonkt-migrate-data.sh	(revision 2dd1dc40db2f7dab2c095eeb309e5aaa02369b4b)
+++ scripts/klonkt-migrate-data.sh	(revision 2dd1dc40db2f7dab2c095eeb309e5aaa02369b4b)
@@ -0,0 +1,176 @@
+#!/usr/bin/env bash
+#
+# Move an existing Klonkt install to the split layout:
+#
+#     /opt/klonkt/                shared code, read-only at runtime
+#     /var/lib/klonkt/<slug>/     this instance's data and .env
+#
+# Before, an instance kept its database, uploads and .env inside the checkout.
+# That made the code directory undeletable (it held live user data), made
+# backups awkward, and meant a second instance needed a second copy of the code.
+#
+# Run as root on the server. Safe to re-run: it stops at the first step that is
+# already done rather than moving anything twice.
+#
+#     sudo bash scripts/klonkt-migrate-data.sh <slug>
+#     sudo bash scripts/klonkt-migrate-data.sh <slug> --dry-run
+#
+# The slug names the instance and nothing else: it is the directory under
+# /var/lib/klonkt and the systemd instance name (klonkt@<slug>).
+
+set -euo pipefail
+
+KLONKT_DIR="${KLONKT_DIR:-/opt/klonkt}"
+KLONKT_USER="${KLONKT_USER:-klonkt}"
+DATA_ROOT="${KLONKT_DATA_ROOT:-/var/lib/klonkt}"
+OLD_UNIT="klonkt.service"
+
+SLUG=""
+DRY=0
+for arg in "$@"; do
+  case "$arg" in
+    --dry-run) DRY=1 ;;
+    -*) echo "unknown option: $arg" >&2; exit 2 ;;
+    *) SLUG="$arg" ;;
+  esac
+done
+
+say()  { printf '  %s\n' "$*"; }
+step() { printf '\n== %s\n' "$*"; }
+die()  { printf '\nERROR: %s\n' "$*" >&2; exit 1; }
+run()  { if [ "$DRY" = 1 ]; then printf '  [dry-run] %s\n' "$*"; else eval "$@"; fi; }
+
+[ "$(id -u)" = 0 ] || die "run this as root (sudo)."
+[ -n "$SLUG" ]      || die "usage: $0 <slug> [--dry-run]   e.g. $0 boiert"
+[[ "$SLUG" =~ ^[a-z0-9][a-z0-9._-]*$ ]] || die "slug must be lowercase letters, digits, dot, dash or underscore."
+
+DATA_DIR="$DATA_ROOT/$SLUG"
+ENV_OLD="$KLONKT_DIR/.env"
+ENV_NEW="$DATA_DIR/.env"
+
+step "Preflight"
+[ -d "$KLONKT_DIR" ]  || die "no install at $KLONKT_DIR"
+[ -f "$ENV_OLD" ] || [ -f "$ENV_NEW" ] || die "no .env at $ENV_OLD (already migrated elsewhere?)"
+id -u "$KLONKT_USER" >/dev/null 2>&1 || die "user $KLONKT_USER does not exist"
+
+# The split only works on code where every media subdirectory derives from
+# MEDIA_PATH. On older code the subdirectories fall back into the checkout, so
+# the app would quietly recreate storage/ next to the code and uploads would
+# land there.
+[ -f "$KLONKT_DIR/src/config/paths.js" ] || die \
+  "this build is too old for the split layout: src/config/paths.js is missing.
+   Update first (git pull in $KLONKT_DIR), then run this again."
+say "code at $KLONKT_DIR supports MEDIA_PATH-derived subdirectories"
+
+if [ -d "$DATA_DIR" ] && [ -n "$(ls -A "$DATA_DIR" 2>/dev/null)" ]; then
+  die "$DATA_DIR already exists and is not empty. Remove it or pick another slug."
+fi
+say "target $DATA_DIR is free"
+[ "$DRY" = 1 ] && say "DRY RUN: nothing will be changed"
+
+step "Stopping the service"
+if systemctl is-active --quiet "$OLD_UNIT"; then
+  run "systemctl stop $OLD_UNIT"
+  say "stopped $OLD_UNIT (SQLite checkpoints its write-ahead log on shutdown)"
+else
+  say "$OLD_UNIT was not running"
+fi
+
+step "Creating the data directory"
+run "mkdir -p '$DATA_DIR'"
+
+step "Moving data out of the checkout"
+if [ -d "$KLONKT_DIR/storage" ]; then
+  say "found $(find "$KLONKT_DIR/storage" -type f 2>/dev/null | wc -l) files in storage/ ($(du -sh "$KLONKT_DIR/storage" 2>/dev/null | cut -f1))"
+  # Everything, including database.sqlite plus its -wal and -shm siblings.
+  run "shopt -s dotglob nullglob; for f in '$KLONKT_DIR/storage/'*; do mv \"\$f\" '$DATA_DIR/'; done"
+  run "rmdir '$KLONKT_DIR/storage' 2>/dev/null || true"
+  say "moved to $DATA_DIR"
+else
+  say "no storage/ directory (already moved?)"
+fi
+
+if [ -f "$ENV_OLD" ]; then
+  run "mv '$ENV_OLD' '$ENV_NEW'"
+  say "moved .env to $ENV_NEW"
+fi
+
+step "Pointing the data paths at the new location"
+# Replace when present, append when absent, so this works regardless of which
+# variables the original install wrote.
+set_env() {
+  local key="$1" val="$2"
+  if [ "$DRY" = 1 ]; then printf '  [dry-run] %s=%s\n' "$key" "$val"; return; fi
+  if grep -q "^${key}=" "$ENV_NEW" 2>/dev/null; then
+    sed -i "s#^${key}=.*#${key}=${val}#" "$ENV_NEW"
+  else
+    printf '%s=%s\n' "$key" "$val" >> "$ENV_NEW"
+  fi
+  printf '  %s=%s\n' "$key" "$val"
+}
+set_env DATABASE_PATH "$DATA_DIR/database.sqlite"
+set_env MEDIA_PATH    "$DATA_DIR/media"
+set_env AUDIO_PATH    "$DATA_DIR/audio"
+
+step "Ownership and permissions"
+run "chown -R '$KLONKT_USER:$KLONKT_USER' '$DATA_DIR'"
+run "chmod 750 '$DATA_DIR'"
+run "chmod 600 '$ENV_NEW'"
+say "data owned by $KLONKT_USER, .env readable only by that user"
+
+step "Installing the systemd template"
+if [ -f "$KLONKT_DIR/deploy/klonkt@.service" ]; then
+  run "install -m 0644 '$KLONKT_DIR/deploy/klonkt@.service' /etc/systemd/system/klonkt@.service"
+  say "installed /etc/systemd/system/klonkt@.service"
+else
+  die "template not found at $KLONKT_DIR/deploy/klonkt@.service"
+fi
+run "systemctl daemon-reload"
+
+step "Switching to klonkt@$SLUG"
+if systemctl is-enabled --quiet "$OLD_UNIT" 2>/dev/null; then
+  run "systemctl disable --now $OLD_UNIT"
+  say "disabled $OLD_UNIT (file kept, so you can roll back)"
+fi
+run "systemctl enable --now 'klonkt@$SLUG'"
+
+step "Verifying"
+if [ "$DRY" = 1 ]; then
+  say "dry run: skipping verification"
+  exit 0
+fi
+sleep 3
+systemctl is-active --quiet "klonkt@$SLUG" || {
+  echo
+  journalctl -u "klonkt@$SLUG" -n 30 --no-pager || true
+  die "klonkt@$SLUG did not start. Roll back with: systemctl enable --now $OLD_UNIT"
+}
+say "klonkt@$SLUG is running"
+
+PORT="$(grep -m1 '^PORT=' "$ENV_NEW" | cut -d= -f2- | tr -d '\r')"
+if [ -n "$PORT" ]; then
+  if curl -fsS --max-time 8 -o /dev/null "http://127.0.0.1:${PORT}/"; then
+    say "responding on 127.0.0.1:${PORT}"
+  else
+    say "WARNING: no answer on 127.0.0.1:${PORT} yet; check: journalctl -u klonkt@$SLUG -f"
+  fi
+fi
+
+if [ -e "$KLONKT_DIR/storage" ]; then
+  say "WARNING: $KLONKT_DIR/storage came back. That means this build still writes"
+  say "         next to its code. Report it; do not delete the directory."
+else
+  say "the checkout no longer holds user data"
+fi
+
+cat <<EOF
+
+Done. This instance now looks like:
+
+  code   $KLONKT_DIR              shared, replaceable, no user data
+  data   $DATA_DIR    database, uploads and .env
+  unit   klonkt@$SLUG
+
+Back up $DATA_DIR and you have the whole instance.
+Add another instance with: klonkt-add-instance.sh <slug> <domain> <port>
+EOF
