proxmox: Selbsthosting-Set (Dialog-Suite + Einzelskripte) + 2 Artikel

Geführter Installer proxmox/install.sh: erst Vorhaben wählen
(Komplettes Büro / 365+Synology ersetzen / nur Website / einzeln),
dann werden die nötigen LXCs der Reihe nach gebaut. Jeder Dienst ist
auch als eigenständiges, einzeln curlbares Skript verfügbar:

- proxmox/nextcloud-lxc.sh    Nextcloud AIO (ersetzt 365/Synology)
- proxmox/empty-lxc.sh        leerer Docker-LXC als Geruest
- proxmox/git-compose-lxc.sh  beliebiges Git-Repo (RAPPORT/DOSSIER)
- (OPENBUREAU: bestehendes cms/proxmox/create-openbureau-lxc.sh)

Gemeinsames Muster: unprivilegierter Debian-12-LXC mit nesting+keyctl,
Docker via get.docker.com, Dienst als Container/Compose. proxmox/README.md
dokumentiert beide Wege.

Dazu zwei Bibliotheksbeitraege (Hochparterre-Ton):
- server-im-eigenen-haus.md  — das Warum/Ziel
- proxmox-schritt-fuer-schritt.md — die Anleitung mit curl-Befehlen

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 01:20:43 +02:00
parent b72f744963
commit b67b24a53c
7 changed files with 601 additions and 0 deletions
@@ -0,0 +1,118 @@
---
title: "Proxmox, Schritt für Schritt"
date: 2026-06-02
tags: ["software", "proxmox", "self-hosting", "anleitung", "lxc"]
summary: "Wie aus einer gebrauchten Kiste die Infrastruktur eines Architekturbüros wird — mit den Skripten, die einen Dienst in Minuten aufstellen."
color: kusa
layout: text
---
Die Kiste aus dem [ersten Teil](/library/software/server-im-eigenen-haus/) muss man nicht streicheln können, um sie zu verstehen. Es genügt ein Bild: Proxmox macht aus einem Rechner ein Mehrfamilienhaus. Das Haus ist die Maschine, die Wohnungen sind die Container, und in jeder Wohnung lebt genau ein Dienst — die Website, die Zeiterfassung, der Dateispeicher. Niemand stört den anderen, jeder hat seine eigene Tür, und zieht eine Partei aus, bleiben die übrigen, wo sie sind.
Dieser Text zeigt, wie man das Haus baut und die erste Wohnung bezieht. Er setzt keine Erfahrung mit Servern voraus, nur die Bereitschaft, einen Befehl abzutippen und zu lesen, was er antwortet.
## Das Fundament
Proxmox VE ist im Kern ein Debian-Linux mit einer Weboberfläche und der Fähigkeit, zweierlei Sorten Wohnungen zu vermieten: vollwertige virtuelle Maschinen und — das ist unser Fall — Linux-Container, sogenannte LXC. Ein Container teilt sich den Kern des Wirts und ist deshalb sparsam: Vier Gigabyte Arbeitsspeicher reichen für einen ausgewachsenen Dienst, ein Dutzend davon laufen auf gewöhnlicher Bürohardware.
Installiert wird Proxmox einmalig vom USB-Stick, so wie man ein Betriebssystem installiert. Das ist gut dokumentiert und hier nicht das Thema. Ab dem Moment, in dem die Weboberfläche unter `https://<ip>:8006` erscheint, beginnt der interessante Teil.
## Das Muster: ein Container, ein Dienst, ein Befehl
Wir richten keinen Dienst von Hand ein. Jeder Handgriff, den man zweimal macht, gehört in ein Skript — schon weil man ihn sonst beim Wiederaufsetzen vergisst. Unser Muster, von Dienst zu Dienst gleich, lautet:
1. einen **unprivilegierten** LXC anlegen (er darf weniger, also kann weniger schiefgehen),
2. ihn so einstellen, dass **Docker** darin läuft (`nesting` und `keyctl`),
3. den Dienst als **Docker-Compose-Stack** hineinstellen,
4. alle **Geheimnisse automatisch erzeugen** lassen, nichts von Hand eintippen,
5. ein **Backup** einrichten, bevor überhaupt Daten da sind.
Das ist die ganze Liturgie. Wer sie einmal in ein Skript gegossen hat, stellt den nächsten Dienst hin, indem er das Skript ruft.
## Die erste Wohnung: unser CMS
Diese Website ist das Musterbeispiel. Ein einziger Befehl, abgesetzt auf dem Proxmox-Wirt als `root`, baut den ganzen Container — Docker, das Repository, sämtliche Schlüssel, der laufende Stack:
```bash
bash <(curl -fsSL https://git.kgva.ch/karim/OPENBUREAU/raw/branch/main/cms/proxmox/create-openbureau-lxc.sh)
```
Das Skript fragt nur nach Speicherort, Netzwerkbrücke und IP — Enter übernimmt je den Vorschlag — und ist nach wenigen Minuten fertig. Am Ende nennt es die Adressen: den Editor unter `…:8080/admin/`, die Website unter `…:8080/`.
Spannend ist nicht der Einzeiler, sondern was er tut. Das [vollständige Skript](https://git.kgva.ch/karim/OPENBUREAU/src/branch/main/cms/proxmox/create-openbureau-lxc.sh) liest sich von oben nach unten wie ein Protokoll. Den Container anlegen, mit den zwei Schaltern, die Docker erlauben:
```bash
pct create "$CTID" "$TEMPLATE_REF" \
--hostname openbureau \
--cores 2 --memory 4096 --swap 1024 \
--rootfs "local-lvm:20" \
--net0 "name=eth0,bridge=vmbr0,ip=dhcp" \
--unprivileged 1 \
--features "nesting=1,keyctl=1" \
--onboot 1
```
Dann, im Container, Docker installieren, das Repository ziehen und — der Teil, der einem die durchwachte Nacht erspart — die Geheimnisse erzeugen, statt sie von Hand zu setzen:
```bash
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker
git clone https://git.kgva.ch/karim/OPENBUREAU.git /opt/openbureau
cd /opt/openbureau/cms
cp .env.example .env
sed -i "s|^POSTGRES_PASSWORD=.*|POSTGRES_PASSWORD=$(openssl rand -hex 32)|" .env
sed -i "s|^JWT_SECRET=.*|JWT_SECRET=$(openssl rand -hex 32)|" .env
docker compose up -d --build
```
Und schliesslich, noch bevor der erste Beitrag geschrieben ist, das tägliche Backup — denn das Forum lebt allein in der Datenbank, nicht im Git:
```bash
printf '15 3 * * * root cd /opt/openbureau/cms && bash scripts/backup-db.sh\n' \
> /etc/cron.d/openbureau-backup
```
Kein Schritt davon ist klug; jeder ist nur aufgeschrieben. Das ist der ganze Trick.
## Ein Menü statt Handarbeit
Weil das Muster sich von Dienst zu Dienst wiederholt, haben wir es ein einziges Mal in ein Installationsskript gegossen. Es ruft sich genauso wie das CMS-Skript — ein Einzeiler auf dem Proxmox-Wirt, als `root` —, nur legt es kein bestimmtes Programm fest, sondern fragt, was man haben will:
```bash
bash <(curl -fsSL https://git.kgva.ch/karim/OPENBUREAU/raw/branch/main/proxmox/install.sh)
```
Zuerst fragt es nicht nach Technik, sondern nach dem Vorhaben: ein ganzes Büro einrichten, bloss Office 365 und die Synology ersetzen, nur die öffentliche Website — oder, für jene, die genau wissen, was sie wollen, einzeln auswählen. Aus der Antwort leitet das Skript ab, welche Container es braucht, und baut sie der Reihe nach. Jeder bekommt seine eigene Wohnung; für jede erledigt das Skript dasselbe, was oben Schritt für Schritt stand: Template holen, unprivilegierten Container anlegen, Docker hineinlegen, den Dienst starten.
Wer das Menü überspringen will, hängt den gewünschten Dienst direkt an:
```bash
… install.sh nextcloud # nur Nextcloud
… install.sh empty dateien 200 8192 # leerer Docker-LXC, 200 GB / 8 GB RAM
… install.sh git git.kgva.ch/karim/RAPPORT-SERVER.git rapport
```
Hinter dem Menü steckt keine grosse Maschine, sondern ein Bündel kleiner, eigenständiger Skripte — eines pro Dienst. Die Suite ist nur der Dialog, der sie der Reihe nach aufruft. Wer das Menü gar nicht braucht, lädt das einzelne Skript direkt:
```bash
bash <(curl -fsSL …/proxmox/nextcloud-lxc.sh) # 500 GB / 8 GB RAM
bash <(curl -fsSL …/proxmox/empty-lxc.sh) dateien 200 8192
bash <(curl -fsSL …/proxmox/git-compose-lxc.sh) git.kgva.ch/karim/RAPPORT-SERVER.git rapport
```
Jedes dieser Skripte ist in sich geschlossen und tut, was oben Schritt für Schritt stand: Template holen, unprivilegierten Container anlegen, Docker hineinlegen, den Dienst starten. Genau diese Wiederholbarkeit ist der Sinn der Übung — ein Dienst, den man nicht mit einem Befehl neu aufsetzen kann, ist ein Dienst, vor dem man sich beim nächsten Mal fürchtet.
## Office 365 und die Synology ersetzen: Nextcloud
Der grösste Brocken verdient einen eigenen Blick, weil er am meisten ersetzt. [Nextcloud](https://nextcloud.com) übernimmt in einem Aufwasch, wofür sonst zwei Abos und eine NAS herhalten: die Dateiablage mit Synchronisation auf alle Geräte — das OneDrive- und Synology-Drive-Erbe —, gemeinsame Kalender und Kontakte, dazu über das eingebaute Office das Bearbeiten von Dokumenten und Tabellen im Browser, zu zweit am selben Text.
Im Menü ist es ein Haken, von Hand der Befehl oben. Was dann läuft, ist die offizielle All-in-One-Variante: ein verwalteter Container, der die übrigen selbst aufsetzt. Den Rest erledigt die Weboberfläche unter Port 8080 — sie vergibt das Admin-Passwort, fragt die Domain ab und startet die eigentlichen Dienste. Ohne eigene Domain erreicht man das Ganze vorerst im lokalen Netz; für den Zugriff von aussen kommt später ein Reverse-Proxy davor, dasselbe Prinzip, das auch unser CMS hinter TLS bringt.
Damit ist die Rechnung geschlossen: Mail, Kalender, Kontakte, Dateien, gemeinsame Dokumente — alles, wofür das Büro bisher Monat für Monat pro Kopf bezahlt hat, läuft im Schrank. Und unsere eigenen Werkzeuge, RAPPORT und DOSSIER, ziehen über denselben Git-Eintrag im Menü nach, weil sie demselben Muster folgen wie alles andere.
## Das Backup ist kein Anhang
Ein Satz zum Schluss, der eigentlich an den Anfang gehört. Selbst zu hosten heisst, selbst für die Sicherung geradezustehen. Zweierlei greift bei uns ineinander. Innerhalb jedes Dienstes sichert ein nächtlicher cron-Lauf die Datenbank weg — bei dieser Website das Forum, bei Nextcloud die Metadaten. Und für die Container als Ganzes nimmt der **Proxmox Backup Server** allabendlich einen Schnappschuss, aus dem sich eine ganze Wohnung in Minuten wiederherstellen lässt, sollte sie einmal abbrennen.
Ein Backup, das man nie zurückgespielt hat, ist eine Hoffnung, kein Backup. Darum gehört der erste Wiederherstellungs-Versuch an den Tag, an dem der Dienst aufgesetzt wird — nicht an den Tag, an dem man ihn braucht.
@@ -0,0 +1,36 @@
---
title: "Server im eigenen Haus"
date: 2026-06-02
tags: ["software", "proxmox", "self-hosting", "infrastruktur"]
summary: "Warum unsere Dienste auf einer eigenen Kiste laufen statt bei Microsoft, Google oder Synology — und was das mit Architektur zu tun hat."
color: yuyake
layout: text
---
Im Schrank neben dem Plotter steht jetzt eine Kiste. Kein schönes Gerät, ein ausgemusterter Bürorechner mit zu vielen Lüftern, der leise vor sich hin rauscht. Auf ihm liegt, was sonst über ein halbes Dutzend Abonnements verteilt wäre: die Korrespondenz, die Pläne, die Zeiterfassung, diese Website. Das Büro hat seine Daten nach Hause geholt.
Lange war das anders, und lange fiel es nicht auf. Ein Architekturbüro produziert Daten, bevor es das erste Gebäude produziert — Wettbewerbsbeiträge, Pläne in dreissig Revisionen, Honorarabrechnungen, die Korrespondenz mit Bauherrschaft und Amt. Dieser Bestand wächst still, und ebenso still ist er in die Cloud gewandert. Microsoft 365 für Mail und Dokumente, OneDrive oder die Synology im Keller für die Dateien, ein gemietetes CRM für die Adressen. Jedes Stück für sich vernünftig, zusammen ein Büro, dessen Substanz auf fremden Servern liegt, zu Bedingungen, die ein anderer schreibt.
Das funktioniert tadellos. Es ist bequem. Und es heisst, dass das Gedächtnis des Büros zur Miete wohnt.
## Die Praxis besitzt ihre Werkzeuge
Dass wir das umdrehen, ist keine Prinzipienreiterei, sondern eine Konsequenz aus dem [Manifest](/manifest/): Ein Büro offen zu führen heisst auch, die eigenen Werkzeuge zu besitzen — so wie ein Schreiner seine Hobel besitzt und nicht pro Span bezahlt. Wer die Werkzeuge mietet, mietet am Ende die eigene Arbeitsweise.
Der Hobel ist in diesem Fall die Kiste im Schrank. Darauf läuft Proxmox, eine quelloffene Software, die aus einem gewöhnlichen Rechner viele kleine, sauber getrennte Maschinen macht. In jeder steckt ein Dienst: diese Website samt dem Editor, mit dem dieser Text geschrieben wurde; RAPPORT, unsere Zeiterfassung; DOSSIER, die Projektablage; der Dateispeicher, der die Synology ablöst; Kalender, Kontakte, Mail. Alles offen, alles auf den eigenen Platten. Was vorher Monat für Monat pro Kopf abgebucht wurde, deckt die gebrauchte Hardware in unter einem Jahr.
## Was das mit Architektur zu tun hat
Mehr, als es zunächst scheint. Wo die Daten einer Bauherrschaft liegen, ist keine Geschmacksfrage, sondern eine des Anstands und des Datenschutzes. Eine Maschine im eigenen Haus beantwortet die Frage, wo die anvertrauten Unterlagen sind, mit einem Fingerzeig auf den Schrank — nicht mit einem Verweis auf Rechenzentren in einer anderen Rechtsordnung.
Dazu kommt die schlichte Unkündbarkeit. Verdoppelt ein Anbieter den Preis, streicht eine Funktion oder stellt das Produkt ein, ist das sein gutes Recht; man steht daneben und zahlt. Bei uns gibt es nichts, das gekündigt werden kann. Die Formate sind offen — die Texte dieser Bibliothek etwa sind schlichte Textdateien, lesbar auch dann, wenn unser ganzer Apparat einmal verschwindet.
Und schliesslich behandelt ein offenes Büro seine Infrastruktur wie einen Entwurf: Man versteht sie, ändert sie, dokumentiert sie. Dieser Aufbau ist deshalb kein Betriebsgeheimnis, sondern steht [unter freier Lizenz](/lizenz/) offen. Wer sein Büro ähnlich einrichten will, kopiert unsere Skripte und macht weiter.
## Der Preis der Selbstverständlichkeit
Bleibt die unbequeme Seite, und sie gehört in jeden ehrlichen Text dieser Art: Man wird sein eigener Hauswart. Backups laufen nicht mehr von allein, Aktualisierungen muss jemand einspielen, und fällt der Strom, klingelt kein Support.
Tragbar finden wir das aus zwei Gründen. Der Aufwand ist kleiner, als er klingt, sobald die Handgriffe automatisiert sind — einen neuen Dienst aufzusetzen ist bei uns ein einziger Befehl und kein verlorener Nachmittag. Und die Kontrolle ist den Rest wert: Lieber für ein Backup geradestehen, das man versteht, als sich auf eines verlassen, das man nie gesehen hat.
Wie die Kiste im Schrank konkret eingerichtet ist — die Maschine, die Container, die Befehle, mit denen ein Dienst in Minuten steht — steht im zweiten Teil: [Proxmox, Schritt für Schritt](/library/software/proxmox-schritt-fuer-schritt/).
+54
View File
@@ -0,0 +1,54 @@
# OPENBUREAU — Proxmox-Selbsthosting-Set
Skripte, um die Dienste eines Architekturbüros auf einem Proxmox-VE-Host
aufzusetzen — jeder Dienst in seinem eigenen, unprivilegierten, Docker-tauglichen
LXC. Alle Skripte werden **auf dem Proxmox-Host als `root`** ausgeführt.
## Zwei Wege
**1. Suite mit Dialog** — fragt, was man will, und installiert einen LXC nach dem
anderen:
```bash
bash <(curl -fsSL https://git.kgva.ch/karim/OPENBUREAU/raw/branch/main/proxmox/install.sh)
```
**2. Einzelskripte** (für Fortgeschrittene) — direkt, ohne Menü. Jedes ist in sich
geschlossen:
```bash
# Website + CMS (eigenes Skript, erzeugt alle Supabase-Secrets)
bash <(curl -fsSL …/cms/proxmox/create-openbureau-lxc.sh)
# Nextcloud (Dateien/Kalender/Kontakte/Office — ersetzt 365 + Synology)
bash <(curl -fsSL …/proxmox/nextcloud-lxc.sh) [disk_gb] [ram_mb]
# Leerer Docker-LXC als Gerüst
bash <(curl -fsSL …/proxmox/empty-lxc.sh) [name] [disk_gb] [ram_mb]
# Beliebiger Dienst aus einem Git-Repo mit docker-compose (RAPPORT, DOSSIER …)
bash <(curl -fsSL …/proxmox/git-compose-lxc.sh) <repo-url> [name] [disk_gb] [ram_mb]
```
`install.sh` akzeptiert dieselben Dienste auch direkt als Argument
(`install.sh nextcloud`, `install.sh git <repo> …`).
## Gemeinsames Muster
Jedes Skript macht dasselbe: aktuelles Debian-12-Template sicherstellen,
unprivilegierten LXC mit `nesting=1,keyctl=1` anlegen (damit Docker darin läuft),
Docker installieren, den Dienst als Container/Compose-Stack starten. Storage, Netz
und SSH-Key lassen sich per Umgebungsvariable überschreiben:
| Variable | Default |
|--------------------|--------------------------|
| `ROOTFS_STORAGE` | `local-lvm` |
| `TEMPLATE_STORAGE` | `local` |
| `BRIDGE` | `vmbr0` |
| `SSH_PUBKEY_FILE` | `~/.ssh/id_ed25519.pub` |
## Hintergrund
Warum und wie — die zwei Artikel in der Bibliothek:
[Server im eigenen Haus](https://openbureau.ch/library/software/server-im-eigenen-haus/)
und [Proxmox, Schritt für Schritt](https://openbureau.ch/library/software/proxmox-schritt-fuer-schritt/).
+74
View File
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
#
# Leerer, Docker-tauglicher LXC für Proxmox VE — das Gerüst für eigene Dienste.
#
# AUF DEM PROXMOX-HOST, als root:
# bash <(curl -fsSL https://git.kgva.ch/karim/OPENBUREAU/raw/branch/main/proxmox/empty-lxc.sh) [name] [disk_gb] [ram_mb]
#
# Beispiel: … empty-lxc.sh dateien 200 8192
#
set -euo pipefail
############################# gemeinsamer Kopf #############################
SSH_PUBKEY_FILE="${SSH_PUBKEY_FILE:-$HOME/.ssh/id_ed25519.pub}"
ROOTFS_STORAGE="${ROOTFS_STORAGE:-local-lvm}"
TEMPLATE_STORAGE="${TEMPLATE_STORAGE:-local}"
BRIDGE="${BRIDGE:-vmbr0}"
say() { echo -e "\n\033[1;36m▸ $*\033[0m"; }
ok() { echo -e "\033[1;32m✓ $*\033[0m"; }
warn() { echo -e "\033[1;33m! $*\033[0m" >&2; }
die() { echo -e "\033[1;31m✗ $*\033[0m" >&2; exit 1; }
[ "$(id -u)" -eq 0 ] || die "Bitte als root auf dem Proxmox-Host ausführen."
command -v pct >/dev/null || die "pct nicht gefunden — läuft das wirklich auf Proxmox VE?"
ensure_template() {
[ -n "${TEMPLATE_REF:-}" ] && return 0
pveam update >/dev/null 2>&1 || true
local tpl
tpl="$(pveam available --section system | awk '/debian-12-standard/{print $2}' | sort -V | tail -1)"
[ -n "$tpl" ] || die "Kein debian-12-Template verfügbar."
pveam list "$TEMPLATE_STORAGE" | grep -q "$tpl" || { say "Lade Template $tpl"; pveam download "$TEMPLATE_STORAGE" "$tpl" >/dev/null; }
TEMPLATE_REF="${TEMPLATE_STORAGE}:vztmpl/${tpl}"
}
# create_lxc <name> <disk_gb> <ram_mb> [cores] — setzt $CTID
create_lxc() {
local name="$1" disk="$2" ram="$3" cores="${4:-2}" ctid
ensure_template
ctid="$(pvesh get /cluster/nextid)"
local args=(
"$ctid" "$TEMPLATE_REF" --hostname "$name"
--cores "$cores" --memory "$ram" --swap 1024
--rootfs "${ROOTFS_STORAGE}:${disk}"
--net0 "name=eth0,bridge=${BRIDGE},ip=dhcp"
--unprivileged 1 --features "nesting=1,keyctl=1" --onboot 1
)
[ -f "$SSH_PUBKEY_FILE" ] && args+=(--ssh-public-keys "$SSH_PUBKEY_FILE")
say "Erstelle LXC $ctid ($name) — ${cores} Kerne, ${ram} MB RAM, ${disk} GB…"
pct create "${args[@]}" >/dev/null
pct start "$ctid"; sleep 5
CTID="$ctid"
}
install_docker() {
say "Installiere Docker in $1"
pct exec "$1" -- bash -euo pipefail -c '
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq ca-certificates curl git openssl >/dev/null
curl -fsSL https://get.docker.com | sh >/dev/null
systemctl enable --now docker
'
}
ip_of() { pct exec "$1" -- hostname -I 2>/dev/null | awk '{print $1}'; }
################################# Dienst #################################
NAME="${1:-docker}"; DISK="${2:-20}"; RAM="${3:-4096}"
create_lxc "$NAME" "$DISK" "$RAM"
install_docker "$CTID"
ok "Leerer Docker-LXC $CTID ($NAME) läuft unter $(ip_of "$CTID")."
echo " Hinein: pct enter $CTID"
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
#
# Beliebiger self-hosted Dienst aus einem Git-Repo mit docker-compose, als
# eigener LXC für Proxmox VE. Folgt demselben Muster wie OPENBUREAU — passend
# für eigene Werkzeuge wie RAPPORT oder DOSSIER.
#
# AUF DEM PROXMOX-HOST, als root:
# bash <(curl -fsSL https://git.kgva.ch/karim/OPENBUREAU/raw/branch/main/proxmox/git-compose-lxc.sh) <repo-url> [name] [disk_gb] [ram_mb]
#
# Beispiel: … git-compose-lxc.sh git.kgva.ch/karim/RAPPORT-SERVER.git rapport
#
set -euo pipefail
############################# gemeinsamer Kopf #############################
SSH_PUBKEY_FILE="${SSH_PUBKEY_FILE:-$HOME/.ssh/id_ed25519.pub}"
ROOTFS_STORAGE="${ROOTFS_STORAGE:-local-lvm}"
TEMPLATE_STORAGE="${TEMPLATE_STORAGE:-local}"
BRIDGE="${BRIDGE:-vmbr0}"
say() { echo -e "\n\033[1;36m▸ $*\033[0m"; }
ok() { echo -e "\033[1;32m✓ $*\033[0m"; }
warn() { echo -e "\033[1;33m! $*\033[0m" >&2; }
die() { echo -e "\033[1;31m✗ $*\033[0m" >&2; exit 1; }
[ "$(id -u)" -eq 0 ] || die "Bitte als root auf dem Proxmox-Host ausführen."
command -v pct >/dev/null || die "pct nicht gefunden — läuft das wirklich auf Proxmox VE?"
ensure_template() {
[ -n "${TEMPLATE_REF:-}" ] && return 0
pveam update >/dev/null 2>&1 || true
local tpl
tpl="$(pveam available --section system | awk '/debian-12-standard/{print $2}' | sort -V | tail -1)"
[ -n "$tpl" ] || die "Kein debian-12-Template verfügbar."
pveam list "$TEMPLATE_STORAGE" | grep -q "$tpl" || { say "Lade Template $tpl"; pveam download "$TEMPLATE_STORAGE" "$tpl" >/dev/null; }
TEMPLATE_REF="${TEMPLATE_STORAGE}:vztmpl/${tpl}"
}
create_lxc() {
local name="$1" disk="$2" ram="$3" cores="${4:-2}" ctid
ensure_template
ctid="$(pvesh get /cluster/nextid)"
local args=(
"$ctid" "$TEMPLATE_REF" --hostname "$name"
--cores "$cores" --memory "$ram" --swap 1024
--rootfs "${ROOTFS_STORAGE}:${disk}"
--net0 "name=eth0,bridge=${BRIDGE},ip=dhcp"
--unprivileged 1 --features "nesting=1,keyctl=1" --onboot 1
)
[ -f "$SSH_PUBKEY_FILE" ] && args+=(--ssh-public-keys "$SSH_PUBKEY_FILE")
say "Erstelle LXC $ctid ($name) — ${cores} Kerne, ${ram} MB RAM, ${disk} GB…"
pct create "${args[@]}" >/dev/null
pct start "$ctid"; sleep 5
CTID="$ctid"
}
install_docker() {
say "Installiere Docker in $1"
pct exec "$1" -- bash -euo pipefail -c '
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq ca-certificates curl git openssl >/dev/null
curl -fsSL https://get.docker.com | sh >/dev/null
systemctl enable --now docker
'
}
ip_of() { pct exec "$1" -- hostname -I 2>/dev/null | awk '{print $1}'; }
################################# Dienst #################################
REPO="${1:-}"; NAME="${2:-}"; DISK="${3:-20}"; RAM="${4:-4096}"
[ -n "$REPO" ] || die "Bitte eine Repo-URL angeben (Arg 1)."
[ -n "$NAME" ] || NAME="$(basename "${REPO%.git}" | tr '[:upper:]' '[:lower:]')"
case "$REPO" in http*://*) : ;; *) REPO="https://$REPO" ;; esac
create_lxc "$NAME" "$DISK" "$RAM"
install_docker "$CTID"
say "Klone $REPO und starte den Stack…"
pct exec "$CTID" -- bash -euo pipefail -c "
git clone --quiet '$REPO' '/opt/$NAME' || { echo 'Clone fehlgeschlagen.'; exit 1; }
cd '/opt/$NAME'
CF=\$(ls docker-compose.y*ml compose.y*ml */docker-compose.y*ml */compose.y*ml 2>/dev/null | head -1)
[ -n \"\$CF\" ] || { echo 'Keine docker-compose-Datei gefunden.'; exit 1; }
cd \"\$(dirname \"\$CF\")\"
if [ ! -f .env ] && [ -f .env.example ]; then
cp .env.example .env
echo 'HINWEIS: .env aus .env.example kopiert — Secrets ggf. anpassen.'
fi
docker compose up -d --build
"
ok "$NAME (LXC $CTID) läuft unter $(ip_of "$CTID")."
warn "App-spezifische Secrets (.env) ggf. prüfen: pct enter $CTID"
+133
View File
@@ -0,0 +1,133 @@
#!/usr/bin/env bash
#
# OPENBUREAU-Suite — der Dialog-Installer für das Selbsthosting-Set auf Proxmox.
#
# Fragt im Dialog, welche Dienste man will, und installiert dann einen LXC nach
# dem anderen — jeder über sein eigenes, eigenständiges Skript (proxmox/*-lxc.sh).
# Fortgeschrittene können diese Einzelskripte auch direkt curlen, ohne die Suite.
#
# AUF DEM PROXMOX-HOST (nicht im Container), als root:
# bash <(curl -fsSL https://git.kgva.ch/karim/OPENBUREAU/raw/branch/main/proxmox/install.sh)
#
# Direkt, ohne Dialog:
# … install.sh openbureau
# … install.sh nextcloud [disk_gb] [ram_mb]
# … install.sh empty [name] [disk_gb] [ram_mb]
# … install.sh git <repo-url> [name] [disk_gb] [ram_mb]
#
set -euo pipefail
RAW="https://git.kgva.ch/karim/OPENBUREAU/raw/branch/main"
say() { echo -e "\n\033[1;36m▸ $*\033[0m"; }
die() { echo -e "\033[1;31m✗ $*\033[0m" >&2; exit 1; }
[ "$(id -u)" -eq 0 ] || die "Bitte als root auf dem Proxmox-Host ausführen."
command -v pct >/dev/null || die "pct nicht gefunden — läuft das wirklich auf Proxmox VE?"
# Jeder Dienst = ein eigenständiges Skript. Die Suite ruft sie nur auf.
run_service() {
local svc="$1"; shift || true
case "$svc" in
openbureau) say "OPENBUREAU — Website + CMS…"; bash <(curl -fsSL "$RAW/cms/proxmox/create-openbureau-lxc.sh") ;;
nextcloud) say "Nextcloud…"; bash <(curl -fsSL "$RAW/proxmox/nextcloud-lxc.sh") "$@" ;;
empty) say "Leerer Docker-LXC…"; bash <(curl -fsSL "$RAW/proxmox/empty-lxc.sh") "$@" ;;
git) say "Git-Compose-Dienst…"; bash <(curl -fsSL "$RAW/proxmox/git-compose-lxc.sh") "$@" ;;
*) die "Unbekannter Dienst: $svc" ;;
esac
}
# ---------------------------------------------------------------------------
# Stufe 1: Vorhaben. schlüssel | Beschreibung | Liste der Dienste
# (Sonderfall "custom" → Einzelauswahl, siehe service_checklist.)
PROFILES=(
"buero|Komplettes Büro einrichten — Website/CMS + Nextcloud|openbureau nextcloud"
"cloud|Office 365 + Synology ersetzen — nur Nextcloud|nextcloud"
"web|Nur die öffentliche Website + CMS|openbureau"
"custom|Einzeln auswählen … (für Fortgeschrittene)|custom"
)
# Stufe 2 (custom): einzelne Dienste. schlüssel | Beschreibung
SERVICES=(
"openbureau|OPENBUREAU — Website + CMS (Hugo + Supabase)"
"nextcloud|Nextcloud — Dateien, Kalender, Kontakte, Office (ersetzt 365/Synology)"
"empty|Leerer Docker-LXC — Gerüst für eigene Dienste"
"git|Git-Compose-Dienst — eigenes Repo (z. B. RAPPORT / DOSSIER)"
)
# Holt für die interaktive Auswahl die Zusatzangaben (Repo-URL, Name) nach.
run_from_menu() {
case "$1" in
git)
local repo name
read -rp " Repo-URL (z. B. git.kgva.ch/karim/RAPPORT-SERVER.git): " repo
[ -n "$repo" ] || { echo " übersprungen (keine URL)."; return 0; }
read -rp " Name [auto]: " name
run_service git "$repo" "$name" ;;
empty)
local name
read -rp " Name des Containers [docker]: " name
run_service empty "${name:-docker}" ;;
*) run_service "$1" ;;
esac
}
# Stufe 2: Einzelauswahl der Dienste (Checkliste).
service_checklist() {
local choices=()
if command -v whiptail >/dev/null && [ -t 0 ]; then
local items=()
for s in "${SERVICES[@]}"; do items+=("${s%%|*}" "${s#*|}" OFF); done
local sel
sel="$(whiptail --title "OPENBUREAU — Dienste auswählen" \
--checklist "Mit der Leertaste auswählen, Enter bestätigt:" 20 78 ${#SERVICES[@]} \
"${items[@]}" 3>&1 1>&2 2>&3)" || { echo "Abgebrochen."; exit 0; }
eval "choices=($sel)" # whiptail liefert die Tags in Anführungszeichen
else
echo "Welche Dienste installieren? (Nummern mit Komma/Leerzeichen, Enter = nichts)"
local i=1
for s in "${SERVICES[@]}"; do printf " %d) %s\n" "$i" "${s#*|}"; i=$((i+1)); done
read -rp "Auswahl: " line
for n in ${line//,/ }; do
[ "$n" -ge 1 ] 2>/dev/null && [ "$n" -le "${#SERVICES[@]}" ] && choices+=("${SERVICES[$((n-1))]%%|*}")
done
fi
[ "${#choices[@]}" -gt 0 ] || { echo "Nichts ausgewählt."; exit 0; }
for c in "${choices[@]}"; do run_from_menu "$c"; done
}
# Stufe 1: Vorhaben wählen, dann das passende Bündel installieren.
choose_profile() {
local key=""
if command -v whiptail >/dev/null && [ -t 0 ]; then
local items=() first=ON
for p in "${PROFILES[@]}"; do items+=("${p%%|*}" "$(echo "$p" | cut -d'|' -f2)" "$first"); first=OFF; done
key="$(whiptail --title "OPENBUREAU — Was hast du vor?" \
--radiolist "Vorhaben wählen (Leertaste markiert, Enter bestätigt):" 20 78 ${#PROFILES[@]} \
"${items[@]}" 3>&1 1>&2 2>&3)" || { echo "Abgebrochen."; exit 0; }
else
echo "Was hast du vor? (eine Nummer)"
local i=1
for p in "${PROFILES[@]}"; do printf " %d) %s\n" "$i" "$(echo "$p" | cut -d'|' -f2)"; i=$((i+1)); done
read -rp "Auswahl [1]: " n; n="${n:-1}"
[ "$n" -ge 1 ] 2>/dev/null && [ "$n" -le "${#PROFILES[@]}" ] || die "Ungültige Auswahl."
key="${PROFILES[$((n-1))]%%|*}"
fi
if [ "$key" = "custom" ]; then
service_checklist
else
local svcs=""
for p in "${PROFILES[@]}"; do [ "${p%%|*}" = "$key" ] && svcs="${p##*|}"; done
say "Vorhaben '$key': installiere -> $svcs"
for s in $svcs; do run_service "$s"; done
fi
}
################################# Einstieg #################################
if [ "$#" -gt 0 ]; then
run_service "$@" # direkt, ohne Dialog
else
choose_profile # geführter Dialog: erst Vorhaben, dann Dienste
fi
say "Fertig. Alle ausgewählten Dienste sind durch."
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
#
# Nextcloud (All-in-One) als eigener LXC für Proxmox VE.
# Dateien, Kalender, Kontakte, Office in einem verwalteten Container —
# ersetzt OneDrive / Synology-Drive + Office 365.
#
# AUF DEM PROXMOX-HOST, als root:
# bash <(curl -fsSL https://git.kgva.ch/karim/OPENBUREAU/raw/branch/main/proxmox/nextcloud-lxc.sh) [disk_gb] [ram_mb]
#
set -euo pipefail
############################# gemeinsamer Kopf #############################
SSH_PUBKEY_FILE="${SSH_PUBKEY_FILE:-$HOME/.ssh/id_ed25519.pub}"
ROOTFS_STORAGE="${ROOTFS_STORAGE:-local-lvm}"
TEMPLATE_STORAGE="${TEMPLATE_STORAGE:-local}"
BRIDGE="${BRIDGE:-vmbr0}"
say() { echo -e "\n\033[1;36m▸ $*\033[0m"; }
ok() { echo -e "\033[1;32m✓ $*\033[0m"; }
warn() { echo -e "\033[1;33m! $*\033[0m" >&2; }
die() { echo -e "\033[1;31m✗ $*\033[0m" >&2; exit 1; }
[ "$(id -u)" -eq 0 ] || die "Bitte als root auf dem Proxmox-Host ausführen."
command -v pct >/dev/null || die "pct nicht gefunden — läuft das wirklich auf Proxmox VE?"
ensure_template() {
[ -n "${TEMPLATE_REF:-}" ] && return 0
pveam update >/dev/null 2>&1 || true
local tpl
tpl="$(pveam available --section system | awk '/debian-12-standard/{print $2}' | sort -V | tail -1)"
[ -n "$tpl" ] || die "Kein debian-12-Template verfügbar."
pveam list "$TEMPLATE_STORAGE" | grep -q "$tpl" || { say "Lade Template $tpl"; pveam download "$TEMPLATE_STORAGE" "$tpl" >/dev/null; }
TEMPLATE_REF="${TEMPLATE_STORAGE}:vztmpl/${tpl}"
}
create_lxc() {
local name="$1" disk="$2" ram="$3" cores="${4:-2}" ctid
ensure_template
ctid="$(pvesh get /cluster/nextid)"
local args=(
"$ctid" "$TEMPLATE_REF" --hostname "$name"
--cores "$cores" --memory "$ram" --swap 1024
--rootfs "${ROOTFS_STORAGE}:${disk}"
--net0 "name=eth0,bridge=${BRIDGE},ip=dhcp"
--unprivileged 1 --features "nesting=1,keyctl=1" --onboot 1
)
[ -f "$SSH_PUBKEY_FILE" ] && args+=(--ssh-public-keys "$SSH_PUBKEY_FILE")
say "Erstelle LXC $ctid ($name) — ${cores} Kerne, ${ram} MB RAM, ${disk} GB…"
pct create "${args[@]}" >/dev/null
pct start "$ctid"; sleep 5
CTID="$ctid"
}
install_docker() {
say "Installiere Docker in $1"
pct exec "$1" -- bash -euo pipefail -c '
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq ca-certificates curl git openssl >/dev/null
curl -fsSL https://get.docker.com | sh >/dev/null
systemctl enable --now docker
'
}
ip_of() { pct exec "$1" -- hostname -I 2>/dev/null | awk '{print $1}'; }
################################# Dienst #################################
# Grosszügig dimensioniert — hier leben die Bürodaten.
DISK="${1:-500}"; RAM="${2:-8192}"
create_lxc nextcloud "$DISK" "$RAM"
install_docker "$CTID"
say "Starte Nextcloud All-in-One (mastercontainer)…"
pct exec "$CTID" -- bash -euo pipefail -c '
docker run -d --name nextcloud-aio-mastercontainer --restart always \
-p 8080:8080 -e APACHE_PORT=11000 \
-v nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
nextcloud/all-in-one:latest >/dev/null
'
IP="$(ip_of "$CTID")"
ok "Nextcloud-LXC $CTID läuft."
cat <<EOF
Einrichtung im Browser: https://${IP}:8080
Dort Admin-Passwort + Domain setzen — Nextcloud startet dann die übrigen
Container (Datenbank, Office, Talk …) selbst.
Ohne eigene Domain ist das vorerst nur im LAN erreichbar. Für den Zugriff
von aussen einen Reverse-Proxy davorsetzen.
EOF