doc: konsolidierte Homelab-Dokumentation + Original-Plan
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
DB_ROOT_PW=change-me-root
|
||||
DB_PW=change-me-nc
|
||||
REDIS_PW=change-me-redis
|
||||
COLLABORA_PW=change-me-collabora
|
||||
WHITEBOARD_JWT=change-me-langes-zufalls-secret
|
||||
@@ -0,0 +1,111 @@
|
||||
# nextcloud stack — rieger-getuned, caddy-ready, mit collabora + whiteboard
|
||||
# läuft im LXC 110. rootfs/app auf NVMe, daten auf tank (/srv/nc_data = tank/nextcloud).
|
||||
# caddy terminiert TLS und proxyt cloud.deinedomain.ch -> web:80
|
||||
# office.deinedomain.ch -> collabora:9980
|
||||
# whiteboard.deinedomain.ch -> whiteboard:3002
|
||||
|
||||
services:
|
||||
db:
|
||||
image: mariadb:11.4
|
||||
restart: unless-stopped
|
||||
command: --transaction-isolation=READ-COMMITTED --log-bin=binlog --binlog-format=ROW
|
||||
volumes:
|
||||
- db:/var/lib/mysql
|
||||
- ./config/mariadb-tuning.cnf:/etc/mysql/conf.d/99-nextcloud.cnf:ro
|
||||
environment:
|
||||
- MARIADB_ROOT_PASSWORD=${DB_ROOT_PW}
|
||||
- MARIADB_DATABASE=nextcloud
|
||||
- MARIADB_USER=nextcloud
|
||||
- MARIADB_PASSWORD=${DB_PW}
|
||||
healthcheck:
|
||||
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
|
||||
interval: 10s
|
||||
retries: 5
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: unless-stopped
|
||||
command: redis-server --requirepass ${REDIS_PW}
|
||||
volumes:
|
||||
- redis:/data
|
||||
|
||||
app:
|
||||
image: nextcloud:fpm
|
||||
restart: unless-stopped
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
volumes:
|
||||
- nextcloud:/var/www/html
|
||||
- /srv/nc_data:/var/www/html/data # <- tank/nextcloud mount im LXC
|
||||
- ./config/php-custom.ini:/usr/local/etc/php/conf.d/zz-nextcloud.ini:ro
|
||||
environment:
|
||||
- MYSQL_HOST=db
|
||||
- MYSQL_DATABASE=nextcloud
|
||||
- MYSQL_USER=nextcloud
|
||||
- MYSQL_PASSWORD=${DB_PW}
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_HOST_PASSWORD=${REDIS_PW}
|
||||
- NEXTCLOUD_TRUSTED_DOMAINS=cloud.deinedomain.ch
|
||||
- TRUSTED_PROXIES=10.0.0.100 # <- Caddy-IP
|
||||
- OVERWRITEPROTOCOL=https
|
||||
- OVERWRITECLIURL=https://cloud.deinedomain.ch
|
||||
- PHP_MEMORY_LIMIT=1024M
|
||||
- PHP_UPLOAD_LIMIT=16G
|
||||
|
||||
web:
|
||||
image: nginx:alpine
|
||||
restart: unless-stopped
|
||||
depends_on: [app]
|
||||
volumes:
|
||||
- nextcloud:/var/www/html:ro
|
||||
- /srv/nc_data:/var/www/html/data:ro
|
||||
- ./config/nginx-nextcloud.conf:/etc/nginx/conf.d/default.conf:ro
|
||||
ports:
|
||||
- "8110:80" # <- Caddy proxyt hierauf
|
||||
|
||||
cron:
|
||||
image: nextcloud:fpm
|
||||
restart: unless-stopped
|
||||
depends_on: [db, redis]
|
||||
volumes:
|
||||
- nextcloud:/var/www/html
|
||||
- /srv/nc_data:/var/www/html/data
|
||||
entrypoint: /cron.sh
|
||||
|
||||
collabora:
|
||||
image: collabora/code:latest
|
||||
restart: unless-stopped
|
||||
cap_add: [MKNOD]
|
||||
environment:
|
||||
- aliasgroup1=https://cloud\.deinedomain\.ch:443
|
||||
- server_name=office.deinedomain.ch
|
||||
- username=admin
|
||||
- password=${COLLABORA_PW}
|
||||
- extra_params=--o:ssl.enable=false --o:ssl.termination=true
|
||||
ports:
|
||||
- "9980:9980" # <- Caddy proxyt office.deinedomain.ch hierauf
|
||||
|
||||
whiteboard:
|
||||
image: ghcr.io/nextcloud-releases/whiteboard:stable
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- NEXTCLOUD_URL=https://cloud.deinedomain.ch
|
||||
- JWT_SECRET_KEY=${WHITEBOARD_JWT}
|
||||
ports:
|
||||
- "3002:3002" # <- Caddy proxyt whiteboard.deinedomain.ch hierauf
|
||||
|
||||
# optional, aber rieger-im-geiste: schnellere previews
|
||||
imaginary:
|
||||
image: nextcloud/aio-imaginary:latest
|
||||
restart: unless-stopped
|
||||
cap_add: [SYS_NICE]
|
||||
environment:
|
||||
- PORT=9000
|
||||
|
||||
volumes:
|
||||
nextcloud:
|
||||
db:
|
||||
redis:
|
||||
@@ -0,0 +1,261 @@
|
||||
# homelab — aufbau-anleitung
|
||||
|
||||
Schritt-für-Schritt nach dem Manifest. Reihenfolge ist bewusst: erst Fundament (Pools, Datasets), dann Netzgerüst (Caddy/WireGuard/DNS), dann Apps. Domains und IPs hier als Platzhalter (`deinedomain.ch`, `10.0.0.x`) — anpassen.
|
||||
|
||||
---
|
||||
|
||||
## 1 — proxmox & rpool
|
||||
|
||||
Proxmox-Installer von USB. Bei der Disk-Auswahl die NVMe nehmen, Filesystem **zfs (RAID0)** (= Single-Disk ZFS), `ashift=12`, compression `lz4`. Das wird dein `rpool`.
|
||||
|
||||
Nach dem ersten Boot: Enterprise-Repo raus, No-Subscription-Repo rein, updaten.
|
||||
|
||||
```bash
|
||||
zpool status # rpool sollte ONLINE sein
|
||||
zfs set atime=off rpool
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2 — tank (mirror) anlegen
|
||||
|
||||
Disk-IDs holen (nie /dev/sdX verwenden — die wandern):
|
||||
|
||||
```bash
|
||||
ls -l /dev/disk/by-id/ | grep -i ata # die zwei 4TB-Platten identifizieren
|
||||
|
||||
zpool create -o ashift=12 \
|
||||
-O compression=lz4 -O atime=off -O xattr=sa \
|
||||
tank mirror /dev/disk/by-id/PLATTE1 /dev/disk/by-id/PLATTE2
|
||||
|
||||
zpool status tank # mirror, beide ONLINE
|
||||
```
|
||||
|
||||
Monatlichen Scrub einrichten:
|
||||
|
||||
```bash
|
||||
echo '0 3 1 * * root /sbin/zpool scrub tank' > /etc/cron.d/zfs-scrub
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3 — datasets & quotas
|
||||
|
||||
```bash
|
||||
zfs create -o quota=1300G tank/business
|
||||
zfs create -o quota=300G tank/private
|
||||
zfs create -o quota=600G -o recordsize=1M tank/immich
|
||||
zfs create -o quota=150G tank/paperless
|
||||
zfs create -o quota=150G tank/nextcloud
|
||||
zfs create -o quota=150G tank/dump
|
||||
zfs create -o quota=50G tank/gitea
|
||||
|
||||
zfs list -o name,quota,used tank # kontrollieren
|
||||
```
|
||||
|
||||
`tank/dump` als Proxmox-Backup-Storage eintragen: Datacenter → Storage → Add → Directory, Pfad `/tank/dump`, Inhalt "VZDump backup file".
|
||||
|
||||
---
|
||||
|
||||
## 4 — LXC-muster & mountpoints
|
||||
|
||||
Pro Container: rootfs auf `rpool`, Bulk-Daten auf `tank`. Beispiel Nextcloud (CTID 110), exklusives Dataset → verwalteter Mountpoint:
|
||||
|
||||
```bash
|
||||
pct set 110 -mp0 tank/nextcloud,mp=/data,backup=0
|
||||
```
|
||||
|
||||
Fileserver (102) braucht die **geteilten** Datasets als Bind-Mount (mehrere Dienste im selben LXC):
|
||||
|
||||
```bash
|
||||
pct set 102 -mp0 /tank/business,mp=/srv/business
|
||||
pct set 102 -mp1 /tank/private,mp=/srv/private
|
||||
```
|
||||
|
||||
**Hinweis unprivilegierte LXC:** in-container UID wird um 100000 verschoben. Damit Schreiben klappt, dem Dataset die verschobene UID/GID geben, z.B. für in-container `1000`:
|
||||
|
||||
```bash
|
||||
chown -R 101000:101000 /tank/business /tank/private
|
||||
```
|
||||
|
||||
Tipp: alle drei Zugriffsdienste (Samba/Filebrowser/Syncthing) **im selben LXC 102** halten — dann gibt es keine container-übergreifenden UID-Probleme.
|
||||
|
||||
---
|
||||
|
||||
## 5 — fileserver: samba + filebrowser + syncthing (LXC 102)
|
||||
|
||||
Debian-LXC, dann:
|
||||
|
||||
**Samba** (zwei Shares):
|
||||
|
||||
```bash
|
||||
apt install -y samba
|
||||
groupadd buro; useradd -M -G buro karim; smbpasswd -a karim
|
||||
```
|
||||
|
||||
`/etc/samba/smb.conf` (Auszug):
|
||||
|
||||
```ini
|
||||
[business]
|
||||
path = /srv/business
|
||||
valid users = @buro
|
||||
read only = no
|
||||
force group = buro
|
||||
create mask = 0660
|
||||
directory mask = 0770
|
||||
|
||||
[private]
|
||||
path = /srv/private
|
||||
valid users = karim
|
||||
read only = no
|
||||
create mask = 0600
|
||||
directory mask = 0700
|
||||
```
|
||||
|
||||
```bash
|
||||
systemctl restart smbd
|
||||
```
|
||||
|
||||
Mounten am Mac: Finder → ⌘K → `smb://10.0.0.102/business`. Linux: `mount -t cifs //10.0.0.102/business /mnt/business -o user=karim`.
|
||||
|
||||
**Filebrowser** (Web + Share-Links), rooted über beide Shares:
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/filebrowser/get/master/get.sh | bash
|
||||
filebrowser config init
|
||||
filebrowser config set --root /srv --address 0.0.0.0 --port 8080
|
||||
filebrowser users add karim DEINPASSWORT --perm.admin
|
||||
```
|
||||
|
||||
Als systemd-Service einrichten (Unit anlegen, enable). Caddy proxyt später `files.deinedomain.ch` → `10.0.0.102:8080`.
|
||||
|
||||
**Syncthing** (das "Drive"): `apt install syncthing`, als User-Service starten, Web-UI auf `:8384`, Ordner `/srv/business` (oder gezielte Unterordner) freigeben und auf dem Laptop verbinden. Da Syncthing plain files schreibt, bleibt SMB parallel nutzbar.
|
||||
|
||||
---
|
||||
|
||||
## 6 — ordnerstruktur anlegen
|
||||
|
||||
```bash
|
||||
# business
|
||||
mkdir -p /srv/business/{_vorlagen,_buro,_bibliothek/{details,cad-blocks,materialien,referenzen},projekte}
|
||||
mkdir -p /srv/business/projekte/2026-001_beispiel/{00_admin,01_grundlagen,02_vorstudien,03_vorprojekt,04_bauprojekt,05_bewilligung,06_ausschreibung,07_ausfuhrung,08_abschluss,cad,plaene,visualisierung,fotos}
|
||||
|
||||
# private
|
||||
mkdir -p /srv/private/{dokumente/2026,finanzen,studium,projekte,scans,archiv}
|
||||
|
||||
chown -R 101000:101000 /srv/business /srv/private
|
||||
```
|
||||
|
||||
Für neue Projekte das `2026-001_beispiel`-Gerüst als Vorlage kopieren (oder ein kleines Skript in `_vorlagen/` ablegen).
|
||||
|
||||
---
|
||||
|
||||
## 7 — caddy (LXC 100)
|
||||
|
||||
`apt install caddy`, dann `/etc/caddy/Caddyfile`:
|
||||
|
||||
```
|
||||
files.deinedomain.ch { reverse_proxy 10.0.0.102:8080 }
|
||||
cloud.deinedomain.ch { reverse_proxy 10.0.0.110:80 }
|
||||
fotos.deinedomain.ch { reverse_proxy 10.0.0.111:2283 }
|
||||
www.deinedomain.ch { root * /srv/www; file_server }
|
||||
```
|
||||
|
||||
```bash
|
||||
systemctl reload caddy
|
||||
```
|
||||
|
||||
Caddy holt TLS automatisch. Am Router nur 80/443 auf Caddy forwarden.
|
||||
|
||||
---
|
||||
|
||||
## 8 — wireguard (LXC 101, wg-easy)
|
||||
|
||||
Docker im LXC, wg-easy gibt dir eine GUI für Peers:
|
||||
|
||||
```bash
|
||||
docker run -d --name wg-easy --cap-add NET_ADMIN --cap-add SYS_MODULE \
|
||||
-e WG_HOST=vpn.deinedomain.ch -e PASSWORD_HASH='...' \
|
||||
-p 51820:51820/udp -p 51821:51821/tcp \
|
||||
-v ~/.wg-easy:/etc/wireguard --restart unless-stopped \
|
||||
ghcr.io/wg-easy/wg-easy
|
||||
```
|
||||
|
||||
Am Router **nur** UDP 51820 forwarden. Peers in der UI anlegen, QR scannen. Im Tunnel erreichst du das interne Subnetz → alle privaten Dienste, ohne sie je öffentlich zu machen.
|
||||
|
||||
---
|
||||
|
||||
## 9 — adguard home (LXC 103)
|
||||
|
||||
Installieren, als DNS im Router (oder im WireGuard-Profil) setzen. Interne A-Records anlegen, damit `cloud.deinedomain.ch` etc. intern auf die LXC-IPs zeigen → gleiche Hostnamen drinnen wie draussen.
|
||||
|
||||
---
|
||||
|
||||
## 10 — app-stacks (NC, immich, paperless)
|
||||
|
||||
Pro Stack ein Docker-LXC, compose-File ins Gitea. Muster für **Nextcloud** (110):
|
||||
|
||||
- compose mit `app`, `db` (Postgres), `redis`. Volumes für App/DB auf rootfs (NVMe).
|
||||
- Daten-Volume auf das gemountete `/data` (= `tank/nextcloud`).
|
||||
- Caddy proxyt `cloud.deinedomain.ch`.
|
||||
|
||||
**Immich** (111) und **Paperless-ngx** (113) analog — offizielle compose-Files nehmen (ändern sich regelmässig, drum upstream statt hier einfrieren) und nur den Daten-Pfad auf das jeweilige `tank/...`-Mount zeigen. Paperless' Consume-Ordner auf `/srv/private/scans` zeigen lassen → Scans landen automatisch im Archiv.
|
||||
|
||||
---
|
||||
|
||||
## 11 — monitoring
|
||||
|
||||
- **Uptime Kuma** (140): jeden Dienst als HTTP-Check anlegen, Push aufs Handy (ntfy/Telegram).
|
||||
- **Beszel** (141): Agent auf dem Proxmox-Host, Pool-Auslastung im Blick.
|
||||
- **Pool-Alert <85%** als simpler Cron:
|
||||
|
||||
```bash
|
||||
cat > /etc/cron.hourly/zfs-capacity << 'EOF'
|
||||
#!/bin/bash
|
||||
cap=$(zpool list -H -o capacity tank | tr -d '%')
|
||||
[ "$cap" -ge 85 ] && echo "tank $cap%" | ntfy publish homelab
|
||||
EOF
|
||||
chmod +x /etc/cron.hourly/zfs-capacity
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 12 — backup (sanoid + restic)
|
||||
|
||||
**Snapshots** (sanoid) — `/etc/sanoid/sanoid.conf`:
|
||||
|
||||
```ini
|
||||
[tank/business]
|
||||
use_template = prod
|
||||
[tank/private]
|
||||
use_template = prod
|
||||
[tank/immich]
|
||||
use_template = prod
|
||||
|
||||
[template_prod]
|
||||
hourly = 24
|
||||
daily = 30
|
||||
monthly = 6
|
||||
autosnap = yes
|
||||
autoprune = yes
|
||||
```
|
||||
|
||||
**Offsite** zur Hetzner Storage Box (Restic über SFTP):
|
||||
|
||||
```bash
|
||||
export RESTIC_REPOSITORY="sftp:uXXXXX@uXXXXX.your-storagebox.de:/backup"
|
||||
export RESTIC_PASSWORD_FILE=/root/.restic-pw
|
||||
restic init
|
||||
restic backup /tank/business /tank/private /tank/nextcloud /tank/immich /tank/paperless /tank/gitea
|
||||
restic forget --keep-daily 14 --keep-weekly 8 --keep-monthly 12 --prune
|
||||
```
|
||||
|
||||
In einen täglichen Cron packen.
|
||||
|
||||
**Restore-Test (Pflicht):** einmal `restic restore latest --target /tmp/restoretest --include /tank/private/dokumente` und prüfen ob die Files stimmen. Ein ungetestetes Backup ist kein Backup.
|
||||
|
||||
---
|
||||
|
||||
## reihenfolge auf einen blick
|
||||
|
||||
1. Proxmox + rpool → 2. tank Mirror + Scrub → 3. Datasets + Quotas → 4. Fileserver (Samba/Filebrowser/Syncthing) + Ordner → 5. Caddy + WireGuard + AdGuard → 6. Kern-Apps → 7. Monitoring → 8. Backup + Restore-Test → 9. Erweiterungen nach Lust.
|
||||
@@ -0,0 +1,232 @@
|
||||
# homelab manifest
|
||||
|
||||
> Referenz für Aufbau und Betrieb meines Proxmox-Servers.
|
||||
> Architekturarbeit + privat. Ein Mini-PC, 1× 1TB NVMe, 2× 4TB, 1× 2TB externe HDD.
|
||||
> An dieses Dokument richte ich die Maschine aus — nicht umgekehrt.
|
||||
|
||||
---
|
||||
|
||||
## prinzipien
|
||||
|
||||
1. **Filesystem-first.** Das Dateisystem ist die Wahrheit. Web-UIs, SMB und Sync sind nur Clients darauf. Keine App "besitzt" die Daten allein.
|
||||
2. **FOSS und self-hosted**, config-as-code. Jede Konfiguration (Caddyfile, compose, ZFS-Layout, Samba) liegt versioniert im Gitea.
|
||||
3. **Öffentlich = Caddy, privat = WireGuard-only.** Nach aussen offen: nur der WireGuard-Port plus 80/443 für die öffentlichen Websites. Alles andere erreiche ich nur durch den Tunnel.
|
||||
4. **RAID ist kein Backup.** 3-2-1: 3 Kopien, 2 Medien, 1 offsite.
|
||||
5. **ZFS-Pool nie über ~85%.** Copy-on-Write braucht Luft. Auslastung wird überwacht, nicht geschätzt.
|
||||
6. **Grosse Daten ≠ vzdump.** Bulk-Mounts laufen mit `backup=0`, separat gesichert per `zfs send`/Restic. vzdump nur für die schlanken rootfs.
|
||||
7. **Bewusster Hybrid.** Single-purpose Infra → native LXC. Echte Multi-Container-App-Stacks → ein Docker-LXC pro Stack. Nie "ein Docker pro LXC für alles".
|
||||
8. **Ein Werkzeug pro Aufgabe.** Lieber ein schlankes Tool das eine Sache gut macht als ein Monolith der alles halb kann.
|
||||
9. **Reserve ist eingeplant**, nicht zufällig. Quotas sind Sicherheitsdeckel, kein starres Partitionieren.
|
||||
|
||||
---
|
||||
|
||||
## hardware & pools
|
||||
|
||||
| Pool | Medium | Topologie | nutzbar | Zweck |
|
||||
|------|--------|-----------|---------|-------|
|
||||
| `rpool` | 1× 1TB NVMe | Single-Disk ZFS | ~900 GiB | OS, alle rootfs, **Datenbanken**, oft gelesene Dienste |
|
||||
| `tank` | 2× 4TB | **ZFS Mirror (RAID1)** | ~3.6 TiB | schwere Daten: Shares, Nextcloud, Immich, Paperless |
|
||||
| — | 1× 2TB ext. HDD | — | ~1.8 TiB | lokales Backup-Ziel (`zfs recv` / Restic) |
|
||||
|
||||
ZFS-Settings beide Pools: `ashift=12`, `compression=lz4`, `atime=off`. Mirror überlebt einen Plattentod und heilt Bitrot beim Scrub (monatlich). NVMe ist Einzelplatte → keine Redundanz, aber nichts Unersetzliches liegt nur dort.
|
||||
|
||||
---
|
||||
|
||||
## dienste & container
|
||||
|
||||
Native LXC für Infra, ein Docker-LXC pro echtem App-Stack. CTID nach Rolle in Blöcken.
|
||||
**[K]** = Kern (zuerst), **[+]** = Erweiterung (nach Bedarf).
|
||||
|
||||
| CTID | Dienst | Typ | rootfs | Daten | für | |
|
||||
|------|--------|-----|--------|-------|-----|--|
|
||||
| 100 | Caddy (Reverse Proxy) | native | 8G | — | beide | K |
|
||||
| 101 | WireGuard (wg-easy) | docker | 4G | — | beide | K |
|
||||
| 102 | Fileserver — Samba + Filebrowser + Syncthing | native | 8G | `tank/business`, `tank/private` | beide | K |
|
||||
| 103 | AdGuard Home (DNS + Adblock) | native | 4G | — | beide | + |
|
||||
| 110 | Nextcloud (app+db+redis) | docker | 40G | `tank/nextcloud` | privat | K |
|
||||
| 111 | Immich (server+ML+db+redis) | docker | 60G | `tank/immich` | privat | K |
|
||||
| 112 | Vaultwarden | native | 4G | — | beide | K |
|
||||
| 113 | Paperless-ngx (DMS + OCR) | docker | 30G | `tank/paperless` | Arbeit | + |
|
||||
| 114 | Stirling-PDF (PDF-Toolkit) | docker | 8G | — | Arbeit | + |
|
||||
| 115 | Collabora Online (Office in NC) | docker | 16G | — | Arbeit | + |
|
||||
| 116 | BookStack (Büro-Wiki / Detailbibliothek) | docker | 8G | — | Arbeit | + |
|
||||
| 120 | Gitea | native | 8G | `tank/gitea` | Arbeit | K |
|
||||
| 121 | CI-Runner (Forgejo/Woodpecker) | docker | 16G | — | Arbeit | + |
|
||||
| 122 | Penpot (Open-Source Figma) | docker | 16G | — | Arbeit | + |
|
||||
| 130+ | Websites (Hugo/static via Caddy) | native | 8G ea. | — | Arbeit | K |
|
||||
| 140 | Uptime Kuma (Monitoring/Status) | docker | 8G | — | Betrieb | + |
|
||||
| 141 | Beszel (System-/Pool-Metriken) | docker | 8G | — | Betrieb | + |
|
||||
| 142 | Homepage (Dashboard / "Launcher") | docker | 4G | — | Betrieb | + |
|
||||
| 160 | Jellyfin (Medien) | docker | 16G | `tank/media`* | privat | + |
|
||||
| 161 | Navidrome (Musik) | native | 8G | `tank/media`* | privat | + |
|
||||
| 162 | Linkwarden (Referenz-/Bookmark-Archiv) | docker | 16G | — | beide | + |
|
||||
| 163 | Miniflux (RSS) | docker | 4G | — | privat | + |
|
||||
|
||||
Nummernschema: 100–109 Infra/Netz · 110–119 Cloud-Apps · 120–129 Dev · 130–139 Web · 140–149 Betrieb · 160–169 Medien/Privat. Lücken bleiben.
|
||||
|
||||
\* Medienbibliothek braucht eine bewusste Budget-Entscheidung — bei echter Film-Sammlung eher eine zusätzliche Platte einplanen statt den Tank zu sprengen.
|
||||
|
||||
---
|
||||
|
||||
## app-katalog: was sonst noch sinn macht
|
||||
|
||||
**Fürs Büro (stark empfohlen)**
|
||||
- **Paperless-ngx** — Dokumentenarchiv mit OCR und Volltextsuche. Löst das Papierproblem: Verträge, Rechnungen, Baugesuche, Korrespondenz scannen → durchsuchbar. Watch-Ordner zieht Scans automatisch ein.
|
||||
- **Stirling-PDF** — alles für PDFs: zusammenführen, teilen, OCR, komprimieren, signieren. Du lebst in Plan-PDFs und Eingaben.
|
||||
- **Collabora Online** — Office-Dokumente direkt im Browser/in Nextcloud editieren. XLSX-kompatibel für Raumlisten.
|
||||
- **BookStack** — Büro-Wiki: Detailbibliothek-Doku, Standards, Vorlagen, Projektwissen an einem Ort.
|
||||
|
||||
**Für den Betrieb des Homelabs**
|
||||
- **Uptime Kuma** — weisst sofort wenn ein Dienst down ist (inkl. Push aufs Handy).
|
||||
- **Beszel** (oder Netdata) — System- und Pool-Metriken → erfüllt die <85%-Alert-Regel.
|
||||
- **Homepage** — eine Startseite die alle Dienste verlinkt, das DSM-Launcher-Gefühl.
|
||||
|
||||
**Privat**
|
||||
- **AdGuard Home** — netzweiter Tracker-/Werbeblocker + interner DNS (löst gleich die internen Hostnamen).
|
||||
- **Linkwarden** — visuelles Referenz-/Bookmark-Archiv. Beruflich top für Präzedenzfälle, Materialien, Detail-Ideen.
|
||||
- **Jellyfin** — Filme/Serien, ergänzt Navidrome (Musik).
|
||||
- **Miniflux** — minimalistischer RSS-Reader.
|
||||
|
||||
**Für deine Software (openbureau)**
|
||||
- **CI-Runner** (Forgejo Actions / Woodpecker) — Tests + Auto-Deploy der Hugo-Site, Builds für Rapport/Dossier/Pendenz.
|
||||
- **Penpot** — Open-Source Figma für UI/Mockups.
|
||||
|
||||
---
|
||||
|
||||
## der zugriff auf den share (die synology-erfahrung, nachgebaut)
|
||||
|
||||
Synologys Magie ist ein Bündel von Zugriffswegen auf *dieselben* Dateien. Filesystem-first nachgebaut, alles im Fileserver-LXC (102):
|
||||
|
||||
- **Samba** → klassisches Netzlaufwerk auf Mac/Linux/Windows. Voller nativer Speed für Rhino/CAD vom Desktop. Der Hauptzugang für die Arbeit.
|
||||
- **Filebrowser** → Web-"File Station": browsen, hoch-/runterladen, **Share-Links** mit Passwort/Ablauf. Liest das FS live, kein DB-Desync. Auch im Handy-Browser brauchbar (PWA).
|
||||
- **Syncthing** → das "Synology Drive": ausgewählte Ordner kontinuierlich auf Laptop syncen, offline arbeiten. Files bleiben *plain* auf der Platte → SMB funktioniert weiter parallel.
|
||||
- **Homepage** → eine Kachel-Startseite über alles, der Launcher.
|
||||
|
||||
> Bewusst *nicht* Seafile: das wäre der bessere Sync-Client, speichert Dateien aber in eigenem Block-Format → dann ginge SMB auf denselben Daten nicht mehr. Verletzt filesystem-first.
|
||||
|
||||
---
|
||||
|
||||
## shares & datenstruktur
|
||||
|
||||
**Zwei getrennte Datasets** (= zwei Samba-Shares): saubere Trennung von Rechten, Snapshots und Backup-Priorität. Ein künftiger Mitarbeiter/Praktikant kriegt `business`, nie `private`.
|
||||
|
||||
### `tank/business` — Architekturbüro
|
||||
|
||||
Projektbezogen, Phasen nach SIA 102. Unterstriche sortieren Nicht-Projekt-Ordner nach oben.
|
||||
|
||||
```
|
||||
business/
|
||||
├── _vorlagen/ Planköpfe, Dokumentvorlagen, CAD-Templates
|
||||
├── _bibliothek/
|
||||
│ ├── details/ wiederverwendbare Konstruktionsdetails
|
||||
│ ├── cad-blocks/
|
||||
│ ├── materialien/
|
||||
│ └── referenzen/ Präzedenzfälle, Vorbilder
|
||||
├── _buro/ Administration, Buchhaltung, Marketing
|
||||
└── projekte/
|
||||
└── 2026-001_projektname/
|
||||
├── 00_admin/ Verträge, Honorar, Protokolle, Korrespondenz
|
||||
├── 01_grundlagen/ Bestand, Vermessung, Gutachten, Fotos
|
||||
├── 02_vorstudien/ SIA 21 — Machbarkeit, Wettbewerb
|
||||
├── 03_vorprojekt/ SIA 31
|
||||
├── 04_bauprojekt/ SIA 32
|
||||
├── 05_bewilligung/ SIA 33 — Baugesuch
|
||||
├── 06_ausschreibung/ SIA 41
|
||||
├── 07_ausfuhrung/ SIA 51/52/53 — Werkplanung, Baustelle
|
||||
├── 08_abschluss/ Abnahme, Revision, Doku
|
||||
├── cad/ Rhino .3dm (working)
|
||||
├── plaene/ ausgegebene PDF-Pläne
|
||||
├── visualisierung/ Renderings
|
||||
└── fotos/ Baustellenfotos
|
||||
```
|
||||
|
||||
Projektnummer `JJJJ-NNN_name` → sortierbar, gleiche Logik wie die CTIDs.
|
||||
|
||||
### `tank/private` — persönlich
|
||||
|
||||
```
|
||||
private/
|
||||
├── dokumente/ Verträge, Versicherung, Behörden
|
||||
│ └── 2026/
|
||||
├── finanzen/
|
||||
├── studium/ HSLU Master — Module, Arbeiten, Thesis
|
||||
├── projekte/ persönliche Projekte
|
||||
├── scans/ Eingang für Paperless
|
||||
└── archiv/
|
||||
```
|
||||
|
||||
Nextcloud bleibt schlank für Kalender/Kontakte/Mobile und leichtes Teilen (eigenes `tank/nextcloud`). Optional kann es `tank/private` als External Storage einhängen, damit die App am Handy auch an die privaten Files kommt — mit dem bekannten Scan-Vorbehalt.
|
||||
|
||||
---
|
||||
|
||||
## speicher-aufteilung (thin + quota, mit reserve)
|
||||
|
||||
### tank — ~3.6 TiB nutzbar
|
||||
|
||||
| Dataset | Quota | Zweck |
|
||||
|---------|-------|-------|
|
||||
| `tank/business` | **1.3 TiB** | Architekturarbeit (SMB + Filebrowser + Syncthing) |
|
||||
| `tank/immich` | **600 GiB** | Foto-/Videobibliothek |
|
||||
| `tank/private` | **300 GiB** | persönliche Files (SMB + Filebrowser + Syncthing) |
|
||||
| `tank/paperless` | **150 GiB** | Dokumentenarchiv |
|
||||
| `tank/nextcloud` | **150 GiB** | Sync/Mobile/Teilen |
|
||||
| `tank/dump` | **150 GiB** | vzdump der rootfs |
|
||||
| `tank/gitea` | **50 GiB** | Repos |
|
||||
| **Summe Quotas** | **~2.7 TiB** | = ~72% bei voll ausgereizten Quotas |
|
||||
| **freie Reserve** | **~0.9 TiB** | bewusst unallokiert → Pool bleibt unter 85% |
|
||||
|
||||
> Quotas sind Deckel, nicht Reservierungen. Sie dürfen den Pool in Summe überschreiten — hier tun sie's bewusst *nicht*, damit selbst bei vollen Quotas Luft bleibt. Medien (Jellyfin) würden diese Reserve fressen → eigene Platte einplanen.
|
||||
|
||||
### rpool — ~900 GiB nutzbar
|
||||
|
||||
OS + ARC + alle rootfs (Kern + Erweiterungen): grob **~350 GiB**. Datenbanken liegen in den rootfs auf der NVMe (schnelle Random-IO). **Reserve ~550 GiB** — bewusst grosszügig: Snapshots, künftige Dienste, Option ein heisses Dataset (z.B. Immich-Thumbnails) auf die NVMe zu pinnen.
|
||||
|
||||
---
|
||||
|
||||
## mount-konventionen
|
||||
|
||||
Zwei Fälle, bewusst unterschiedlich:
|
||||
|
||||
- **Exklusive Service-Daten** (Nextcloud, Immich, Paperless, Gitea) → Proxmox-verwalteter Mountpoint, eigenes subvol:
|
||||
```
|
||||
mp0: tank/nextcloud,mp=/data,backup=0,size=150G
|
||||
```
|
||||
- **Geteilte Datasets** (`business`, `private`, von Samba+Filebrowser+Syncthing im selben LXC genutzt) → **Bind-Mount** des Host-Pfads:
|
||||
```
|
||||
mp0: /tank/business,mp=/srv/business
|
||||
mp1: /tank/private,mp=/srv/private
|
||||
```
|
||||
|
||||
`backup=0` auf allen grossen Mounts. Dataset-Settings nach Inhalt: `recordsize=1M` für Immich/Renderings, Default 128K sonst.
|
||||
|
||||
---
|
||||
|
||||
## netz & zugriff
|
||||
|
||||
- **Öffentlich:** Router → nur 80/443 → Caddy (100), macht TLS und routet zu den öffentlichen Websites. Sonst nichts nach aussen.
|
||||
- **Privat:** Router → nur WireGuard-UDP → wg-easy (101). Proxmox-UI, Nextcloud, Immich, Vaultwarden, Filebrowser, Paperless: **nur** durch den Tunnel über interne Hostnamen.
|
||||
- **Interne Namen** über AdGuard (103) als DNS + Caddy als internen Proxy → keine IPs merken.
|
||||
|
||||
---
|
||||
|
||||
## backup-strategie (3-2-1)
|
||||
|
||||
1. **Snapshots** (sanoid) auf allen `tank`-Datendatasets — Schutz gegen "ups, gelöscht".
|
||||
2. **Lokal:** `zfs send`/Restic der wichtigen Datasets + `tank/dump` → 2TB externe HDD.
|
||||
3. **Offsite:** Restic der Unersetzlichen (`business`, `private`, `immich`, `nextcloud`, `paperless`, `gitea`, `vaultwarden`) → Hetzner Storage Box, verschlüsselt + versioniert.
|
||||
|
||||
> RAID gegen Plattentod. Snapshots gegen Fehler. Offsite gegen Brand und Ransomware. Alle drei. **Restore mindestens einmal testen** — ein ungetestetes Backup ist kein Backup.
|
||||
|
||||
---
|
||||
|
||||
## checkliste
|
||||
|
||||
- [ ] Proxmox auf NVMe, `rpool` als Single-Disk ZFS
|
||||
- [ ] `tank` als Mirror, Settings setzen, Scrub-Cron
|
||||
- [ ] Datasets + Quotas anlegen
|
||||
- [ ] Fileserver-LXC (102): Samba (2 Shares) + Filebrowser + Syncthing
|
||||
- [ ] Ordnerstruktur business/private anlegen
|
||||
- [ ] Caddy + WireGuard + AdGuard (Netzgerüst)
|
||||
- [ ] Kern-Apps (NC, Immich, Vaultwarden, Gitea), dann Erweiterungen nach Bedarf
|
||||
- [ ] Monitoring (Uptime Kuma, Beszel) + Pool-Alert <85%
|
||||
- [ ] sanoid + Restic offsite, **Restore testen**
|
||||
@@ -0,0 +1,81 @@
|
||||
# homelab rebuild — projekt-kontext / handoff
|
||||
|
||||
> Dieses Dokument bündelt den Stand des Homelab-Rebuild-Projekts, damit ein neuer Assistent (z.B. Claude Code) sofort im Bild ist. Stand: Juni 2026.
|
||||
|
||||
## ziel
|
||||
|
||||
Sauberer Komplett-Neuaufbau des Proxmox-Homelabs: PVE neu installieren mit **ZFS-Root**, den ZFS-Mirror `tank` neu anlegen, alles am geschriebenen **homelab-manifest** ausrichten. Vorher: backup-first Migration (alles sichern, verifizieren, *dann* wipen).
|
||||
|
||||
## kontext zur person
|
||||
|
||||
Architekt (Masterstudium berufsbegleitend, Luzern) mit kleinem Büro-Kontext, FOSS-/Linux-Mensch, entwickelt eigene Software-Suite **openbureau** (Rapport, Dossier, Pendenz) mit Claude Code. Vor-Architektur-Hintergrund: Elektroinstallation + BIM-Projektleitung. Werte: Datensouveränität, config-as-code, self-hosted.
|
||||
|
||||
## hardware
|
||||
|
||||
- Mini-PC Server
|
||||
- 1× 1TB NVMe — **aktuell LVM-Thin (`local-lvm`) Root**, soll nach Reinstall **ZFS-Root (`rpool`)** werden
|
||||
- 2× 4TB — **ZFS Mirror `tank`** (~3.6 TiB nutzbar, die Bulk-Daten)
|
||||
- 1× 2TB externe HDD — `ext-hdd01` unter `/mnt/pve/ext-hdd01` (Directory, Backup-Ziel)
|
||||
- 1× 2TB USB-C NVMe SSD — neu, für den **PBS-Datastore** (Pfad ist beim User zu bestätigen, Annahme `/mnt/pve/pbs-ssd`)
|
||||
|
||||
## aktuelles inventar (echte CTIDs/VMIDs)
|
||||
|
||||
LXC: 100 nextcloud · 101 immich · 102 vaultwarden · 103 mailserver · 105 obsidian-livesync · 106 gitea · 107 openbureau · 110 flarum · 111 nginx-proxy-manager · 200 kgva-website · 210 openbureau-web · 211 rapport-website · 212 dossier-website · 219 dev.openbureau · 220 trattoriamarina.ch · 900 rapport-server
|
||||
VM: 500 windows · 800 homeassistant-os
|
||||
|
||||
## getroffene entscheidungen
|
||||
|
||||
**Architektur-Prinzipien:** filesystem-first (das FS ist die Wahrheit, Web/SMB/Sync sind Clients); FOSS + config-as-code in Gitea; öffentlich = Caddy / privat = WireGuard-only; RAID ≠ Backup (3-2-1); ZFS-Pool nie über 85%; grosse Daten mit `backup=0` + separat sichern; bewusster Hybrid (single-purpose Infra = native LXC, echte Multi-Container-Apps = ein Docker-LXC pro Stack).
|
||||
|
||||
**ZFS:** tank = Mirror (einzige sinnvolle Redundanz bei 2 Platten). rpool = Single-Disk-ZFS für Einheitlichkeit. ashift=12, lz4, atime=off.
|
||||
|
||||
**Speicher:** thin provisioning mit Quotas als Sicherheitsdeckel (nicht starr partitionieren). Reserve bewusst eingeplant.
|
||||
|
||||
**Reverse Proxy:** Caddy statt NPM (Binary, auto-HTTPS, Caddyfile config-as-code). Edge-Härtung (TLS, Header, CrowdSec) bei Caddy, nicht in den App-Containern.
|
||||
|
||||
**Nextcloud:** Docker-compose-Stack (NICHT native Rieger, NICHT AIO) — aber mit Rieger-Tuning-Werten (php opcache/JIT, APCu, Redis-Locking, MariaDB READ-COMMITTED). fpm + nginx-Sidecar. Inkl. Collabora + Whiteboard-Backend. Compose-Datei liegt vor (siehe Dateien).
|
||||
|
||||
**Supabase:** nur für die eigenen openbureau-Apps als eigener, getrennter Stack — NICHT als Nextcloud-DB. NC bekommt eine nackte MariaDB/Postgres. NC- und Supabase-DBs strikt getrennt.
|
||||
|
||||
**Shares:** zwei getrennte Datasets/Samba-Shares — `tank/business` (projektbasiert nach SIA-Phasen 00–08) und `tank/private` (nach Lebensbereichen). Synology-artiger Zugriff via Samba + Filebrowser (Web + Share-Links) + Syncthing (das "Drive"). Bewusst kein Seafile (sperrt Files weg → SMB ginge nicht mehr).
|
||||
|
||||
**Architektur-spezifische Erweiterungen empfohlen:** Paperless-ngx (DMS+OCR), Stirling-PDF, BookStack-Wiki, Speckle (BIM-Datenhub, bei BIM-Arbeit), Authentik (SSO sobald mehr Leute). Plus Betrieb: Uptime Kuma, Beszel, Homepage. Privat: AdGuard Home, Linkwarden, Jellyfin/Navidrome, Miniflux.
|
||||
|
||||
**Backup-Kette:**
|
||||
|
||||
| Schicht | Werkzeug | Lokal | Offsite |
|
||||
|---------|----------|-------|---------|
|
||||
| Container + VMs | PBS | neue SSD | (aus Gitea reproduzierbar) |
|
||||
| Tank-Daten | Restic | ext-hdd01 (HDD) | Hetzner Storage Box |
|
||||
|
||||
PBS läuft als privileged LXC, Datastore auf der SSD (überlebt den Wipe). Nach Reinstall: frisches PBS → bestehenden Datastore wieder einhängen → Gäste zurück. Tank: neu anlegen → Restic zurück. Verschlüsselungs-Key (falls genutzt) MUSS off-box gesichert werden.
|
||||
|
||||
## migrations-reihenfolge
|
||||
|
||||
1. PBS aufsetzen (Skript vorhanden), Datastore auf SSD.
|
||||
2. Alle 18 Gäste per `vzdump --mode stop --storage pbs` sichern.
|
||||
3. Stateful-Dienste app-level dumpen (mailserver-Backup + DKIM/DNS notieren; DB-Dumps).
|
||||
4. Tank-Daten per Restic auf ext-hdd01 + Hetzner.
|
||||
5. **Verifizieren + je 1 Container- und 1 Tank-Restore testen.**
|
||||
6. Erst dann wipen: PVE neu auf NVMe (ZFS-root, NUR NVMe als Ziel), tank neu als Mirror.
|
||||
7. Zurückspielen: PBS-Datastore wieder einhängen → Gäste; tank-Datasets nach Manifest → Restic zurück. Mailserver zuletzt.
|
||||
8. Laufendes Backup-System einrichten (PBS-Job + sanoid + Restic-Cron).
|
||||
|
||||
## offene punkte (vom user noch zu liefern/bestätigen)
|
||||
|
||||
- [ ] Discovery-Output: `zfs list -o name,used,mountpoint -r tank` + `pct config <id>` aller Container → um die echten Restic-Pfade und `backup=0/1`-Flags zu fixieren. **Wichtig:** App-Daten liegen in `subvol-*`-Mountpoints (z.B. `subvol-100-disk-0` = Nextcloud-Data) — nicht über Dataset-Namen auto-erkennbar.
|
||||
- [ ] Was ist `103 mailserver` genau (docker-mailserver / Mailcow)? → bestimmt Backup-Kommando + DKIM-Handling.
|
||||
- [ ] Stateful-Bestätigung: 107 openbureau & 900 rapport-server = Postgres? 105 obsidian-livesync = CouchDB? 219 dev.openbureau = ?
|
||||
- [ ] Pfad/Name der neuen SSD bestätigen (`cat /etc/pve/storage.cfg`).
|
||||
- [ ] Hetzner Storage Box vorhanden oder Offsite später?
|
||||
- [ ] PBS-Setup-Skript gelaufen? PBS sauber in PVE sichtbar?
|
||||
|
||||
## produzierte dateien
|
||||
|
||||
- `homelab-manifest.md` — die Referenz-Architektur (Pools, Dienste, CTID-Schema, Speicher-Quotas, Shares+Struktur, Netz, Backup, App-Katalog).
|
||||
- `homelab-anleitung.md` — Aufbau-Anleitung Schritt 1–12 (Proxmox → Pools → Datasets → Fileserver → Caddy/WireGuard/AdGuard → Apps → Monitoring → Backup).
|
||||
- `nextcloud-stack/` — docker-compose.yml + config/ (php-custom.ini, mariadb-tuning.cnf, nginx-nextcloud.conf) + .env.example.
|
||||
- `migrations-backup-runbook.md` — Backup-vor-dem-Wipe Runbook mit echtem Inventar.
|
||||
- `pbs-und-tank-backup-anleitung.md` — PBS aufsetzen + Tank sichern, Schritt für Schritt.
|
||||
- `setup-pbs.sh` — interaktives, ungefährliches Skript das den PBS-LXC provisioniert.
|
||||
- `homelab-projekt-kontext.md` — dieses Dokument.
|
||||
@@ -0,0 +1,11 @@
|
||||
[mariadb]
|
||||
transaction_isolation = READ-COMMITTED
|
||||
binlog_format = ROW
|
||||
innodb_file_per_table = 1
|
||||
innodb_buffer_pool_size = 1G
|
||||
innodb_io_capacity = 4000
|
||||
innodb_flush_log_at_trx_commit = 2
|
||||
character-set-server = utf8mb4
|
||||
collation-server = utf8mb4_general_ci
|
||||
skip-character-set-client-handshake
|
||||
max_connections = 200
|
||||
@@ -0,0 +1,148 @@
|
||||
# migrations-backup runbook — vor dem full-wipe
|
||||
|
||||
> Ziel: alles sichern → verifizieren → NVMe + Tank wipen → Proxmox & Tank frisch nach Manifest → zurückspielen.
|
||||
> **Regel: solange das Backup nicht getestet ist, wird nichts gewiped.** Nach dem Wipe gibt es keinen `zpool import` mehr — das Backup ist die einzige Kopie.
|
||||
|
||||
## ausgangslage (aus deinen screenshots)
|
||||
|
||||
- Root liegt auf **local-lvm (LVM-Thin)**, nicht ZFS → Reinstall ist der einzige Weg zum ZFS-Root.
|
||||
- **tank** = ZFS-Mirror (die Bulk-Daten).
|
||||
- **ext-hdd01** (`/mnt/pve/ext-hdd01`, Directory, Backup-fähig) = Backup-Ziel, überlebt den Wipe.
|
||||
|
||||
## dein inventar
|
||||
|
||||
| ID | Name | Typ | Annahme: stateful? |
|
||||
|----|------|-----|--------------------|
|
||||
| 100 | nextcloud | LXC | DB + Files |
|
||||
| 101 | immich | LXC | Postgres + Files |
|
||||
| 102 | vaultwarden | LXC | Daten/SQLite |
|
||||
| 103 | mailserver | LXC | **KRITISCH** — Mail + DB + DKIM |
|
||||
| 105 | obsidian-livesync | LXC | CouchDB? |
|
||||
| 106 | gitea | LXC | DB + Repos |
|
||||
| 107 | openbureau | LXC | Postgres? |
|
||||
| 110 | flarum | LXC | DB |
|
||||
| 111 | nginx-proxy-manager | LXC | Config/Certs |
|
||||
| 200 | kgva-website | LXC | meist statisch |
|
||||
| 210 | openbureau-web | LXC | meist statisch |
|
||||
| 211 | rapport-website | LXC | meist statisch |
|
||||
| 212 | dossier-website | LXC | meist statisch |
|
||||
| 219 | dev.openbureau | LXC | ? |
|
||||
| 220 | trattoriamarina.ch | LXC | meist statisch |
|
||||
| 900 | rapport-server | LXC | Postgres/Supabase? |
|
||||
| 500 | windows | **VM** | grösster Brocken — Platz checken |
|
||||
| 800 | homeassistant-os | **VM** | Config |
|
||||
|
||||
> Die „stateful?"-Spalte sind **Annahmen** — bitte bestätigen/korrigieren (siehe Schritt 0).
|
||||
|
||||
---
|
||||
|
||||
## schritt 0 — discovery (mountpoints + backup-flags aufdecken)
|
||||
|
||||
```bash
|
||||
# was ist wo gemountet, und ist backup=1 oder backup=0?
|
||||
for id in $(pct list | awk 'NR>1{print $1}'); do
|
||||
echo "=== CT $id ==="; pct config $id | grep -E '^(rootfs|mp[0-9]+):'
|
||||
done
|
||||
|
||||
# was liegt auf dem tank?
|
||||
zfs list -r tank
|
||||
|
||||
# freier platz auf der backup-platte?
|
||||
df -h /mnt/pve/ext-hdd01
|
||||
```
|
||||
|
||||
Das zeigt, welche Daten schon in den vzdumps stecken (rootfs + `backup=1`-Mounts) und welche **separat per Restic** müssen (`backup=0`-Mounts auf dem Tank).
|
||||
→ **Diese Ausgabe brauche ich, um die exakten Restic-Pfade einzusetzen.**
|
||||
|
||||
---
|
||||
|
||||
## schritt 1 — stateful dienste app-level dumpen (portabel über den rebuild)
|
||||
|
||||
Macht die Wiederherstellung sauber unabhängig von Block-Konsistenz. Dumps in einen Pfad schreiben, der mitgesichert wird (rootfs oder ein `backup=1`-Mount).
|
||||
|
||||
- **103 mailserver** — sein eigenes Backup nutzen. Bei docker-mailserver: `setup backup` bzw. das Mailcow-Backup-Skript. **DKIM-Keys + DNS-Records (SPF/DMARC/MX) separat notieren** — der häufigste Migrations-Schmerz.
|
||||
- **DB-Dienste** (100/101/106/110/107/900 — je nach Engine):
|
||||
```bash
|
||||
# Postgres (im docker-container):
|
||||
pct exec <id> -- docker exec <pg-container> pg_dumpall -U postgres > /pfad/dump.sql
|
||||
# MariaDB/MySQL:
|
||||
pct exec <id> -- docker exec <db-container> mysqldump --all-databases -uroot -p<pw> > /pfad/dump.sql
|
||||
```
|
||||
- **102 vaultwarden / 105 obsidian-livesync** — vor dem Sichern stoppen, dann ist der Datenstand konsistent.
|
||||
|
||||
---
|
||||
|
||||
## schritt 2 — container + VMs sichern (vzdump → ext-hdd01)
|
||||
|
||||
Weil wir eh wipen, ist Downtime okay → `--mode stop` = sauber konsistent.
|
||||
|
||||
```bash
|
||||
# alle CTs und VMs:
|
||||
for id in $(pct list | awk 'NR>1{print $1}') $(qm list | awk 'NR>1{print $1}'); do
|
||||
vzdump $id --mode stop --compress zstd --storage ext-hdd01
|
||||
done
|
||||
```
|
||||
|
||||
> Achtung: **500 (windows)** ist vermutlich der grösste Brocken. Vorher `df -h /mnt/pve/ext-hdd01` prüfen.
|
||||
|
||||
---
|
||||
|
||||
## schritt 3 — tank-daten sichern (restic → ext-hdd01 UND hetzner)
|
||||
|
||||
Pfade aus Schritt 0 einsetzen. Zwei unabhängige Kopien, weil es nach dem Wipe die einzige Quelle ist.
|
||||
|
||||
```bash
|
||||
export RESTIC_PASSWORD_FILE=/root/.restic-pw
|
||||
|
||||
# lokal auf die externe platte
|
||||
export RESTIC_REPOSITORY=/mnt/pve/ext-hdd01/restic-tank
|
||||
restic init
|
||||
restic backup /tank/<dataset1> /tank/<dataset2> /tank/<...>
|
||||
|
||||
# offsite nach hetzner
|
||||
export RESTIC_REPOSITORY=sftp:uXXXX@uXXXX.your-storagebox.de:/tank
|
||||
restic init
|
||||
restic backup /tank/<dataset1> /tank/<dataset2> /tank/<...>
|
||||
```
|
||||
|
||||
Restic als root → Eigentümer/Rechte (inkl. Idmap-Offsets der Shares) kommen 1:1 zurück.
|
||||
|
||||
---
|
||||
|
||||
## schritt 4 — verifizieren + restore-test (PFLICHT)
|
||||
|
||||
```bash
|
||||
restic -r /mnt/pve/ext-hdd01/restic-tank check
|
||||
|
||||
# einen container testweise auf freie ID zurückholen:
|
||||
pct restore 999 /mnt/pve/ext-hdd01/dump/vzdump-lxc-102-*.tar.zst --storage local-lvm
|
||||
pct start 999 # läuft er? dann wieder weg:
|
||||
pct stop 999 && pct destroy 999
|
||||
|
||||
# einen tank-pfad testweise restoren:
|
||||
restic -r /mnt/pve/ext-hdd01/restic-tank restore latest --target /tmp/rtest --include /tank/<dataset>
|
||||
ls -la /tmp/rtest/tank/<dataset>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## schritt 5 — wipe-freigabe (erst wenn ALLES grün)
|
||||
|
||||
- [ ] vzdump aller 18 Gäste auf ext-hdd01 vorhanden
|
||||
- [ ] Tank-Restic auf ext-hdd01 **und** Hetzner, `restic check` ok
|
||||
- [ ] DB-/Mail-Dumps vorhanden und im Backup enthalten
|
||||
- [ ] je 1 Container- **und** 1 Tank-Restore erfolgreich getestet
|
||||
- [ ] DKIM-Keys + DNS-Records notiert
|
||||
- [ ] beim Reinstall wird **ext-hdd01 NICHT** als Ziel gewählt
|
||||
|
||||
---
|
||||
|
||||
## schritt 6 — nach dem rebuild: reihenfolge
|
||||
|
||||
1. Proxmox neu auf NVMe, **ZFS-root (rpool)** — nur NVMe als Ziel.
|
||||
2. `tank` neu als Mirror + Datasets/Quotas nach Manifest.
|
||||
3. ext-hdd01 wieder mounten.
|
||||
4. Tank-Daten per `restic restore` zurück in die frischen Datasets.
|
||||
5. Container/VMs aus vzdump zurück: `pct restore <id> <file> --storage ...` bzw. `qmrestore <file> <id>` für die VMs. Dabei kannst du gleich auf das neue Manifest-Schema umnummerieren.
|
||||
6. **mailserver zuletzt** und mit Ruhe; DKIM/DNS prüfen, Testmail.
|
||||
7. PBS + sanoid + Restic als laufendes System einrichten (permanentes Backup).
|
||||
@@ -0,0 +1,54 @@
|
||||
upstream php-handler { server app:9000; }
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /var/www/html;
|
||||
|
||||
client_max_body_size 16G;
|
||||
client_body_timeout 3600s;
|
||||
fastcgi_buffers 64 4K;
|
||||
fastcgi_hide_header X-Powered-By;
|
||||
|
||||
index index.php index.html /index.php$request_uri;
|
||||
|
||||
location = / { if ($http_user_agent ~ ^DavClnt) { return 302 /remote.php/webdav/$is_args$args; } }
|
||||
location = /robots.txt { allow all; log_not_found off; access_log off; }
|
||||
|
||||
location ^~ /.well-known {
|
||||
location = /.well-known/carddav { return 301 /remote.php/dav/; }
|
||||
location = /.well-known/caldav { return 301 /remote.php/dav/; }
|
||||
location /.well-known/acme-challenge { try_files $uri $uri/ =404; }
|
||||
location /.well-known/pki-validation { try_files $uri $uri/ =404; }
|
||||
return 301 /index.php$request_uri;
|
||||
}
|
||||
|
||||
location ~ ^/(?:build|tests|config|lib|3rdparty|templates|data)(?:$|/) { return 404; }
|
||||
location ~ ^/(?:\.|autotest|occ|issue|indie|db_|console) { return 404; }
|
||||
|
||||
location ~ \.php(?:$|/) {
|
||||
rewrite ^/(?!index|remote|public|cron|core\/ajax\/update|status|ocs\/v[12]|updater\/.+|ocs-provider\/.+|.+\/richdocumentscode\/proxy) /index.php$request_uri;
|
||||
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
|
||||
set $path_info $fastcgi_path_info;
|
||||
try_files $fastcgi_script_name =404;
|
||||
include fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param PATH_INFO $path_info;
|
||||
fastcgi_param modHeadersAvailable true;
|
||||
fastcgi_param front_controller_active true;
|
||||
fastcgi_pass php-handler;
|
||||
fastcgi_intercept_errors on;
|
||||
fastcgi_request_buffering off;
|
||||
fastcgi_max_temp_file_size 0;
|
||||
}
|
||||
|
||||
location ~ \.(?:css|js|svg|gif|png|jpg|ico|wasm|tflite|map|ttf|bcmap|mp4|webm|ogg|flac)$ {
|
||||
try_files $uri /index.php$request_uri;
|
||||
access_log off;
|
||||
expires 6M;
|
||||
}
|
||||
location ~ \.woff2?$ { try_files $uri /index.php$request_uri; expires 7d; access_log off; }
|
||||
|
||||
location /remote { return 301 /remote.php$request_uri; }
|
||||
location / { try_files $uri $uri/ /index.php$request_uri; }
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
# anleitung: PBS aufsetzen + tank sichern
|
||||
|
||||
Zwei Teile: erst Proxmox Backup Server installieren & konfigurieren (Datastore auf der neuen SSD), dann den Tank sichern.
|
||||
|
||||
---
|
||||
|
||||
## variablen — oben ausfüllen
|
||||
|
||||
```text
|
||||
PBS_DATASTORE_PATH = /mnt/pve/pbs-ssd # pfad deiner neuen SSD -> prüfen mit: cat /etc/pve/storage.cfg
|
||||
PBS_CTID = 145 # freie container-ID
|
||||
PBS_IP / GW = <freie-IP>/24 / <gateway> # in deinem bridge-subnetz
|
||||
ETT_HDD = /mnt/pve/ext-hdd01 # die alte HDD = restic-ziel für den tank
|
||||
HETZNER = uXXXX@uXXXX.your-storagebox.de
|
||||
```
|
||||
|
||||
Pfad der neuen SSD bestätigen:
|
||||
```bash
|
||||
cat /etc/pve/storage.cfg # zeigt den namen + pfad der neu angelegten directory-storage
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# TEIL 1 — PBS installieren & konfigurieren
|
||||
|
||||
## 1.1 — Debian-13-Template holen (auf dem PVE-Host)
|
||||
```bash
|
||||
pveam update
|
||||
pveam available | grep debian-13
|
||||
pveam download local debian-13-standard_13.0-1_amd64.tar.zst # name ggf. anpassen
|
||||
```
|
||||
|
||||
## 1.2 — PBS-LXC anlegen (privileged), SSD reinmounten
|
||||
```bash
|
||||
pct create 145 local:vztmpl/debian-13-standard_13.0-1_amd64.tar.zst \
|
||||
--hostname pbs --cores 2 --memory 2048 --swap 512 \
|
||||
--rootfs local-lvm:16 --unprivileged 0 \
|
||||
--net0 name=eth0,bridge=vmbr0,ip=<freie-IP>/24,gw=<gateway> \
|
||||
--password
|
||||
|
||||
# die neue SSD als datastore in den container binden:
|
||||
pct set 145 -mp0 /mnt/pve/pbs-ssd,mp=/datastore
|
||||
pct start 145
|
||||
```
|
||||
|
||||
## 1.3 — PBS im Container installieren
|
||||
```bash
|
||||
pct enter 145
|
||||
|
||||
wget https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg \
|
||||
-O /usr/share/keyrings/proxmox-archive-keyring.gpg
|
||||
|
||||
cat > /etc/apt/sources.list.d/pbs.sources << 'EOF'
|
||||
Types: deb
|
||||
URIs: http://download.proxmox.com/debian/pbs
|
||||
Suites: trixie
|
||||
Components: pbs-no-subscription
|
||||
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
||||
EOF
|
||||
|
||||
apt update && apt install -y proxmox-backup-server
|
||||
```
|
||||
Bei der Postfix-Frage „No configuration" wählen.
|
||||
|
||||
## 1.4 — Web-UI + Datastore anlegen
|
||||
- Öffne `https://<PBS_IP>:8007`, Login `root` + Container-Passwort (Self-signed-Warnung ist normal).
|
||||
- **Datastore → Add Datastore**: Name `homelab`, Backing Path `/datastore`.
|
||||
|
||||
## 1.5 — Aufräum- & Prüf-Jobs (im PBS)
|
||||
- Datastore `homelab` → **Prune & GC**:
|
||||
- Prune: keep-last 3, keep-daily 7, keep-weekly 4, keep-monthly 6
|
||||
- GC-Schedule: wöchentlich
|
||||
- **Verify Jobs** → Add: wöchentlich (prüft die Backups gegen Bitrot).
|
||||
|
||||
## 1.6 — PBS in PVE als Backup-Ziel einbinden
|
||||
- Im PBS: Dashboard → **Show Fingerprint** kopieren.
|
||||
- Im PVE: Datacenter → Storage → **Add → Proxmox Backup Server**:
|
||||
- ID `pbs`, Server `<PBS_IP>`, Datastore `homelab`, Username `root@pam`, Passwort, Fingerprint einfügen.
|
||||
|
||||
## 1.7 — Alle Gäste sichern
|
||||
**Einmalig jetzt (vor dem Wipe), konsistent per stop-modus:**
|
||||
```bash
|
||||
for id in $(pct list | awk 'NR>1{print $1}') $(qm list | awk 'NR>1{print $1}'); do
|
||||
vzdump $id --mode stop --storage pbs
|
||||
done
|
||||
```
|
||||
Das sichert deine 16 LXC (100–900) + die 2 VMs (500 windows, 800 homeassistant) ins PBS.
|
||||
> 500 (windows) ist der grösste Brocken — vorher Platz auf der SSD prüfen: `df -h /mnt/pve/pbs-ssd`.
|
||||
|
||||
**Für den laufenden Betrieb** (nach dem Rebuild): Datacenter → Backup → Add → täglicher Job, alle Gäste, Storage `pbs`, Modus Snapshot.
|
||||
|
||||
---
|
||||
|
||||
# TEIL 2 — tank sichern
|
||||
|
||||
PBS deckt die **Container/VMs** ab. Die **Bulk-Daten auf dem Tank** sind eine eigene Schicht — die sicherst du mit Restic auf die alte HDD (`ext-hdd01`) **und** offsite nach Hetzner.
|
||||
|
||||
## 2.1 — Discovery: was liegt wo, und steckt es schon im PBS?
|
||||
```bash
|
||||
# alle datasets + mountpoints auf dem tank:
|
||||
zfs list -o name,used,mountpoint -r tank
|
||||
|
||||
# pro container: mountpoints + backup-flag:
|
||||
for id in $(pct list | awk 'NR>1{print $1}'); do
|
||||
echo "=== CT $id ==="; pct config $id | grep -E '^(rootfs|mp[0-9]+):'
|
||||
done
|
||||
```
|
||||
Entscheidend ist das `backup=`-Flag jedes Daten-Mountpoints:
|
||||
- **`backup=1`** → die Daten sind schon in den PBS-Sicherungen aus 1.7. Restic ist dann nur noch für die **Offsite-Kopie** nötig.
|
||||
- **`backup=0`** → die Daten sind **nicht** im PBS → Restic ist hier Pflicht, lokal *und* offsite.
|
||||
|
||||
> Schick mir die Ausgabe dieser beiden Befehle, dann setze ich unten die echten Pfade ein. Bis dahin: in den Restic-Befehl kommen die `mountpoint`-Pfade der Daten-Datasets aus der `zfs list` (deine App-Daten wie nc_data, immich-library usw. — **nicht** die rootfs-Subvols der Websites, die deckt PBS ab).
|
||||
|
||||
## 2.2 — Restic-Repos initialisieren
|
||||
```bash
|
||||
export RESTIC_PASSWORD_FILE=/root/.restic-pw # vorher anlegen: ein starkes passwort reinschreiben, chmod 600
|
||||
|
||||
# lokal auf die HDD:
|
||||
export RESTIC_REPOSITORY=/mnt/pve/ext-hdd01/restic-tank
|
||||
restic init
|
||||
|
||||
# offsite nach hetzner:
|
||||
export RESTIC_REPOSITORY=sftp:uXXXX@uXXXX.your-storagebox.de:/tank
|
||||
restic init
|
||||
```
|
||||
|
||||
## 2.3 — Tank sichern (kalt, weil Migration → Dienste eh gestoppt)
|
||||
Pfade aus 2.1 einsetzen:
|
||||
```bash
|
||||
# lokal
|
||||
export RESTIC_REPOSITORY=/mnt/pve/ext-hdd01/restic-tank
|
||||
restic backup /tank/<dataset-a> /tank/<dataset-b> /tank/<...>
|
||||
|
||||
# offsite
|
||||
export RESTIC_REPOSITORY=sftp:uXXXX@uXXXX.your-storagebox.de:/tank
|
||||
restic backup /tank/<dataset-a> /tank/<dataset-b> /tank/<...>
|
||||
```
|
||||
Restic als root → Eigentümer/Rechte (inkl. Idmap-Offsets) kommen 1:1 zurück.
|
||||
|
||||
## 2.4 — Verifizieren + Restore-Test (PFLICHT vor dem Wipe)
|
||||
```bash
|
||||
restic -r /mnt/pve/ext-hdd01/restic-tank check
|
||||
restic -r /mnt/pve/ext-hdd01/restic-tank restore latest \
|
||||
--target /tmp/rtest --include /tank/<dataset-a>
|
||||
ls -la /tmp/rtest/tank/<dataset-a> # stimmen die dateien?
|
||||
```
|
||||
|
||||
## 2.5 — Für den laufenden Betrieb
|
||||
Nach dem Rebuild beide Restic-Läufe (lokal + Hetzner) in einen täglichen Cron, plus Pruning:
|
||||
```bash
|
||||
restic forget --keep-daily 14 --keep-weekly 8 --keep-monthly 12 --prune
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## die ganze kette auf einen blick
|
||||
|
||||
| Schicht | Werkzeug | Lokal | Offsite |
|
||||
|---------|----------|-------|---------|
|
||||
| Container + VMs | PBS | SSD (`pbs-ssd`) | — (aus Gitea reproduzierbar) |
|
||||
| Tank-Daten | Restic | HDD (`ext-hdd01`) | Hetzner |
|
||||
|
||||
Beide externen Platten + die Hetzner-Box überleben den Wipe. Nach dem Rebuild: PBS-Datastore auf der SSD wieder einhängen → Gäste zurück; Tank neu anlegen → Restic zurück.
|
||||
@@ -0,0 +1,22 @@
|
||||
; rieger-spirit php-tuning für nextcloud
|
||||
memory_limit = 1024M
|
||||
upload_max_filesize = 16G
|
||||
post_max_size = 16G
|
||||
max_execution_time = 3600
|
||||
max_input_time = 3600
|
||||
output_buffering = 0
|
||||
date.timezone = Europe/Zurich
|
||||
|
||||
; opcache + JIT
|
||||
opcache.enable = 1
|
||||
opcache.enable_cli = 1
|
||||
opcache.interned_strings_buffer = 32
|
||||
opcache.max_accelerated_files = 10000
|
||||
opcache.memory_consumption = 256
|
||||
opcache.save_comments = 1
|
||||
opcache.revalidate_freq = 60
|
||||
opcache.jit = 1255
|
||||
opcache.jit_buffer_size = 128M
|
||||
|
||||
; APCu als lokaler cache (im offiziellen image enthalten)
|
||||
apc.enable_cli = 1
|
||||
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# setup-pbs.sh — richtet einen Proxmox Backup Server als LXC ein.
|
||||
# Datastore landet auf der angegebenen (bereits eingebundenen) SSD.
|
||||
#
|
||||
# Macht NUR additive Dinge: LXC anlegen, PBS installieren, Datastore anlegen,
|
||||
# in PVE als Storage einbinden. Fasst KEINE Platten an, loescht nichts,
|
||||
# startet KEINE Backups. Das machst du danach bewusst selbst.
|
||||
#
|
||||
# Auf dem Proxmox-VE-Host als root ausfuehren. Vorher durchlesen.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
[[ $EUID -eq 0 ]] || { echo "Bitte als root ausfuehren."; exit 1; }
|
||||
command -v pct >/dev/null || { echo "Kein Proxmox-VE-Host (pct fehlt)."; exit 1; }
|
||||
|
||||
echo "=== PBS-LXC Setup ==="
|
||||
echo
|
||||
|
||||
# --- datastore-pfad (die neue SSD) ---
|
||||
echo "Eingebundene Directory-Storages (Pfad-Spalte):"
|
||||
awk '/^dir:/{name=$2} /path /{print " "name" -> "$2}' /etc/pve/storage.cfg || true
|
||||
echo
|
||||
read -rp "Pfad der SSD fuer den Datastore [/mnt/pve/pbs-ssd]: " DS_PATH
|
||||
DS_PATH=${DS_PATH:-/mnt/pve/pbs-ssd}
|
||||
[[ -d $DS_PATH ]] || { echo "FEHLER: $DS_PATH existiert nicht. Erst die SSD einbinden."; exit 1; }
|
||||
|
||||
# --- container-ID (naechste freie als default) ---
|
||||
NEXTID=$(pvesh get /cluster/nextid)
|
||||
read -rp "Container-ID [$NEXTID]: " CTID
|
||||
CTID=${CTID:-$NEXTID}
|
||||
|
||||
# --- netzwerk (gateway autodetektiert) ---
|
||||
GW_DETECT=$(ip route | awk '/default/{print $3; exit}')
|
||||
read -rp "Gateway [$GW_DETECT]: " GW
|
||||
GW=${GW:-$GW_DETECT}
|
||||
read -rp "Bridge [vmbr0]: " BRIDGE
|
||||
BRIDGE=${BRIDGE:-vmbr0}
|
||||
read -rp "Feste IP fuer PBS inkl. Maske (z.B. 192.168.1.50/24): " CTIP
|
||||
[[ -n ${CTIP:-} ]] || { echo "FEHLER: IP erforderlich."; exit 1; }
|
||||
IPONLY=${CTIP%%/*}
|
||||
|
||||
# --- rootfs-storage ---
|
||||
read -rp "Storage fuer die Container-rootfs [local-lvm]: " ROOTSTORE
|
||||
ROOTSTORE=${ROOTSTORE:-local-lvm}
|
||||
|
||||
# --- root-passwort fuer den container (= PBS-Login root@pam) ---
|
||||
read -rsp "Root-Passwort fuer den PBS-Container: " CTPW; echo
|
||||
[[ -n ${CTPW:-} ]] || { echo "FEHLER: Passwort erforderlich."; exit 1; }
|
||||
|
||||
# --- zusammenfassung + bestaetigung ---
|
||||
cat <<SUMMARY
|
||||
|
||||
Zusammenfassung:
|
||||
CTID $CTID
|
||||
Hostname pbs
|
||||
IP / GW $CTIP / $GW (Bridge $BRIDGE)
|
||||
rootfs ${ROOTSTORE}:16 GiB
|
||||
Datastore-Pfad $DS_PATH -> im Container /datastore
|
||||
Datastore-Name homelab
|
||||
|
||||
SUMMARY
|
||||
read -rp "So anlegen? [y/N]: " OK
|
||||
[[ ${OK,,} == y ]] || { echo "Abgebrochen."; exit 0; }
|
||||
|
||||
# --- debian-13-template sicherstellen ---
|
||||
echo ">> Template pruefen/holen ..."
|
||||
pveam update >/dev/null 2>&1 || true
|
||||
TMPL=$(pveam available --section system | awk '/debian-13-standard/{print $2}' | sort | tail -n1)
|
||||
[[ -n $TMPL ]] || { echo "FEHLER: kein debian-13-Template gefunden."; exit 1; }
|
||||
pveam list local | grep -q "$TMPL" || pveam download local "$TMPL"
|
||||
|
||||
# --- LXC anlegen ---
|
||||
echo ">> Erstelle LXC $CTID ..."
|
||||
pct create "$CTID" "local:vztmpl/$TMPL" \
|
||||
--hostname pbs --cores 2 --memory 2048 --swap 512 \
|
||||
--rootfs "${ROOTSTORE}:16" --unprivileged 0 \
|
||||
--net0 "name=eth0,bridge=${BRIDGE},ip=${CTIP},gw=${GW}" \
|
||||
--onboot 1
|
||||
|
||||
# datastore reinmounten + starten
|
||||
pct set "$CTID" -mp0 "${DS_PATH},mp=/datastore"
|
||||
pct start "$CTID"
|
||||
sleep 5
|
||||
pct exec "$CTID" -- bash -c "echo 'root:${CTPW}' | chpasswd"
|
||||
|
||||
# --- PBS installieren ---
|
||||
echo ">> Installiere PBS im Container ..."
|
||||
pct exec "$CTID" -- bash -c '
|
||||
set -e
|
||||
wget -qO /usr/share/keyrings/proxmox-archive-keyring.gpg \
|
||||
https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg
|
||||
cat > /etc/apt/sources.list.d/pbs.sources <<SRC
|
||||
Types: deb
|
||||
URIs: http://download.proxmox.com/debian/pbs
|
||||
Suites: trixie
|
||||
Components: pbs-no-subscription
|
||||
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
|
||||
SRC
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y proxmox-backup-server
|
||||
'
|
||||
|
||||
# --- datastore anlegen ---
|
||||
echo ">> Lege Datastore 'homelab' an ..."
|
||||
pct exec "$CTID" -- proxmox-backup-manager datastore create homelab /datastore 2>/dev/null \
|
||||
|| echo " (Datastore existiert evtl. schon - ok)"
|
||||
|
||||
# --- in PVE als storage einbinden (fingerprint automatisch) ---
|
||||
echo ">> Binde PBS in PVE ein ..."
|
||||
FP=$(pct exec "$CTID" -- proxmox-backup-manager cert info | awk -F': ' '/[Ff]ingerprint/{print $2; exit}')
|
||||
if [[ -n ${FP:-} ]]; then
|
||||
pvesm add pbs pbs --server "$IPONLY" --datastore homelab \
|
||||
--username root@pam --password "$CTPW" --fingerprint "$FP" 2>/dev/null \
|
||||
&& echo " PVE-Storage 'pbs' angelegt." \
|
||||
|| echo " (Storage 'pbs' existiert evtl. schon - ok)"
|
||||
else
|
||||
echo " Fingerprint nicht automatisch gefunden - bitte manuell einbinden (siehe unten)."
|
||||
fi
|
||||
|
||||
cat <<DONE
|
||||
|
||||
=== Fertig ===
|
||||
PBS-Weboberflaeche: https://${IPONLY}:8007 (Login: root + dein Container-Passwort)
|
||||
|
||||
Empfohlen jetzt in der PBS-GUI:
|
||||
- Datastore 'homelab' -> Prune & GC: keep-last 3, daily 7, weekly 4, monthly 6; GC woechentlich
|
||||
- Verify Jobs -> woechentlich
|
||||
|
||||
Backups starten (bewusst, separat) - alle Gaeste einmalig ins PBS:
|
||||
for id in \$(pct list | awk 'NR>1{print \$1}') \$(qm list | awk 'NR>1{print \$1}'); do
|
||||
vzdump \$id --mode stop --storage pbs
|
||||
done
|
||||
|
||||
DONE
|
||||
Reference in New Issue
Block a user