Initial: RAPPORT Server Proxmox-LXC-Installer v0.1.0
Ein-Befehl-Installer: baut einen unprivilegierten Debian-12-LXC mit Docker, klont den SERVER-CONTAINER-Stack, generiert Secrets + JWT-Keys, holt die DB-Migrations und startet den Compose-Stack. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
.DS_Store
|
||||||
|
*.log
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
Rapport-Server — Self-Hosting-Stack für Rapport
|
||||||
|
Copyright (C) 2026 Karim Gabriele Varano
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published
|
||||||
|
by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
Full license text: https://www.gnu.org/licenses/agpl-3.0.txt
|
||||||
|
|
||||||
|
The bundled Supabase containers (postgres, gotrue, postgrest, realtime,
|
||||||
|
storage-api, kong, supabase/postgres image) are distributed under their
|
||||||
|
own permissive licenses (mostly Apache-2.0 and PostgreSQL License) by the
|
||||||
|
Supabase team. See https://supabase.com/docs/guides/self-hosting for details.
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
# RAPPORT-SERVER-PROXMOX-LXC
|
||||||
|
|
||||||
|
> ⚠️ **Status: Alpha.** Baut auf [SERVER-CONTAINER](https://git.kgva.ch/karim/rapport-server) auf, das selbst noch nicht end-to-end läuft (siehe dortige „Bekannte offene Punkte"). Erst zum Testen gedacht, nicht für Produktion.
|
||||||
|
|
||||||
|
Ein-Befehl-Installer, der den Rapport-Self-Hosting-Stack in einen **Proxmox-LXC-Container** deployt. Für Leute, die einen Proxmox-Host haben und Rapport nicht auf dem Mac (SERVER-APP), sondern auf dem Server laufen lassen wollen.
|
||||||
|
|
||||||
|
Der vierte Deployment-Pfad der Rapport-Familie:
|
||||||
|
|
||||||
|
| Repo | Zielgruppe |
|
||||||
|
|---|---|
|
||||||
|
| `APP` | Endnutzer (Desktop-Client) |
|
||||||
|
| `SERVER-APP` | Mac-Mini-Selfhost per Doppelklick (Tauri) |
|
||||||
|
| `SERVER-CONTAINER` | Docker-Compose von Hand (Tech-affin) |
|
||||||
|
| **`SERVER-PROXMOX-LXC`** | **Proxmox-Homelab — ein Befehl, fertiger Container** |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Was das Script macht
|
||||||
|
|
||||||
|
Läuft auf der **Proxmox-VE-Host-Shell** und:
|
||||||
|
|
||||||
|
1. Lädt das Debian-12-Template (falls nicht vorhanden)
|
||||||
|
2. Erstellt einen **unprivilegierten** LXC mit `nesting=1,keyctl=1` (nötig für Docker)
|
||||||
|
3. Installiert Docker + Node im Container
|
||||||
|
4. Klont [SERVER-CONTAINER](https://git.kgva.ch/karim/rapport-server) nach `/opt/rapport`
|
||||||
|
5. Generiert zufällige `POSTGRES_PASSWORD` + `JWT_SECRET` und daraus passende `ANON_KEY` / `SERVICE_ROLE_KEY`
|
||||||
|
6. Setzt `SITE_URL` / `API_EXTERNAL_URL` auf die LAN-IP des Containers
|
||||||
|
7. Holt die DB-Migrations (`sync-migrations.sh`)
|
||||||
|
8. Startet den Stack mit `docker compose up -d`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Benutzung
|
||||||
|
|
||||||
|
Auf der **Proxmox-Host-Shell** (als root):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash -c "$(curl -fsSL https://git.kgva.ch/karim/rapport-server-proxmox-lxc/raw/branch/main/rapport-lxc.sh)"
|
||||||
|
```
|
||||||
|
|
||||||
|
Danach erreichbar unter `http://<container-ip>:8080`.
|
||||||
|
|
||||||
|
### Parameter (per Env-Var)
|
||||||
|
|
||||||
|
Alle Defaults lassen sich überschreiben:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
RAM_MB=8192 DISK_GB=30 CT_HOSTNAME=rapport \
|
||||||
|
NET_IP=192.168.1.50/24 NET_GW=192.168.1.1 \
|
||||||
|
bash -c "$(curl -fsSL https://git.kgva.ch/karim/rapport-server-proxmox-lxc/raw/branch/main/rapport-lxc.sh)"
|
||||||
|
```
|
||||||
|
|
||||||
|
| Var | Default | Bedeutung |
|
||||||
|
|---|---|---|
|
||||||
|
| `CTID` | nächste freie ID | Container-ID |
|
||||||
|
| `CT_HOSTNAME` | `rapport-server` | Hostname |
|
||||||
|
| `CORES` | `2` | CPU-Kerne |
|
||||||
|
| `RAM_MB` | `6144` | RAM (min. 4096 empfohlen) |
|
||||||
|
| `SWAP_MB` | `2048` | Swap |
|
||||||
|
| `DISK_GB` | `20` | Disk (Supabase-Images sind gross) |
|
||||||
|
| `BRIDGE` | `vmbr0` | Netzwerk-Bridge |
|
||||||
|
| `STORAGE` | `local-lvm` | Storage für rootfs |
|
||||||
|
| `TMPL_STORAGE` | `local` | Storage für Template-Cache |
|
||||||
|
| `NET_IP` | `dhcp` | `dhcp` oder z.B. `192.168.1.50/24` |
|
||||||
|
| `NET_GW` | — | Gateway (Pflicht bei statischer IP) |
|
||||||
|
| `PASSWORD` | — | root-Passwort im Container (leer = kein Login) |
|
||||||
|
| `REPO_URL` | `…/rapport-server.git` | Quelle des Compose-Stacks |
|
||||||
|
| `REPO_REF` | `main` | Branch/Tag |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Voraussetzungen
|
||||||
|
|
||||||
|
- Proxmox VE 8.x
|
||||||
|
- ~20 GB freier Storage, ≥4 GB RAM für den Container
|
||||||
|
- Internet-Zugang im Container (Docker-Images + Repos pullen)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verwaltung
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pct exec <CTID> -- bash -c 'cd /opt/rapport && docker compose ps' # Status
|
||||||
|
pct exec <CTID> -- bash -c 'cd /opt/rapport && docker compose logs -f' # Logs
|
||||||
|
pct enter <CTID> # Shell
|
||||||
|
```
|
||||||
|
|
||||||
|
Backup (Postgres):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pct exec <CTID> -- bash -c 'cd /opt/rapport && docker compose exec -T db pg_dumpall -U postgres' > backup.sql
|
||||||
|
```
|
||||||
|
|
||||||
|
Oder einfach den ganzen LXC per Proxmox-Backup (vzdump) sichern.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lizenz
|
||||||
|
|
||||||
|
GNU AGPL-3.0-or-later — identisch zur Rapport-Familie.
|
||||||
Executable
+186
@@ -0,0 +1,186 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# RAPPORT Server — Proxmox-LXC-Installer
|
||||||
|
#
|
||||||
|
# Läuft auf der PROXMOX-VE-HOST-SHELL (nicht im Container!).
|
||||||
|
# Baut einen unprivilegierten Debian-12-LXC, installiert Docker, klont den
|
||||||
|
# Rapport-Compose-Stack (SERVER-CONTAINER), generiert Secrets + JWT-Keys,
|
||||||
|
# holt die DB-Migrations, setzt die LAN-URLs und startet den Stack.
|
||||||
|
#
|
||||||
|
# bash -c "$(curl -fsSL https://git.kgva.ch/karim/rapport-server-proxmox-lxc/raw/branch/main/rapport-lxc.sh)"
|
||||||
|
#
|
||||||
|
# …oder lokal: bash rapport-lxc.sh
|
||||||
|
#
|
||||||
|
# Alle Parameter sind per Env-Var überschreibbar, z.B.:
|
||||||
|
# RAM_MB=8192 NET_IP=192.168.1.50/24 NET_GW=192.168.1.1 bash rapport-lxc.sh
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
VERSION="0.1.0"
|
||||||
|
|
||||||
|
# ═══ Konfiguration ══════════════════════════════════════════════════════════
|
||||||
|
CTID="${CTID:-$(pvesh get /cluster/nextid)}" # freie Container-ID
|
||||||
|
CT_HOSTNAME="${CT_HOSTNAME:-rapport-server}"
|
||||||
|
DISK_GB="${DISK_GB:-20}" # Supabase-Images sind gross
|
||||||
|
CORES="${CORES:-2}"
|
||||||
|
RAM_MB="${RAM_MB:-6144}" # Postgres + 6 Services, min. 4 GB
|
||||||
|
SWAP_MB="${SWAP_MB:-2048}"
|
||||||
|
BRIDGE="${BRIDGE:-vmbr0}"
|
||||||
|
STORAGE="${STORAGE:-local-lvm}" # Storage für rootfs
|
||||||
|
TMPL_STORAGE="${TMPL_STORAGE:-local}" # Storage für Template-Cache
|
||||||
|
NET_IP="${NET_IP:-dhcp}" # dhcp ODER z.B. 192.168.1.50/24
|
||||||
|
NET_GW="${NET_GW:-}" # nur bei statischer IP nötig
|
||||||
|
PASSWORD="${PASSWORD:-}" # root-PW im Container (leer = kein Login)
|
||||||
|
REPO_URL="${REPO_URL:-https://git.kgva.ch/karim/rapport-server.git}"
|
||||||
|
REPO_REF="${REPO_REF:-main}"
|
||||||
|
TEMPLATE="${TEMPLATE:-debian-12-standard}" # pveam-Template-Name (Präfix)
|
||||||
|
|
||||||
|
# ═══ Ausgabe-Helfer ═════════════════════════════════════════════════════════
|
||||||
|
GN='\033[0;32m'; YW='\033[0;33m'; RD='\033[0;31m'; BL='\033[1;34m'; CL='\033[0m'
|
||||||
|
msg() { echo -e "${GN}✔${CL} $*"; }
|
||||||
|
info() { echo -e "${YW}→${CL} $*"; }
|
||||||
|
die() { echo -e "${RD}✗${CL} $*" >&2; exit 1; }
|
||||||
|
|
||||||
|
echo -e "${BL}RAPPORT Server — Proxmox-LXC-Installer v${VERSION}${CL}"
|
||||||
|
|
||||||
|
# ═══ Vorbedingungen ═════════════════════════════════════════════════════════
|
||||||
|
[[ $EUID -eq 0 ]] || die "Bitte als root auf der Proxmox-Host-Shell ausführen."
|
||||||
|
command -v pct >/dev/null || die "pct nicht gefunden — läuft das auf einem Proxmox-VE-Host?"
|
||||||
|
command -v pveam >/dev/null || die "pveam nicht gefunden — Proxmox-VE-Host erwartet."
|
||||||
|
|
||||||
|
# ═══ 1 · Debian-Template sicherstellen ══════════════════════════════════════
|
||||||
|
info "Suche Debian-12-Template …"
|
||||||
|
pveam update >/dev/null 2>&1 || true
|
||||||
|
TMPL_FILE="$(pveam available --section system | awk -v t="$TEMPLATE" '$2 ~ t {print $2}' | sort -V | tail -n1)"
|
||||||
|
[[ -n "$TMPL_FILE" ]] || die "Kein Template '$TEMPLATE*' verfügbar (siehe: pveam available)."
|
||||||
|
|
||||||
|
if ! pveam list "$TMPL_STORAGE" 2>/dev/null | grep -q "$TMPL_FILE"; then
|
||||||
|
info "Lade Template $TMPL_FILE auf $TMPL_STORAGE …"
|
||||||
|
pveam download "$TMPL_STORAGE" "$TMPL_FILE"
|
||||||
|
fi
|
||||||
|
TMPL_REF="${TMPL_STORAGE}:vztmpl/${TMPL_FILE}"
|
||||||
|
msg "Template: $TMPL_REF"
|
||||||
|
|
||||||
|
# ═══ 2 · Netzwerk-String bauen ══════════════════════════════════════════════
|
||||||
|
if [[ "$NET_IP" == "dhcp" ]]; then
|
||||||
|
NETCFG="name=eth0,bridge=${BRIDGE},ip=dhcp"
|
||||||
|
else
|
||||||
|
[[ -n "$NET_GW" ]] || die "Bei statischer IP (NET_IP=$NET_IP) muss NET_GW gesetzt sein."
|
||||||
|
NETCFG="name=eth0,bridge=${BRIDGE},ip=${NET_IP},gw=${NET_GW}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ═══ 3 · Container erstellen (unprivilegiert + Docker-Features) ══════════════
|
||||||
|
info "Erstelle LXC #${CTID} (${CT_HOSTNAME}) …"
|
||||||
|
CREATE_ARGS=(
|
||||||
|
"$CTID" "$TMPL_REF"
|
||||||
|
--hostname "$CT_HOSTNAME"
|
||||||
|
--cores "$CORES"
|
||||||
|
--memory "$RAM_MB"
|
||||||
|
--swap "$SWAP_MB"
|
||||||
|
--rootfs "${STORAGE}:${DISK_GB}"
|
||||||
|
--net0 "$NETCFG"
|
||||||
|
--features "nesting=1,keyctl=1" # Pflicht, damit Docker im unpriv. LXC läuft
|
||||||
|
--unprivileged 1
|
||||||
|
--onboot 1
|
||||||
|
--ostype debian
|
||||||
|
)
|
||||||
|
[[ -n "$PASSWORD" ]] && CREATE_ARGS+=(--password "$PASSWORD")
|
||||||
|
pct create "${CREATE_ARGS[@]}"
|
||||||
|
msg "Container #${CTID} erstellt."
|
||||||
|
|
||||||
|
info "Starte Container …"
|
||||||
|
pct start "$CTID"
|
||||||
|
|
||||||
|
info "Warte auf Netzwerk …"
|
||||||
|
for _ in $(seq 1 30); do
|
||||||
|
pct exec "$CTID" -- getent hosts deb.debian.org >/dev/null 2>&1 && break
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
# ═══ 4 · Basis-Pakete + Docker + Node installieren ══════════════════════════
|
||||||
|
# Node wird für scripts/generate-keys.mjs gebraucht (ANON/SERVICE-JWT-Keys).
|
||||||
|
info "Installiere Basis-Pakete + Docker im Container (kann ein paar Minuten dauern) …"
|
||||||
|
pct exec "$CTID" -- bash -c '
|
||||||
|
set -euo pipefail
|
||||||
|
export DEBIAN_FRONTEND=noninteractive
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y -qq ca-certificates curl git openssl nodejs >/dev/null
|
||||||
|
curl -fsSL https://get.docker.com | sh >/dev/null 2>&1
|
||||||
|
systemctl enable --now docker >/dev/null 2>&1 || true
|
||||||
|
'
|
||||||
|
msg "Docker + Node installiert."
|
||||||
|
|
||||||
|
# ═══ 5 · Repo klonen ════════════════════════════════════════════════════════
|
||||||
|
info "Klone Rapport-Stack ($REPO_URL @ $REPO_REF) …"
|
||||||
|
pct exec "$CTID" -- bash -c "
|
||||||
|
set -euo pipefail
|
||||||
|
rm -rf /opt/rapport
|
||||||
|
git clone --branch '$REPO_REF' --depth 1 '$REPO_URL' /opt/rapport
|
||||||
|
"
|
||||||
|
msg "Stack nach /opt/rapport geklont."
|
||||||
|
|
||||||
|
# ═══ 6 · Container-IP ermitteln ═════════════════════════════════════════════
|
||||||
|
info "Ermittle Container-IP …"
|
||||||
|
CT_IP=""
|
||||||
|
for _ in $(seq 1 15); do
|
||||||
|
CT_IP="$(pct exec "$CTID" -- hostname -I 2>/dev/null | awk '{print $1}')"
|
||||||
|
[[ -n "$CT_IP" ]] && break
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
[[ -n "$CT_IP" ]] || die "Konnte keine Container-IP ermitteln."
|
||||||
|
msg "Container-IP: $CT_IP"
|
||||||
|
|
||||||
|
# ═══ 7 · .env erzeugen: Secrets, JWT-Keys, LAN-URLs ═════════════════════════
|
||||||
|
# Reihenfolge laut SERVER-CONTAINER/README:
|
||||||
|
# .env aus .env.example → POSTGRES_PASSWORD + JWT_SECRET → ANON/SERVICE-Keys
|
||||||
|
# (müssen aus DEM JWT_SECRET abgeleitet sein!) → SITE_URL/API_EXTERNAL_URL
|
||||||
|
info "Generiere Secrets + JWT-Keys, setze LAN-URLs …"
|
||||||
|
pct exec "$CTID" -- env CT_IP="$CT_IP" bash -c '
|
||||||
|
set -euo pipefail
|
||||||
|
cd /opt/rapport
|
||||||
|
cp -n .env.example .env
|
||||||
|
|
||||||
|
PW=$(openssl rand -hex 32)
|
||||||
|
JWT=$(openssl rand -hex 32)
|
||||||
|
sed -i "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=${PW}|" .env
|
||||||
|
sed -i "s|^JWT_SECRET=.*|JWT_SECRET=${JWT}|" .env
|
||||||
|
|
||||||
|
# ANON_KEY + SERVICE_ROLE_KEY aus dem JWT_SECRET ableiten
|
||||||
|
KEYS=$(node scripts/generate-keys.mjs 2>/dev/null)
|
||||||
|
ANON=$(echo "$KEYS" | sed -n "s|^ANON_KEY=||p")
|
||||||
|
SVC=$(echo "$KEYS" | sed -n "s|^SERVICE_ROLE_KEY=||p")
|
||||||
|
[ -n "$ANON" ] && [ -n "$SVC" ] || { echo "Key-Generierung fehlgeschlagen" >&2; exit 1; }
|
||||||
|
grep -q "^ANON_KEY=" .env && sed -i "s|^ANON_KEY=.*|ANON_KEY=${ANON}|" .env || echo "ANON_KEY=${ANON}" >> .env
|
||||||
|
grep -q "^SERVICE_ROLE_KEY=" .env && sed -i "s|^SERVICE_ROLE_KEY=.*|SERVICE_ROLE_KEY=${SVC}|" .env || echo "SERVICE_ROLE_KEY=${SVC}" >> .env
|
||||||
|
|
||||||
|
# LAN-URLs auf die Container-IP zeigen lassen
|
||||||
|
sed -i "s|^SITE_URL=.*|SITE_URL=http://${CT_IP}:8080|" .env
|
||||||
|
sed -i "s|^API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://${CT_IP}:8000|" .env
|
||||||
|
|
||||||
|
# Standard-Ports (LXC ist dediziert, keine Konflikte wie auf Karims Dev-Mac)
|
||||||
|
sed -i "s|^APP_PORT=.*|APP_PORT=8080|" .env 2>/dev/null || true
|
||||||
|
sed -i "s|^KONG_HTTP_PORT=.*|KONG_HTTP_PORT=8000|" .env 2>/dev/null || true
|
||||||
|
sed -i "s|^DB_PORT=.*|DB_PORT=5432|" .env 2>/dev/null || true
|
||||||
|
chmod 600 .env
|
||||||
|
'
|
||||||
|
msg ".env erzeugt (Secrets zufällig, Keys passend zum JWT_SECRET)."
|
||||||
|
|
||||||
|
# ═══ 8 · DB-Migrations holen ════════════════════════════════════════════════
|
||||||
|
info "Hole DB-Migrations aus dem App-Repo …"
|
||||||
|
pct exec "$CTID" -- bash -c "cd /opt/rapport && bash scripts/sync-migrations.sh"
|
||||||
|
msg "Migrations synchronisiert."
|
||||||
|
|
||||||
|
# ═══ 9 · Stack hochfahren ═══════════════════════════════════════════════════
|
||||||
|
info "Starte Compose-Stack (Images pullen, Erststart ~1-2 Min) …"
|
||||||
|
pct exec "$CTID" -- bash -c "cd /opt/rapport && docker compose up -d"
|
||||||
|
|
||||||
|
# ═══ Fertig ═════════════════════════════════════════════════════════════════
|
||||||
|
echo
|
||||||
|
msg "RAPPORT Server läuft in LXC #${CTID} (${CT_HOSTNAME})"
|
||||||
|
echo -e " ${GN}Frontend:${CL} http://${CT_IP}:8080"
|
||||||
|
echo -e " ${GN}API/Kong:${CL} http://${CT_IP}:8000"
|
||||||
|
echo -e " ${GN}Postgres:${CL} ${CT_IP}:5432 (nur containerintern; PW in /opt/rapport/.env)"
|
||||||
|
echo
|
||||||
|
info "Status: pct exec ${CTID} -- bash -c 'cd /opt/rapport && docker compose ps'"
|
||||||
|
info "Logs: pct exec ${CTID} -- bash -c 'cd /opt/rapport && docker compose logs -f'"
|
||||||
|
info "Shell: pct enter ${CTID}"
|
||||||
Reference in New Issue
Block a user