Rename phy-z-srv-gpu01 to phy-srv-gpu01; fix share analysis scripts

- rename host/group/folder everywhere to match the server's actual
  hostname and physical label
- share-analysis.ps1: sanitize the output filename prefix — '-Paths "D:"'
  produced 'D:-file-types.csv' and Export-Csv failed with 'path format
  not supported', so no CSVs were written
- share-analysis.ps1: new -FolderDepth so folders can be aggregated at
  D:\Abteilungen\<Share> level, which matches the share layout
- list-shares.ps1: -WithSize walks local paths instead of UNC when run on
  the server itself (UNC was orders of magnitude slower and looked stuck)
  and prints progress every 50k files

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-09-03 14:47:07 +02:00
co-authored by Claude Opus 4.8
parent 9c38c60783
commit 75296ce58c
16 changed files with 81 additions and 34 deletions
+36
View File
@@ -0,0 +1,36 @@
## Hardware Components
### HPE GPU SERVER, 2 CPUs, 128GB RAM, RTX Pro 6000 GPU 96GB RAM, Red. Netzteil
| POS | BEZEICHNUNG |ANZ. |EINZELPREIS | GESAMTPREIS |
| - | - | - | - | - |
| 1 | HPE DL380 Gen12 SFF NC CTO Svr | 1 | 1.598,59 € | 1.598,59 € |
| 2 | HPE Intel Xeon 6714P 4.0GHz 8-core 165W Processor | 2 | 4.148,67 € | 8.297,34 € |
| 3 | HPE 16GB 1Rx8 PC5-6400B-R Smart FIO Kit | 8 | 1.171,82 € | 9.374,52 € |
| 4 | HPE DL3XX Gen12 8SFF x1 U.3 TM Kit | 1 | 87,48 € | 87,48 € |
| 5 | HPE 960G NVMe RI SFF BC U.3ST V2 MV SSD | 2 | 1.757,04 € | 3.514,08 € |
| 6 | HPE DL380 G11 2U x8/x16/x8 Sec Riser Kit | 1 | 67,98 € | 67,98 € |
| 7 | NVIDIA RTX PRO 6000 96G PCIe | 1 | 13.976,64 € | 13.976,64 € |
| 8 | HPE 96W Smart Stg Li-ion Batt 145mm Kit | 1 | 36,80 € | 36,80 € |
| 9 | HPE DL360 Gen11 Stg Cntrl Enable Cbl Kit | 1 | 7,20 € | 7,20 € |
| 10 | HPE MR408i-o Gen11 SPDM Storage Cntlr | 1 | 1.075,46 € | 1.075,46 € |
| 11 | BCM 57412 10GbE 2p SFP+ OCP3 Adptr | 1 | 152,35 € | 152,35 € |
| 12 | HPE BLc 10G SFP+ SR Transceiver | 2 | 16,20 € | 32,41 € |
| 13 | HPE 1800W-2200W FS Ti Ht Plg PS Kit | 2 | 283,11 € | 566,21 € |
| 14 | HPE C13 - C14 250V 10A 2m FIO Pwr Cord | 2 | 2,58 € | 5,15 € |
| 15 | HPE iLO Adv 1-svr Lic 3yr Support | 1 | 315,27 € | 315,27 € |
| 16 | HPE Cmp Cloud Mgmt Srv FIO Enablement | 1 | 1,04 € | 1,04 € |
| 17 | HPE DL380/DL560 Gen11 2U GPU Pwr Cbl Kit | 1 | 26,96 € | 26,96 € |
| 18 | HPE DL3XX/ML350 G12 CPU1/OCPB x8 Cbl Kit | 1 | 20,84 € | 20,84 € |
| 19 | HPE DL380 Gen12 8SFF UMB OROC Cbl Kit | 1 | 22,71 € | 22,71 € |
| 20 | HPE DL380/DL560 G11 2U High Perf Fan Kit | 1 | 197,57 € | 197,57 € |
| 21 | HPE DL380 Gen12 GPU Air Upg Enable Kit | 1 | 158,88 € | 158,88 € |
| 22 | HPE DL3XX Gen11 Easy Install Rail 3 Kit | 1 | 41,60 € | 41,60 € |
| 23 | HPE Localization FIO Kit | 1 | 0,01 € | 0,01 € |
| 24 | HPE DL3XX Gen12 High Perf Heat Sink Kit | 2 | 75,27 € | 150,54 € |
| 25 | HPE 23C Max Rec Ambient Temp Config Trk | 1 | 0,01 € | 0,01 € |
| 26 | HPE COM Std 5yr Up ProLiant SaaS | 1 | 490,13 € | 490,13 € |
| 27 | HPE 5Y Tech Care Essential SVC | 1 | none | none |
| 28 | HPE iLO Advanced Non Blade Support | 1 | 25,17 € | 25,17 € |
| 29 | HPE DL380 Gen12 Support | 1 | 4.145,87 € | 4.145,87 € |
| 30 | HP ENTERPRISE X242 10G SFP+ to SFP+ 3m DAC Cable | 1 | 96,00 € | 96,00 € |
+31
View File
@@ -0,0 +1,31 @@
# phy-srv-gpu01
GPU server for AI/ML workloads. Hardware is ordered/assessed; OS setup and configuration are the next step.
| | |
| - | - |
| IP | 192.168.66.69 (planned) |
| Ansible group | `phy_srv_gpu01` |
| Status | planned — not yet configured |
## Hardware
HPE ProLiant DL380 Gen12, 2× Intel Xeon 6714P (8-core, 4.0 GHz), 128 GB RAM,
NVIDIA RTX PRO 6000 96 GB, 2× 960 GB NVMe SSD, redundant PSU — full BOM in [HW.md](HW.md).
## Planning notes
- [Hardware assessment (2026-07-06)](notes/20260706-hardware-assessment.md)
- [Software assessment (2026-07-06)](notes/20260706-software-assessment.md)
- [Deep dive (2026-07-07)](notes/20260707-deep-dive.md)
- [Project plan for the setup (2026-07-10)](notes/20260710-projektplan.md) — start here when the server arrives
Open pre-work items are tracked in the repo-root [TODO.md](../../TODO.md).
## Runbooks
- [NVIDIA driver, CUDA repo & container toolkit (2026-07-14)](manuals/20260714-nvidia-driver-install.md) — automated by the Ansible role `nvidia_gpu`
## Scripts
- [share-analysis.ps1](scripts/share-analysis.ps1) — read-only SMB share analysis (projektplan §2.1); run on a Windows machine with read access to the shares
@@ -0,0 +1,87 @@
# Manual nvidia driver, cuda repo and container toolkit
Automated by the Ansible role `ansible/roles/nvidia_gpu` — this manual documents the steps.
**Disable Secure Boot in BIOS first** (unsigned kernel modules won't load otherwise).
## NVIDIA driver
Check if GPUs are recognized by the base OS:
```bash
sudo lspci | grep -i nvidia
```
Which should show some output if it finds nvidia devices.
Search for available drivers for your GPUs:
```bash
sudo ubuntu-drivers devices
```
Install the driver pinned. The RTX PRO 6000 (Blackwell) needs **driver >= 580**
and **only works with the open kernel modules** — the proprietary ones load but
fail at `RmInitAdapter`, leaving `nvidia-smi` with "No devices were found".
Use the `-server-open` variant and prefer a pinned install over
`ubuntu-drivers autoinstall` so the choice is explicit and reproducible
(re-check for a newer branch at install time):
```bash
sudo apt install -y --no-install-recommends nvidia-driver-595-server-open
```
`--no-install-recommends` keeps `nvidia-settings` and its GTK dependency chain
off this headless server.
> Note: do **not** additionally install `cuda-drivers` from the NVIDIA repo —
> that would mix the Ubuntu-archive driver with the NVIDIA-repo driver and the
> two can conflict. Pick one source; we use the Ubuntu archive.
Reboot the system for changes to take effect:
```bash
sudo reboot
```
Show GPU stats with:
```bash
nvidia-smi
```
## CUDA repository (toolkit optional)
Add the NVIDIA CUDA apt repository:
```bash
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt update
```
The full CUDA toolkit is **not needed** for Docker-based workloads (vLLM etc. —
the driver plus container toolkit suffice). Only if compiling on the host:
```bash
sudo apt install -y cuda-toolkit # meta package, pulls the current release
```
## Container toolkit
Install the Nvidia Container toolkit:
```bash
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update
sudo apt install -y nvidia-container-toolkit
```
Configure Docker to use the NVIDIA runtime (writes `/etc/docker/daemon.json`) and restart it —
without this step `docker run --gpus all` fails:
```bash
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
```
Test a simple cuda container and nvidia-smi command inside:
```bash
docker run --rm --gpus all nvidia/cuda:13.0.0-base-ubuntu24.04 nvidia-smi
```
@@ -0,0 +1,90 @@
# Hardware Assessment — GPU Server für LLM-Workloads
Date: 2026-07-06
Status: Draft for internal review
Budget: ~50.000 €
## TL;DR
Die Beobachtung vom Bestandsserver (CPU < 10 %, RAM < 150 GB, GPUs zeitweise
voll ausgelastet) ist typisch: **LLM-Inference ist fast reine GPU-Last.**
Downscaling von CPU und RAM ist richtig. Bei der GPU empfehle ich statt einer
halben H100-NVL-Konfiguration die **NVIDIA RTX PRO 6000 Blackwell Server
Edition (96 GB)** — mehr VRAM als eine H100 NVL, in Single-GPU-Inference-
Benchmarks gleichauf oder schneller, für etwa **ein Drittel des Preises**
(~910 k€ statt ~31 k€). Gesamtsystem landet grob bei **2025 k€ inkl.
Support** — deutlich unter Budget.
## 1. Einordnung des Bestandsservers
Die Referenz-Konfiguration (DL380a Gen11, 2× Xeon 6526Y, 512 GB RAM,
2× H100 NVL) ist für den Inference-Use-Case überdimensioniert:
- **CPU < 10 %**: erwartbar — bei GPU-Inference macht die CPU nur
Tokenizing, Scheduling und I/O. 2× 16 Kerne sind unnötig.
- **RAM < 100150 GB**: davon ist erfahrungsgemäß ein großer Teil
Page-Cache. Echter Bedarf: Modell-Staging + Services, deutlich unter 128 GB.
- **2× H100 NVL**: NVLink-Pärchen lohnt sich nur, wenn ein Modell über beide
GPUs gespannt wird (Tensor-Parallel). Für Modelle ≤ 96 GB unnötig.
## 2. GPU-Empfehlung
| | RTX PRO 6000 Blackwell SE | H100 NVL | L40S |
|---|---|---|---|
| VRAM | **96 GB GDDR7** | 94 GB HBM3 | 48 GB GDDR6 |
| Speicherbandbreite | 1.792 GB/s | 3.900 GB/s | 864 GB/s |
| FP8/FP4 | ✅ / ✅ (Blackwell) | ✅ / ❌ | ❌ / ❌ |
| NVLink | ❌ (hier irrelevant, 1 GPU) | ✅ | ❌ |
| Straßenpreis (ca.) | **~910 k€** | ~31 k€ | ~8 k€ |
- Benchmarks 2026 zeigen die RTX PRO 6000 bei Single-GPU-LLM-Inference
**gleichauf bis vor der H100** bei ~28 % niedrigeren Kosten pro Token
([cloudrift.ai](https://www.cloudrift.ai/blog/benchmarking-rtx6000-vs-datacenter-gpus),
[openmetal.io](https://openmetal.io/resources/blog/comparing-the-nvidia-rtx-pro-6000-vs-h100-for-ai-inference/)).
- Gegenüber der in den Notizen angesetzten 48-GB-Karte (L40S-Klasse):
doppelter VRAM und > 2× Bandbreite für ähnliches Geld — 96 GB erlauben
70B-Modelle in FP8 bzw. viel KV-Cache für parallele Nutzer
([gpuperhour.com](https://gpuperhour.com/compare/l40s-vs-rtx-pro-6000-blackwell)).
- H100 NVL nur dann, wenn später auf 2 GPUs mit Tensor-Parallelität erweitert
werden soll — dafür gibt es hier kein Anzeichen.
⚠️ Mit dem Distributor klären, welche HPE-Plattform (DL380 Gen11/Gen12 bzw.
DL380a) für die RTX PRO 6000 Blackwell SE qualifiziert ist.
## 3. Empfohlene Konfiguration (angelehnt an das Referenz-Angebot)
| Komponente | Referenz-Server | Empfehlung neu | Begründung |
|---|---|---|---|
| Chassis | DL380a Gen11 | DL380 Gen11/Gen12 mit GPU-Kit (oder DL380a) | 1 GPU braucht kein 4-GPU-Chassis |
| CPU | 2× Xeon 6526Y (32C) | **1× Xeon 16C** (z. B. 6526Y o. kleiner) | CPU-Last < 10 % beobachtet |
| RAM | 512 GB (8× 64 GB) | **128 GB (8× 16 GB)** | Alle 8 Kanäle belegt (Bandbreite), Upgrade-Pfad auf 256 GB frei |
| Boot-SSD | 2× 960 GB NVMe | 2× 960 GB NVMe (RAID 1) | unverändert |
| Daten-SSD | — | **+ 2× 3,84 TB NVMe (RAID 1)** | Modelle + Vektor-Index der Datei-Suche |
| NIC | 2× 25 GbE + 4× 1 GbE | 1× 25/10 GbE SFP28 + 1 GbE (Mgmt) | Indizierung zieht die SMB-Shares übers Netz — 1 GbE wäre der Flaschenhals |
| GPU | 2× H100 NVL 94 GB | **1× RTX PRO 6000 Blackwell SE 96 GB** | s. o. |
| PSU | 4× 18002200 W | 2× 1800 W (redundant) | 1 GPU à 600 W |
| iLO Adv + Support | 5 J. | 5 J. Tech Care Essential | unverändert |
**Grobe Kostenschätzung:** Server-Basis + CPU + RAM + Storage ≈ 1012 k€,
GPU ≈ 910 k€, Support ≈ 46 k€ → **~2328 k€**. Es bleibt Budget für
Einrichtung/Software-Dienstleistung (RAG-Pipeline!) und ggf. eine zweite GPU
später — das Chassis dafür gleich mit einplanen (Riser/Netzteile).
## 4. Passt eine GPU für "viele Nutzer"?
Ja, für den beschriebenen Fall (Nicht-Power-User, Chat + Datei-Suche):
- Nicht-Power-User erzeugen wenig gleichzeitige Last; realistisch sind
520 parallele Anfragen selbst bei 100+ angebundenen Usern.
- vLLM mit einem 2032B-Modell in FP8 auf 96 GB bedient das komfortabel;
ein 70B-Modell geht ebenfalls, mit weniger Parallelitäts-Headroom.
- Wachstumspfad: zweite RTX PRO 6000 nachrüsten (zwei unabhängige
vLLM-Instanzen bzw. Modell-Replikation — kein NVLink nötig).
## Quellen
- [RTX PRO 6000 vs H100/H200/L40S LLM Inference Benchmarks (cloudrift.ai)](https://www.cloudrift.ai/blog/benchmarking-rtx6000-vs-datacenter-gpus)
- [RTX PRO 6000 Pricing July 2026 (Thunder Compute)](https://www.thundercompute.com/blog/nvidia-rtx-pro-6000-pricing)
- [L40S vs RTX PRO 6000 (gpuperhour)](https://gpuperhour.com/compare/l40s-vs-rtx-pro-6000-blackwell)
- [RTX PRO 6000 vs H100 for AI Inference (OpenMetal)](https://openmetal.io/resources/blog/comparing-the-nvidia-rtx-pro-6000-vs-h100-for-ai-inference/)
- [RTX PRO 6000 Benchmarks: 30B AWQ, 70B FP8, Cost per Mtok (Spheron)](https://www.spheron.network/blog/rent-nvidia-rtx-pro-6000/)
@@ -0,0 +1,119 @@
# Software Assessment — GPU Server für LLM-Workloads
Date: 2026-07-06
Status: Draft for internal review
## TL;DR
**LM Studio ist als Server-Dienst für viele Benutzer nicht geeignet.** Es ist ein
Desktop-Tool für Einzelnutzer. Empfehlung: **vLLM** als Inference-Server +
**Open WebUI** als Benutzeroberfläche (Multi-User, LDAP). Für die Datei-Suche
über SMB-Shares wird zusätzlich eine RAG-/Enterprise-Search-Komponente
benötigt — das ist der eigentlich anspruchsvolle Teil des Projekts, nicht das
LLM-Hosting.
## 1. Ist LM Studio als Server-Dienst geeignet?
Kurz: **Nein**, nicht für den Multi-User-Betrieb.
Was LM Studio inzwischen kann:
- Headless-Betrieb ohne GUI über den `llmster`-Daemon (offiziell dokumentiert:
[lmstudio.ai/docs/app/api/headless](https://lmstudio.ai/docs/app/api/headless))
- OpenAI-kompatible lokale API
- Seit 2025 auch für kommerzielle Nutzung kostenlos
Warum es trotzdem ungeeignet ist:
| Anforderung | LM Studio |
|---|---|
| Multi-User / Benutzerverwaltung | ❌ Keine — die API ist für einen lokalen Einzelnutzer gedacht, ohne Benutzerkonten |
| LDAP/AD-Anbindung | ❌ Nicht vorhanden |
| Gleichzeitige Anfragen vieler Nutzer | ❌ llama.cpp-Backend, kein Continuous Batching wie vLLM — Durchsatz bricht bei Parallelität ein |
| Modell-Verwaltung im Betrieb | ⚠️ Modell-Laden ist exklusiv; Wechsel unterbricht den Dienst |
| RAG / Suche über Netzlaufwerke | ❌ Nur lokale Datei-Anhänge im Chat, keine Index-Pipeline |
Fazit: LM Studio bleibt sinnvoll als **lokales Tool für Power-User am
Arbeitsplatz**, aber der Server braucht einen echten Inference-Stack.
## 2. Empfohlener Software-Stack
```
[User-Browser] ──► Open WebUI (Auth via LDAP/AD, Chat-UI, RAG)
│ OpenAI-kompatible API
vLLM (Inference-Server, Continuous Batching)
1× GPU (96 GB VRAM)
[SMB-Shares] ──► Index-Pipeline (Embedding + Vektor-DB) ──► RAG-Abfragen
```
### Inference: vLLM
- De-facto-Standard für Multi-User-LLM-Serving: Continuous Batching,
PagedAttention, hoher Durchsatz bei vielen gleichzeitigen Nutzern
- OpenAI-kompatible API → jedes Frontend (auch LM Studio-Clients der
Power-User) kann sich verbinden
- Läuft als Docker-Container, gut automatisierbar
### Frontend: Open WebUI
- Multi-User mit Rollen/Gruppen, **native LDAP-Anbindung** (deckt die
Anforderung "Alle User angebunden (LDAP?)" aus den Notizen ab)
- Chat-UI im ChatGPT-Stil — geeignet für Nicht-Power-User
- Eingebaute RAG-Funktion ("Knowledge Bases"): Dokumente hochladen und
durchsuchen
### Datei-Suche über SMB-Shares — der kritische Punkt
"Suchmaschine für eigene Daten" heißt: die Shares müssen **indiziert** werden
(Embedding-Modell + Vektor-Datenbank), nicht nur per Chat angehängt. Optionen:
1. **Open WebUI Knowledge Bases** — seit v0.9.6 mit offiziellem Sync-Tool
`oikb`: gemountete Verzeichnisse (→ SMB-Shares) werden inkrementell in
Knowledge Bases gespiegelt. Details: `20260707-deep-dive.md` §4.3.
2. **Onyx (ehem. Danswer)** — dediziertes Enterprise-Search-Produkt,
Konnektoren für Datei-Quellen, eigene Benutzer-/Rechteverwaltung,
nutzt das lokale LLM via OpenAI-API. Passender für den Vollausbau.
3. Eigene Pipeline (gemountete Shares + Embedding-Job + Qdrant/pgvector) —
maximale Kontrolle, mehr Aufwand.
⚠️ **Offener Punkt, vor Angebot klären:** Müssen Suchergebnisse die
NTFS-/Share-Berechtigungen respektieren (User A darf Dokumente von Abteilung B
nicht sehen)? **Permission-Trimming ist in keiner der Standard-Lösungen
trivial** und bestimmt maßgeblich Aufwand und Produktwahl. Wenn ja, ist Onyx
oder eine kommerzielle Lösung fast zwingend; wenn die Shares ohnehin für alle
lesbar sind, reicht der einfache Stack.
### Modelle (bei 96 GB VRAM)
- Start: ein 2032B-Instruct-Modell (z. B. Qwen3-32B oder gpt-oss-20b) in
FP8 — schnell, viel KV-Cache-Headroom für viele parallele Nutzer, gutes
Deutsch
- Option: 70B-Klasse in FP8 oder gpt-oss-120b (MXFP4, ~63 GB) passt auf die
eine Karte, wenn mehr Qualität gewünscht ist — auf Kosten des Durchsatzes
- Zusätzlich ein Embedding-Modell für die Suche (z. B. multilingual-e5 oder
bge-m3, klein, läuft nebenher auf derselben GPU)
### Betrieb
- Ubuntu Server 24.04 LTS, NVIDIA-Treiber + Container Toolkit
- Alles als Docker Compose (vLLM, Open WebUI, Vektor-DB) → reproduzierbar,
einfache Updates
- Backup nur für Konfiguration + Vektor-DB nötig; Modelle sind wiederbeschaffbar
## 3. Offene Fragen an den Kunden
1. Wie viele Benutzer insgesamt / gleichzeitig? (bestimmt Modellgröße)
2. Datenvolumen und Dateitypen auf den Shares? (Office, PDF, Scans/OCR?)
3. Müssen Berechtigungen der Shares in der Suche durchgesetzt werden? (s. o.)
4. Nur Chat + Suche, oder auch API-Zugriff für Anwendungen/Skripte?
## Quellen
- [LM Studio Docs — Headless/llmster](https://lmstudio.ai/docs/app/api/headless)
- [Top 20 Tools to Run LLMs Locally in 2026 (iunera)](https://www.iunera.com/kraken/enterprise-ai/top-20-tools-to-run-llms-locally-in-2026-ollama-anythingllm-open-webui-lm-studio-vllm-and-every-real-alternative-compared/)
- [Open WebUI Overview (glukhov.org)](https://www.glukhov.org/llm-hosting/llm-frontends/open-webui-overview-quickstart-and-alternatives/)
- [LM Studio Complete Guide 2026 (codersera)](https://codersera.com/blog/lm-studio-complete-guide-2026/)
- [Open WebUI + LM Studio Integration (markaicode)](https://markaicode.com/integrate/open-webui-with-lm-studio/)
@@ -0,0 +1,427 @@
# Deep Dive — GPU-Auswahl, Inference-Backend & Anbindung der SMB-Wissensbasis
Date: 2026-07-07
Status: Entscheidungsgrundlage für Kundengespräch
Basiert auf: `20260706-hardware-assessment.md`, `20260706-software-assessment.md`
---
## 0. Executive Summary
| Entscheidung | Empfehlung | Konfidenz |
|---|---|---|
| GPU | **RTX PRO 6000 Blackwell Server Edition (96 GB)** statt H100 NVL | Hoch — durch mehrere unabhängige Benchmarks gedeckt |
| LM Studio als Server | **Nein** — Desktop-Tool, kein Mehrbenutzer-Server | Hoch |
| Inference-Backend | **vLLM** (ggf. Ollama zusätzlich für Power-User-Experimente) | Hoch |
| Frontend | **Open WebUI** (LDAP/AD, Gruppen, Knowledge Bases) | Hoch |
| SMB-Anbindung | **Phase 1: Open WebUI Knowledge + offizielles Sync-Tool `oikb`**; Phase 2: Onyx nur falls Skalierung/Berechtigungen es erzwingen | Mittel — hängt von offenen Fragen ab (§6) |
---
## 1. GPU: RTX PRO 6000 Blackwell SE vs. H100 NVL
Der Kunde hat die H100 genannt. Das ist verständlich („die KI-GPU"), aber die
H100 ist eine **Hopper-Karte von 2022/23** und für verteiltes Training bzw.
Multi-GPU-Inference gebaut. Die RTX PRO 6000 Blackwell Server Edition (2025)
ist **zwei Architektur-Generationen neuer** und von NVIDIA explizit als
Enterprise-Inference-Karte positioniert.
### 1.1 Daten im Vergleich
| | RTX PRO 6000 Blackwell SE | H100 NVL |
|---|---|---|
| Architektur | Blackwell (2025) | Hopper (2022/23) |
| VRAM | **96 GB GDDR7, ECC** | 94 GB HBM3, ECC |
| Speicherbandbreite | 1,79 TB/s | **3,9 TB/s** |
| FP8 / FP4 (NVFP4) | ✅ / **✅** | ✅ / ❌ |
| MIG (GPU-Partitionierung) | ✅ bis 4 Instanzen ([NVIDIA](https://www.nvidia.com/en-us/data-center/rtx-pro-6000-blackwell-server-edition/)) | ✅ bis 7 Instanzen |
| vGPU-Support | ✅ (vGPU 19.0, [NVIDIA Blog](https://developer.nvidia.com/blog/nvidia-vgpu-19-0-enables-graphics-and-ai-virtualization-on-nvidia-blackwell-gpus/)) | ✅ |
| NVLink | ❌ (nur PCIe Gen5) | ✅ (Paarweise) |
| Leistungsaufnahme | 600 W (passiv, Server-Kühlung) | ~400 W |
| Formfaktor | PCIe FHFL Dual-Slot, OEM-qualifiziert (u. a. [Lenovo](https://lenovopress.lenovo.com/lp2263-thinksystem-nvidia-rtx-pro-6000-blackwell-server-edition-pcie-gen5-gpu), HPE) | PCIe Dual-Slot |
| Straßenpreis | **~910 k€** | ~31 k€ (lt. Referenz-Angebot) |
### 1.2 Benchmark-Evidenz (der Kern der Argumentation)
Mehrere **unabhängige** Quellen, alle mit vLLM als Serving-Stack:
1. **Single-GPU-Durchsatz:** RTX PRO 6000 schlägt sogar die H100 SXM
(3.140 vs. 2.987 tok/s) bei **28 % niedrigeren Kosten pro Token**
([cloudrift.ai](https://www.cloudrift.ai/blog/benchmarking-rtx6000-vs-datacenter-gpus)).
2. **Unter Parallellast** (unser Szenario!): stärkste Skalierung aller
getesteten Karten; auf Qwen-32B vor A100 **und** H100
([databasemart vLLM-Benchmark](https://www.databasemart.com/blog/vllm-gpu-benchmark-pro6000)).
3. **Bei 100 gleichzeitigen Requests**: bis zu **1,63× höherer Durchsatz als
die H100** ([Akamai-Benchmark](https://www.akamai.com/blog/cloud/benchmarking-nvidia-rtx-pro-6000-blackwell-akamai-cloud)).
4. Kostenanalyse pro Million Token: [Spheron](https://www.spheron.network/blog/rent-nvidia-rtx-pro-6000/),
unabhängige Tests: [HOSTKEY](https://huggingface.co/blog/HOSTKEY/nvidia-rtx-6000-blackwell-server-edition-tests-ben),
Vergleich: [OpenMetal](https://openmetal.io/resources/blog/comparing-the-nvidia-rtx-pro-6000-vs-h100-for-ai-inference/).
Warum schlägt GDDR7 hier HBM3? Die H100 spielt ihre Bandbreite erst bei
Multi-GPU-Tensor-Parallelität über NVLink aus. Bei **einer** Karte limitiert
meist Compute/KV-Cache-Management — und da ist Blackwell schlicht neuer
(mehr FP8/FP4-Durchsatz, größere L2-Caches, besserer Scheduler).
### 1.3 Wann wäre die H100 trotzdem richtig? (Ehrlichkeit fürs Gespräch)
- **Multi-GPU-Modelle**: Modelle > 96 GB über 2+ GPUs gespannt → NVLink
gewinnt deutlich (8× H100 SXM ≈ 3× Durchsatz von 8× RTX PRO 6000).
- **Training/Fine-Tuning im Dauerbetrieb** über mehrere Karten.
- **Extreme Kontextlängen + sehr hohe Batchgrößen** (Bandbreiten-limitiert).
**Keiner dieser Punkte trifft hier zu**: 1 GPU, reine Inference, ≤ 20
gleichzeitige Anfragen, Modelle ≤ 96 GB. Selbst der Wachstumspfad (zweite
RTX PRO 6000, zwei unabhängige vLLM-Replikate) braucht kein NVLink.
### 1.4 Argumentationslinie für den Kunden (Kurzform)
1. „Die H100 ist eine 2022er-Architektur am Ende ihres Zyklus; die RTX PRO
6000 ist NVIDIAs aktuelle Enterprise-Inference-Karte (Blackwell, 2025)."
2. „Für genau unser Szenario — eine Karte, viele gleichzeitige Chat-Anfragen —
ist sie in unabhängigen Benchmarks **gleich schnell bis 1,6× schneller**."
3. „Mehr Speicher (96 vs. 94 GB), ECC, MIG, vGPU — alle Enterprise-Merkmale
vorhanden, OEM-qualifiziert (HPE/Lenovo/Dell)."
4. „**Ein Drittel des Preises.** Die Differenz (~21 k€) finanziert die
eigentliche Wertschöpfung — die Such-Pipeline — oder eine zweite GPU."
5. Einziger technischer Trade-off: kein NVLink und höhere Leistungsaufnahme
(600 W) — beides für diesen Use Case irrelevant bzw. vom Chassis abgedeckt.
⚠️ Vor Angebot prüfen: HPE-Qualifizierung der Karte für das konkrete Chassis
(DL380 Gen12 / DL380a) und vBIOS-Stand für MIG.
### 1.5 Alternativen — vollständig geprüft (Stand Juli 2026)
Re-Evaluation gegen das gesamte Feld, nicht nur gegen die H100:
| Kandidat | VRAM | Preis (ca.) | Bewertung für unseren Fall |
|---|---|---|---|
| **RTX PRO 6000 Blackwell SE** | 96 GB | **~910 k€** | ✅ Empfehlung — s. §1.2, aber Risiken in §1.6 beachten |
| H100 NVL | 94 GB | ~2630 k€ | Ausgereifteste Software-Basis (Hopper/SM90), sonst in allen Benchmarks unterlegen; 3× Preis. **Der risikoärmste, teuerste Weg.** |
| H200 NVL | 141 GB | ~3042 k€ ([IntuitionLabs](https://intuitionlabs.ai/articles/nvidia-ai-gpu-pricing-guide), [JarvisLabs](https://jarvislabs.ai/blog/h200-price)) | 45 GB mehr VRAM als nötig; Preis wie H100+1520 %. Nur sinnvoll bei Modellen > 96 GB — nicht unser Fall. |
| L40S | 48 GB | ~8 k€ | Halber Speicher, weniger als halber Durchsatz (864 GB/s, Ada 2022, kein FP4), kaum billiger. Von der RTX PRO 6000 obsolet gemacht. |
| 2× RTX PRO 5000 Blackwell | 2×48 GB | ~9 k€ | Gleicher Gesamt-VRAM, aber Tensor-Parallel über PCIe (Overhead), doppelte Komplexität, gleiche SM120-Risiken. Kein Vorteil. |
| RTX 5090 (Consumer) | 32 GB | ~2,5 k€ | Kein ECC, keine OEM-/Server-Qualifizierung, EULA-Grauzone im Rechenzentrum, 32 GB zu klein. Nein. |
| AMD MI300X | 192 GB | ~1518 k€ ([gpucost](https://gpucost.org/gpu/mi300x)) | Auf dem Papier attraktiv (192 GB!), ROCm/vLLM inzwischen First-Class ([AMD](https://rocm.blogs.amd.com/software-tools-optimization/vllm-omni/README.html)). Aber: **OAM-Modul, keine PCIe-Einzelkarte** — existiert praktisch nur in 8-GPU-Plattformen, nicht im ProLiant-2U. Dazu ROCm-Betriebsrisiko ohne AMD-Erfahrung im Team. Scheidet formfaktorbedingt aus. |
Fazit der Re-Evaluation: Die Empfehlung **hält**. Die einzige echte
Alternative ist die H100 NVL als „konservative Versicherung" zum 3-fachen
Preis — alle anderen Kandidaten scheitern an Formfaktor, Speicher oder
Preis-Leistung.
### 1.6 Risiken der RTX PRO 6000 SE — ehrlich benannt (wichtig!)
Die Karte ist neue Architektur (Blackwell/SM120), und 2025 bis Anfang 2026
gab es dokumentierte Kinderkrankheiten:
- **Kernel-Kompatibilität**: SM120 ist nicht abwärtskompatibel zu
Hopper-Kerneln; exotische Modellarchitekturen liefen anfangs nicht
(z. B. [DeepSeek-Serie](https://github.com/vllm-project/vllm/issues/26211),
[NVFP4-Kernel](https://github.com/vllm-project/vllm/issues/23497)).
Mainstream-Modelle (Llama, Qwen, Mistral, gpt-oss) sind davon nicht
betroffen.
- **Reset-Bug bei Virtualisierung** (VM-Passthrough): GPU nach VM-Shutdown
in unrecoverable state; ab Treiber 580+ weitgehend behoben. → **Bare-Metal
betreiben** (ohnehin empfohlen).
- Vereinzelte Berichte über GPU-Resets unter Dauerlast und ein
[offenes vLLM-Issue (03/2026)](https://github.com/vllm-project/vllm/issues/36327) —
letzteres betrifft `--tensor-parallel-size 4`, also **Multi-GPU, nicht
unsere Single-GPU-Konfiguration**.
**Warum das Risiko tragbar ist (Stand Juli 2026):**
1. NVIDIA führt die Karte offiziell in den
[vLLM-Release-Notes](https://docs.nvidia.com/deeplearning/frameworks/vllm-release-notes/rel-25-09.html)
(funktionaler Support, CUDA 13.0) und stellt einen eigenen
[vLLM-Container für RTX](https://build.nvidia.com/rtx/vllm) bereit.
2. Systemhäuser liefern inzwischen **vLLM-vorvalidierte Server mit genau
dieser Karte** aus ([VRLA Tech](https://vrlatech.com/running-vllm-on-your-own-hardware-the-production-guide-for-2026/)).
3. HPE-qualifizierte Karte + Support-Vertrag = Hardware-Risiko abgedeckt.
**Absicherung im Projekt:** aktueller Treiber (≥ 580/581), NVIDIA-vLLM-
Container statt Eigenbau, Bare-Metal, Mainstream-Modell, und die Pilotphase
(§4.7) als **Burn-in-Test** nutzen — 2 Wochen Dauerlast vor Produktivsetzung.
Falls der Kunde maximale Risikoaversion über Preis stellt: H100 NVL als
dokumentierte Alternative anbieten und die Differenz transparent machen
(~20 k€ „Versicherungsprämie" für ausgereiftere Software-Basis).
---
## 2. Warum LM Studio (llmster) kein Server ist — Argumentation
LM Studio ist bei den Mitarbeitern bekannt und beliebt — das Argument muss
also respektvoll sein: **das Tool ist gut, aber es löst eine andere Aufgabe.**
| Server-Anforderung | LM Studio / llmster | Server-Stack (vLLM + Open WebUI) |
|---|---|---|
| Benutzerkonten & Login | ❌ keine Benutzerverwaltung | ✅ LDAP/AD, Rollen, Gruppen |
| Wer darf was (Modelle, Daten)? | ❌ ein API-Key für alle | ✅ RBAC pro Gruppe/Ressource |
| 1020 gleichzeitige Anfragen | ❌ llama.cpp-Engine, Anfragen stauen sich | ✅ Continuous Batching |
| Zentrale Wissensbasis (SMB) | ❌ nur lokale Datei-Anhänge pro Chat | ✅ zentrale, gepflegte Indizes |
| Audit/Protokollierung | ❌ | ✅ |
| Betrieb (Updates, Monitoring, HA) | ⚠️ Desktop-App-Zyklus | ✅ Container, Standard-Ops |
Kernsatz fürs Gespräch: *„llmster macht aus LM Studio einen Dienst ohne
Bildschirm — aber nicht ohne die Einzelnutzer-Architektur. Es fehlt alles,
was aus einem laufenden Modell einen Firmendienst macht: Anmeldung,
Berechtigungen, Parallelität, zentrale Datenanbindung."*
Wichtig: Die Mitarbeiter **verlieren nichts** — Open WebUI fühlt sich wie
ChatGPT an (einfacher als LM Studio), und Power-User können LM Studio lokal
weiterverwenden oder per API-Key direkt gegen den Server arbeiten.
---
## 3. Inference-Backend: vLLM vs. Ollama
Beide sprechen die OpenAI-API und funktionieren mit Open WebUI. Der
Unterschied liegt in Architektur und Zielgruppe.
### 3.1 Ist vLLM wirklich effizienter? Ja — deutlich, sobald parallel
Aktuelle Benchmarks (2025/26):
- Bei **50 gleichzeitigen Nutzern**: vLLM ~920 tok/s gesamt, Ollama
plateauiert bei ~155 tok/s; je nach Test **2029× mehr Durchsatz** und
819× niedrigere P95-Latenz ([runaihome](https://runaihome.com/blog/vllm-vs-ollama-when-each-wins-2026/),
[tech-insider](https://tech-insider.org/vllm-vs-ollama-2026/)).
- Peer-reviewte Studie: Ollama erreicht **Engpass bei ~10 gleichzeitigen
Nutzern**, 1330 % Timeouts unter Last; vLLM 100 % Erfolgsquote
([MDPI Applied Sciences](https://doi.org/10.3390/app16115435)).
- Bei **einem einzelnen Nutzer** ist Ollama minimal schneller beim ersten
Token (~45 vs. ~82 ms TTFT) — irrelevant für einen Firmenserver
([Red Hat Benchmark](https://developers.redhat.com/articles/2025/08/08/ollama-vs-vllm-deep-dive-performance-benchmarking),
[SitePoint](https://www.sitepoint.com/ollama-vs-vllm-performance-benchmark-2026/)).
### 3.2 Der Modellwechsel-Trade-off, korrekt dargestellt
| | Ollama | vLLM |
|---|---|---|
| Modellwechsel | ✅ automatisch, lädt/entlädt on-the-fly | ⚠️ 1 Modell pro Instanz, Wechsel = Neustart (12 min) |
| Mehrere Modelle parallel | ⚠️ ja, aber teilen sich GPU unkontrolliert | ✅ mehrere Instanzen mit fester VRAM-Zuteilung |
| Durchsatz unter Last | ❌ s. o. | ✅ |
Die Flexibilität von Ollama ist im Firmenbetrieb **weniger wert, als sie
klingt**: Nicht-Power-User wollen kein Modell wählen — sie wollen, dass es
funktioniert. Ein ungeplanter Modellwechsel entlädt zudem das Modell der
anderen 15 aktiven Nutzer (Kaltstart-Latenz für alle).
**Empfehlung — kein Entweder-oder:**
```
GPU (96 GB, via MIG oder VRAM-Quoten aufteilbar)
├── vLLM #1: Haupt-Chatmodell (fest, z. B. Qwen3-32B FP8) ~40 GB
├── vLLM #2: Embedding-Modell für die Suche ~4 GB
└── optional Ollama: Spielwiese für Power-User Rest
```
Open WebUI kann mehrere Backends gleichzeitig einbinden — die Normalnutzer
sehen nur „Firmen-Assistent", Power-User sehen zusätzlich die
Ollama-Modelle. Damit ist die Kundenfrage („Flexibilität vs. Effizienz")
keine Entscheidung mehr, sondern eine Konfiguration.
### 3.3 Lastabschätzung (100 Mitarbeiter)
Erfahrungswerte für interne Chat-Assistenten: ~3060 % registrieren sich,
davon ~1020 % gleichzeitig aktiv in Spitzen → **515 parallele Anfragen**.
Das bedient eine RTX PRO 6000 mit einem 32B-FP8-Modell unter vLLM komfortabel
(vgl. Benchmarks oben: brauchbare Latenz noch bei 50100 parallelen Requests
mit kleineren Modellen).
---
## 4. Anbindung der Wissensbasis (SMB-Shares) — der Deep Dive
### 4.1 Was technisch passieren muss (unabhängig vom Produkt)
Ein LLM kann nicht „auf den Share schauen". Die Pipeline ist immer:
```
SMB-Share ──mount──► Server (read-only, Service-Account)
Extraktion (PDF, Office, E-Mail…; OCR für Scans)
Chunking + Embedding (Embedding-Modell auf der GPU)
Vektor-Datenbank (Index) ← periodischer Sync (cron)
Retrieval zur Laufzeit: Frage → relevante Chunks → LLM antwortet mit Quellen
```
Die Produktfrage ist nur: **wer betreibt diese Pipeline und wie gut?**
### 4.2 Option A — LM Studio: scheidet aus
Kein zentraler Index möglich. Jeder Nutzer müsste Dateien manuell in seinen
Chat ziehen (RAG pro Sitzung, wenige Dateien). Für „Suchmaschine über die
Firmendaten" schlicht das falsche Werkzeug — unabhängig von der Serverfrage.
### 4.3 Option B — Open WebUI Knowledge + offizielles Sync-Tool `oikb` ⭐
Seit **v0.9.6** hat Open WebUI eine offizielle Lösung genau für unseren Fall
([Docs](https://docs.openwebui.com/features/knowledge-base-sync/),
[GitHub open-webui/oikb](https://github.com/open-webui/oikb)):
- **`oikb`** (CLI, [PyPI](https://pypi.org/project/oikb/)) synchronisiert ein
Verzeichnis — also einen **auf dem Server gemounteten SMB-Share** — in eine
Knowledge Base: inkrementell per SHA-256-Diff, nur neue/geänderte Dateien
werden neu embedded, Löschungen werden nachgezogen, Ordnerstruktur wird
gespiegelt. Ein unveränderter 10.000-Dateien-Bestand re-synct in Sekunden.
- Läuft als **Cron/systemd-Timer pro Share** → vollautomatisch, kein
Nutzer-Zutun.
- **Berechtigungen**: Knowledge Bases sind per Gruppe zugreifbar; Open WebUI
synchronisiert **Gruppen direkt aus LDAP/AD**
([LDAP-Doku](https://docs.openwebui.com/features/authentication-access/auth/ldap/)).
Damit lässt sich die Share-Struktur abbilden:
```
\\fs01\konstruktion ──oikb──► KB "Konstruktion" ──► AD-Gruppe Konstruktion
\\fs01\vertrieb ──oikb──► KB "Vertrieb" ──► AD-Gruppe Vertrieb
\\fs01\allgemein ──oikb──► KB "Allgemein" ──► alle
```
- **Nutzererlebnis Normies**: Vorkonfigurierte „Modelle" in Open WebUI
(z. B. „Konstruktions-Assistent" = Chatmodell + KB Konstruktion fest
verdrahtet). Der Nutzer wählt den Assistenten und fragt auf Deutsch —
kein RAG-Wissen nötig, Antworten mit Quellenangabe (Datei + Passage).
- **Power-User**: können eigene Knowledge Bases anlegen, per `#` gezielt
Kollektionen in den Chat holen, oder per API-Key direkt gegen vLLM arbeiten
(IDE-Integration, Skripte).
**Grenzen (ehrlich einplanen):**
1. **Berechtigungsgranularität = Share/Ordner-Ebene**, nicht NTFS-Datei-ACL.
Wer Zugriff auf die KB „Konstruktion" hat, kann alles darin erfragen.
2. **Skala**: praktikabel bis in den Bereich von einigen zehntausend
Dokumenten pro Kollektion; bei sehr großen Shares selektiv syncen
(Unterordner, Dateitypen-Filter). Für den DB-Unterbau bei Wachstum
**pgvector statt Default** einplanen ([Scaling-Doku](https://docs.openwebui.com/getting-started/advanced-topics/scaling/)).
3. **Extraktionsqualität** hängt von den Dateitypen ab: Office/PDF gut;
**gescannte PDFs brauchen OCR** (Tika/Docling als Extraktions-Engine
konfigurieren); CAD/proprietäre Formate bleiben außen vor.
### 4.4 Option C — Onyx (ehem. Danswer) als dedizierte Such-Anwendung
[Onyx](https://github.com/onyx-dot-app/onyx) ist ein spezialisiertes
Enterprise-Search-Produkt mit 50+ Konnektoren, darunter ein **File Connector
für lokale/Netzlaufwerke** ([Connector-Doku](https://docs.onyx.app/overview/core_features/connectors)) —
SMB-Anbindung also wie bei Option B über einen Mount, aber mit produktisierter
Crawling-/Index-Pipeline.
- **Stärken**: bessere Such-Ergonomie (hybride Suche, Reranking), skaliert
auf große Bestände, Konnektoren für spätere Quellen (SharePoint, Wiki,
E-Mail…), eigenes Berechtigungsmodell.
- **Schwächen für unseren Fall**:
- **Document-Level Permission Sync ist Enterprise Edition** (kostenpflichtig)
und primär für Cloud-Quellen (SharePoint, Google Drive) gebaut — für
SMB-NTFS-ACLs gibt es auch hier **keine fertige Lösung**.
- **Zweite Anwendung** mit eigener UI, eigener Nutzerverwaltung, eigenem
Betrieb (mehrere Container, Vespa-Index, Worker) — spürbar mehr
Betriebsaufwand als Option B.
- Zwei UIs für die Nutzer (Chat in Open WebUI, Suche in Onyx) oder man
ersetzt Open WebUI ganz durch Onyx' Chat — dann verliert man dessen
Flexibilität (Multi-Backend, Power-User-Features).
### 4.5 Option D — Eigenbau-Pipeline (Qdrant/pgvector + eigener Indexer)
Maximale Kontrolle (eigenes ACL-Modell, beliebige Formate/OCR, Anbindung an
Open WebUI über dessen Pipelines/Tools-API) — aber man betreibt und wartet
eine Individualsoftware. Nur gerechtfertigt, wenn **Datei-genaue
NTFS-Berechtigungen** harte Anforderung sind; dann als Projekt kalkulieren
(mehrere Wochen Entwicklung + laufende Pflege).
### 4.6 Entscheidungsmatrix
| Kriterium | B: Open WebUI + oikb | C: Onyx | D: Eigenbau |
|---|---|---|---|
| Aufwand Einführung | **niedrig** (Tage) | mittel (Wochen) | hoch |
| Betriebsaufwand | **niedrig** (1 Stack) | mittel (2 Stacks) | hoch |
| Berechtigungen | Share/Ordner-Ebene via AD-Gruppen | ähnlich; Datei-Ebene nur EE + nicht für SMB | frei gestaltbar |
| Suchqualität große Bestände | gut bis ~10⁴–10⁵ Docs | **sehr gut, skaliert** | je nach Bau |
| Normie-Tauglichkeit | **sehr gut** (ein UI, Assistenten) | gut | je nach Bau |
| Power-User | **sehr gut** (API, eigene KBs, Ollama) | mittel | gut |
| Kosten | Open Source | OSS; Permissions = EE-Lizenz | Entwicklungszeit |
### 4.7 Empfehlung: Phasenmodell
- **Phase 1 (Pilot, ~46 Wochen):** vLLM + Open WebUI + LDAP + `oikb` auf
12 ausgewählte Shares (read-only Service-Account). Misst reale Nutzung,
Suchqualität und die tatsächliche Parallellast — mit minimalem Invest.
- **Phase 2 (Entscheid nach Pilot):** Bleiben = Ausbau Option B (mehr Shares,
pgvector, OCR). Nur bei nachgewiesenem Bedarf (sehr große Bestände, harte
Berechtigungsanforderungen) → Onyx bzw. Eigenbau ergänzen. Die
Investition aus Phase 1 (Server, vLLM, LDAP, Mounts) bleibt vollständig
erhalten — Onyx würde denselben vLLM-Endpunkt nutzen.
Das Phasenmodell ist auch das Verkaufsargument: kein Big-Bang, Budget bleibt
unter Kontrolle, und die GPU-Ersparnis aus §1 finanziert die Pilotphase.
---
## 5. Gesamtbild (Zielarchitektur Phase 1)
```
┌────────────────────────────────────────────┐
Mitarbeiter ────────►│ Open WebUI (LDAP/AD-Login, Gruppen) │
(Browser) │ ├─ „Firmen-Assistent" + KB je Abteilung │
│ └─ Power-User: API-Keys, eigene KBs │
└───────┬────────────────────────┬───────────┘
Power-User ─────────────────►│ OpenAI-API │ Retrieval
(Skripte, IDE, LM Studio ▼ ▼
lokal als Client) vLLM #1 Chat 32B FP8 Vektor-DB (pgvector)
vLLM #2 Embeddings ▲
┌──────────────────────┐ │ oikb-Sync (cron)
│ RTX PRO 6000 96 GB │ SMB-Mounts (ro)
└──────────────────────┘ ▲
\\fs01\... Shares
```
## 6. Offene Fragen an den Kunden (vor Angebotslegung)
1. **Berechtigungen** (kritischster Punkt): Genügt Zugriffssteuerung auf
Share-/Ordner-Ebene via AD-Gruppen — oder müssen Datei-genaue NTFS-ACLs
durchgesetzt werden? (Letzteres = Eigenbau-Anteil, deutlich teurer.)
2. **Datenbestand**: Wie viele Shares, wie viel Volumen, welche Dateitypen?
Anteil gescannter Dokumente (OCR-Bedarf)? CAD-Daten relevant?
3. **Nutzerkreis**: Wie viele der ~100 Mitarbeiter sollen initial angebunden
werden? Gibt es Abteilungen mit Priorität?
4. **H100-Motivation**: Steht hinter dem H100-Wunsch eine konkrete Anforderung
(Training? Zukunftspläne?) oder Markenwahrnehmung? → bestimmt, wie viel
der Argumentation aus §1.4 nötig ist.
5. **Virtualisierung**: Soll die GPU ggf. in eine VM-Umgebung (vGPU/MIG,
Lizenzthema) oder Bare-Metal-Betrieb (empfohlen, einfacher)?
6. **Sprache**: Dokumente überwiegend Deutsch? (Modellwahl: Qwen3/Mistral/
Llama sind mehrsprachig solide; Embedding-Modell muss multilingual sein,
z. B. bge-m3.)
## 7. Quellen
**GPU:**
[NVIDIA RTX PRO 6000 Blackwell SE](https://www.nvidia.com/en-us/data-center/rtx-pro-6000-blackwell-server-edition/) ·
[cloudrift.ai Benchmark](https://www.cloudrift.ai/blog/benchmarking-rtx6000-vs-datacenter-gpus) ·
[Akamai Benchmark](https://www.akamai.com/blog/cloud/benchmarking-nvidia-rtx-pro-6000-blackwell-akamai-cloud) ·
[databasemart vLLM-Benchmark](https://www.databasemart.com/blog/vllm-gpu-benchmark-pro6000) ·
[HOSTKEY Tests](https://huggingface.co/blog/HOSTKEY/nvidia-rtx-6000-blackwell-server-edition-tests-ben) ·
[OpenMetal RTX PRO 6000 vs H100](https://openmetal.io/resources/blog/comparing-the-nvidia-rtx-pro-6000-vs-h100-for-ai-inference/) ·
[Spheron Cost/Mtok](https://www.spheron.network/blog/rent-nvidia-rtx-pro-6000/) ·
[Lenovo Product Guide](https://lenovopress.lenovo.com/lp2263-thinksystem-nvidia-rtx-pro-6000-blackwell-server-edition-pcie-gen5-gpu) ·
[NVIDIA vGPU 19.0](https://developer.nvidia.com/blog/nvidia-vgpu-19-0-enables-graphics-and-ai-virtualization-on-nvidia-blackwell-gpus/) ·
[MIG auf RTX PRO 6000 (Medium)](https://medium.com/@sangjinn/maximize-your-gpu-efficiency-configuring-4-nvidia-mig-instances-on-the-rtx-pro-6000-blackwell-1c9b3714af61)
**vLLM vs Ollama:**
[Red Hat Benchmark](https://developers.redhat.com/articles/2025/08/08/ollama-vs-vllm-deep-dive-performance-benchmarking) ·
[MDPI-Studie](https://doi.org/10.3390/app16115435) ·
[runaihome Konkurrenz-Zahlen](https://runaihome.com/blog/vllm-vs-ollama-when-each-wins-2026/) ·
[tech-insider](https://tech-insider.org/vllm-vs-ollama-2026/) ·
[SitePoint](https://www.sitepoint.com/ollama-vs-vllm-performance-benchmark-2026/)
**Open WebUI / RAG:**
[Knowledge-Doku](https://docs.openwebui.com/features/workspace/knowledge/) ·
[Knowledge Base Sync / oikb](https://docs.openwebui.com/features/knowledge-base-sync/) ·
[oikb GitHub](https://github.com/open-webui/oikb) ·
[LDAP-Doku](https://docs.openwebui.com/features/authentication-access/auth/ldap/) ·
[Scaling-Doku](https://docs.openwebui.com/getting-started/advanced-topics/scaling/)
**Onyx:**
[Onyx GitHub](https://github.com/onyx-dot-app/onyx) ·
[Connector-Übersicht](https://docs.onyx.app/overview/core_features/connectors)
@@ -0,0 +1,251 @@
# Projektplan — Setup phy-srv-gpu01 (LLM-Server mit SMB-Wissensbasis)
Date: 2026-07-10 (Stand-Update: 2026-09-03)
Status: **In Umsetzung** — Server geliefert, Basis-Setup + GPU-Treiber fertig
Basiert auf: `20260706-hardware-assessment.md`, `20260706-software-assessment.md`, `20260707-deep-dive.md`
---
## Stand 2026-09-03 (Kurzfassung — Details in den jeweiligen Abschnitten)
**Erledigt:** Server geliefert und im Rack; Basis-Setup (Ubuntu 24.04.4, Nutzer,
Netz, RAID/Filesystem — s. `SETUP.md` im Repo-Root); GPU-Treiber via Ansible-Rolle
`nvidia_gpu` (595er-Branch, **open** Kernel-Module — Blackwell läuft nicht mit den
proprietären Modulen, s. `manuals/20260714-nvidia-driver-install.md`); Docker
installiert. Share-Analyse gelaufen (§2.1).
**Als Nächstes:** CIFS-Mounts + Container-Stack (Rollen `cifs_mounts`, `llm_stack`)
— entspricht Phasen 35 in §3.
**Kritischer offener Punkt:** Der Bestand ist rund **12× größer** als die in §1
gesetzte Schwelle. Ohne Eingrenzung des Korpus (Kundengespräch) ist Phase 5 nicht
sinnvoll umsetzbar — s. §2.1 und §2.6.
## 0. Rahmenbedingungen (fixiert)
| Punkt | Entscheidung |
|---|---|
| Hardware | HPE DL380 Gen12, 2× Xeon 6714P, 128 GB RAM, RTX PRO 6000 96 GB, 2× 960 GB NVMe — s. `HW.md` |
| Betrieb | **Bare Metal**, keine Virtualisierung des LLM-Stacks |
| Nutzer | ~25, gemischt (technisch versiert + Nicht-Techniker) |
| Auth | **AD/LDAP** (Details Service-Account/Basis-DN als Pre-Work klären) |
| Berechtigungen | **Einfach: alle Nutzer sehen den gesamten indizierten Bestand** (Kunde bestätigt; Shares ohne HR-/Personal-Inhalte halten) |
| Sprache | Dokumente & Nutzer überwiegend **Deutsch** → Modell- und Embedding-Wahl daran ausrichten |
| Inference | **vLLM** mit fester GPU-Auslastung (kein Ollama als Basis) |
| Wissensbasis | SMB-Shares (einige TB, PDF/Office), **Sync ist die Kernanforderung** |
| Später | Jira-Anbindung u. a. Workloads; dafür ggf. **MIG** |
| Vorgehen | Setup vollständig über **Ansible** (dieses Repo), PoC vorab: nein — reine Planung |
## 1. Produktentscheidung: Open WebUI statt Onyx
**Empfehlung: Open WebUI** (bestätigt und verschärft die Empfehlung aus dem
Deep Dive §4.7). Ausschlaggebend, Stand 2026-07-10 verifiziert:
1. **Onyx' File-Connector ist reiner manueller Upload** — kein Verzeichnis-Watching,
kein periodischer Re-Sync eines gemounteten Shares
([Doku](https://docs.onyx.app/admins/connectors/official/file)). Die
Kernanforderung „Shares synchron halten" hieße bei Onyx: Eigenbau gegen
deren API. Open WebUI hat dafür das offizielle Sync-Tool
[`oikb`](https://github.com/open-webui/oikb) (inkrementell, Löschungen,
Cron-fähig — Deep Dive §4.3).
2. **Onyx: SSO (OIDC/SAML), User Groups, RBAC, Permission-Sync = Enterprise
Edition (kostenpflichtig)**
([Doku](https://docs.onyx.app/deployment/miscellaneous/enterprise_edition)).
Open WebUI: LDAP/AD-Anbindung und Gruppen sind frei.
3. Onyx bringt einen zweiten schweren Stack mit (Vespa-Index, Postgres,
Worker) — auf 128 GB RAM **neben** vLLM spürbar; widerspricht „it just works".
4. Bedienung: ein UI im ChatGPT-Stil, vorkonfigurierte Assistenten mit fester
Knowledge Base → passt für den gemischten Nutzerkreis.
**Wann die Entscheidung zu revidieren ist** (Trigger, nach der Share-Analyse §2.1):
- Der *relevante* Bestand liegt deutlich über ~10⁵ Dokumenten und lässt sich
nicht sinnvoll filtern → Suchqualität/Skalierung von Open WebUI prüfen,
Onyx (mit Eigenbau-Sync) oder Eigenbau-Pipeline (Deep Dive §4.5) neu bewerten.
- Datei-genaue NTFS-ACLs werden doch Anforderung → Neubewertung (Deep Dive §4.4/4.5).
**Status 2026-09-03:** Trigger 1 ist **eingetreten** (1,26 Mio. Office-Dateien,
§2.1). Die Produktentscheidung bleibt dennoch bei Open WebUI — aber **nur unter
der Bedingung**, dass der Korpus auf einen kuratierten Teilbestand eingegrenzt
wird (§2.6). Ein ungefilterter Vollindex über 1,26 Mio. Dokumente ist mit Open
WebUI nicht sinnvoll zu betreiben (und liefert auch inhaltlich schlechte
Antworten — RAG über einen kompletten, ungeordneten Fileserver retourniert
überwiegend Rauschen).
Jira später: für *Indizierung* von Jira-Inhalten hat Onyx zwar native
Konnektoren, Open WebUI deckt das aber über Tools/MCP (Atlassian-MCP) bzw.
API-Workloads direkt gegen vLLM ab — kein Entscheidungskriterium für heute.
## 2. Phase 0 — Pre-Work (jetzt, ohne Server)
### 2.1 Share-Analyse (wichtigster Punkt — ja, unbedingt vorab)
Read-only-Skript auf einer beliebigen Maschine mit Share-Zugriff (kein
GPU-Server nötig). Zu erheben:
- Volumen & Dateianzahl **pro Share und Dateityp** (pdf/docx/xlsx/…)
- Anteil **gescannter PDFs** (Stichprobe: PDFs ohne Textlayer) → OCR-Bedarf
- Duplikate/Altversionen („Kopie von …", Versionsordner) → Filterregeln
- Änderungsrate (mtime-Verteilung) → Sync-Frequenz
- Anteil nicht extrahierbarer Formate (CAD, Bilder, Archive) → Erwartungsmanagement
- Sonderzeichen/Pfadlängen, Encoding (Umlaute!)
Ergebnis bestimmt: Korpus-Umfang (→ §1-Trigger), OCR-Pipeline ja/nein,
Index-Größe (→ Storage-Check §2.3), Erst-Indexierungsdauer.
**Ergebnis (Lauf 2026-07-14, Skript `scripts/share-analysis.ps1` auf `D:` des
Fileservers Z-FILESERVER):**
| Kennzahl | Wert | Konsequenz |
|---|---|---|
| Gesamt | **3.322.175 Dateien, 2.224 GB** | Scan lief über `D:` gesamt, nicht je Share → Obergrenze, enthält evtl. nicht freigegebene Daten |
| `office` (extrahierbar) | **1.258.642 Dateien, 587 GB** | **12× über der §1-Schwelle** → Eingrenzung zwingend (§2.6) |
| `other` (unklassifiziert) | 1.575.752 Dateien, 934 GB | größter Block, **noch unidentifiziert**`D-file-types.csv` auswerten |
| Bilder / CAD / Archive / Medien | 403k / 60k / 12,6k / 6,2k Dateien | nicht extrahierbar → Erwartungsmanagement |
| PDFs | **924.076**, davon ~**18 % ohne Textlayer** (Stichprobe 200) | ≈ **166.000 Scan-PDFs → OCR**; eigener Zeit-/GPU-Aufwand, konkurriert mit vLLM |
| Änderungsrate | 94 % älter als 1 Jahr; nur ~158k in den letzten 365 Tagen | Sync-Frequenz unkritisch; **Alter ≠ Irrelevanz** (Datenblätter, Normen) → Filter primär ordner-/inhaltsbasiert, Aktualität nur sekundär |
| Duplikat-/Altversions-Muster | 33.179 Dateien | Filterregeln |
| Pfade > 240 Zeichen | 25.169 | Extraktions-Toolchain gezielt testen |
| Nicht-ASCII-Dateinamen | 126.111 | UTF-8 durchgängig (CIFS-Mount-Optionen → Extraktion → Index) |
**Noch auszuwerten** (CSVs liegen auf dem Fileserver, noch nicht gesichtet):
`D-toplevel-folders.csv` (Größe/Anzahl je Top-Level-Ordner — die Grundlage für
die Include-/Exclude-Liste) und `D-file-types.csv` (Auflösung der `other`-Kategorie).
### 2.2 Mit dem Kunden zu klären (Checkliste) — Stand 2026-09-03
- [x] AD/LDAP: AD-Gruppe **`llm_users`** wird angelegt. *Offen:* Bind-Service-Account
und Basis-DN (werden für die LDAP-Konfiguration in Open WebUI gebraucht)
- [x] **SMB-Service-Account read-only** wird separat angelegt (kein Nutzer-Account)
- [ ] **Welche Shares/Unterordner in den Index?** — offen, wird besprochen;
**wichtigster Punkt**, s. §2.1/§2.6
- [x] DNS-Name **`chat.phytron.local`** (vorerst); **kein TLS/keine lokalen
Zertifikate** zu Beginn → reines HTTP im LAN. *Hinweis:* AD-Passwörter gehen
damit im Klartext über das Netz; für später ein internes CA-Zertifikat
einplanen (Reverse Proxy ist vorbereitet, nur Zertifikat fehlt)
- [x] Internetzugang: **unbeschränkt**, keine Einschränkung nötig
- [ ] Protokollierung/Datenschutz: **noch zu besprechen** (Chat-Logs =
Verhaltensdaten; Betriebsrat/DSGVO) — vor dem Rollout klären, nicht vor der
technischen Umsetzung
- [x] Betrieb/Wartung/Updates: **wir** (Softbox)
- [x] „Andere Workloads": **Jira vorerst nicht relevant** → MIG bleibt aus, die
volle GPU geht an den LLM-Stack
### 2.3 Storage-Check — ✅ erledigt, unkritisch
Ist-Stand auf dem Server: **876 GB nutzbar** (`/`, LVM), davon 818 GB frei.
Gegenrechnung mit §2.1: selbst ein Vollindex des `office`-Bestands
(1,26 Mio. Dokumente → grob 150250 GB inkl. extrahiertem Text, Embeddings und
Index-Overhead) plus Modelle (~100 GB) passt. Strategie bleibt: **kein lokaler
Spiegel der Shares**, nur Index. **Keine zusätzlichen NVMe nötig.**
Der begrenzende Faktor ist damit *nicht* der Plattenplatz, sondern Retrieval-Qualität
und Indexgröße (§2.6).
### 2.4 Ansible vorbereiten (im Repo, testbar ohne GPU)
- `run.yml`: Play für `phy_srv_gpu01` ergänzen
- Rollen-Skelett unter `ansible/roles/` (Details erst bei Umsetzung):
- `nvidia_gpu` — Treiber (≥ 580), Container Toolkit, optional MIG, DCGM
- `cifs_mounts` — ro-Mounts, Credentials aus `group_vars/secrets.yml`
- `llm_stack` — Docker Compose: vLLM (feste `--gpu-memory-utilization`),
Embedding-Server, Open WebUI + pgvector, Reverse Proxy (TLS), oikb-Timer
- Bestehendes nachnutzen: `geerlingguy.security` (Basis-Härtung),
`geerlingguy.docker`
- `group_vars/phy_srv_gpu01.yml`: nur Overrides (Modellname, VRAM-Quote,
Share-Liste, LDAP-Parameter)
- Alles außer GPU-Rolle ist vorab in einer Wegwerf-VM testbar (`just run`-Pfad)
### 2.5 Modell-Shortlist — Entscheidung 2026-09-03
**Startmodell: Qwen3-32B FP8.** Begründung: ~35 GB VRAM, damit reichlich Reserve
für KV-Cache (~25 parallele Nutzer) und das Embedding-Modell daneben; gutes
Deutsch; hoher Durchsatz. Alternative bei Qualitätsbedarf: gpt-oss-120b
(MoE, ~6065 GB) — weniger Parallelität, engerer Fit. Embeddings:
bge-m3 / multilingual-e5 (mehrsprachig, zwingend für Deutsch).
Das ist der **Startwert als Rollen-Variable**, keine endgültige Festlegung: Die
finale Wahl fällt gegen das Eval-Set (s. u.) und nach dem vLLM-Smoke-Test.
⚠️ **Offener technischer Punkt:** Die offiziellen vLLM-Docker-Images unterstützen
SM120 (Blackwell Workstation/Server) laut aktueller Quellenlage **nicht
zuverlässig out of the box**; auf RTX-PRO-6000-Systemen werden verbreitet
Nightly- oder Community-Builds eingesetzt
([vLLM-Forum](https://discuss.vllm.ai/t/support-for-rtx-6000-blackwell-96gb-card/1707),
[rtx6kpro](https://github.com/local-inference-lab/rtx6kpro/blob/master/inference-engines/vllm.md)).
Vor dem Bau der Rolle `llm_stack` daher **ein Container manuell auf dem Server
testen** und das funktionierende Image pinnen — dieselbe Fehlerklasse wie beim
Treiber (proprietäre vs. open Kernel-Module).
Kleines deutsches Eval-Set (2030 Fragen mit erwarteten Antworten aus dem
Bestand) mit dem Kunden sammeln — das ist später das Abnahmekriterium.
### 2.6 Korpus eingrenzen (neu, wichtigster offener Punkt)
Die Share-Analyse (§2.1) hat 1,26 Mio. potenziell extrahierbare Dokumente
ergeben. „Alles indizieren" ist weder technisch sinnvoll noch inhaltlich
wünschenswert. Vorschlag für das Kundengespräch:
1. **Ordner statt Alter als Filter.** Aus `D-toplevel-folders.csv` gemeinsam mit
dem Kunden die Ordner markieren, die tatsächlich Wissensbasis sind
(Doku, Normen, Datenblätter, Handbücher, Projektdoku) — Rest bleibt außen vor.
Reiner Aktualitätsfilter wäre falsch: Datenblätter und Normen sind über Jahre
gültig.
2. **Ausschlusslisten** für Duplikate/Altversionen (33k Treffer), Archiv- und
Backup-Ordner, nicht extrahierbare Formate (CAD, Bilder, Medien).
3. **Zielgröße nennen.** Als Orientierung: eine gut funktionierende
Wissensbasis liegt eher im Bereich **10.000100.000 Dokumente**. Das ist
kein hartes Limit, aber der Bereich, in dem Retrieval-Qualität und
Indexierungsdauer beherrschbar bleiben.
4. **Iterativ starten.** Phase 5 zuerst mit *einem* gut abgegrenzten Share/Ordner,
Qualität am Eval-Set (§2.5) messen, dann erweitern. Das erspart einen
tagelangen Fehlversuch über den Gesamtbestand.
**Erwartungsmanagement gegenüber dem Kunden:** Die ursprüngliche Anforderung
lautete sinngemäß „unsere Dokumente auslesen". Realistisch ist „ein kuratierter
Teil unserer Dokumente, dafür mit guten Antworten". Das früh sagen — nicht erst,
wenn der Vollindex schlechte Treffer liefert.
## 3. Phasen ab Server-Lieferung
| Phase | Inhalt | Ergebnis/Abnahme |
|---|---|---|
| **1. Basis** (Woche 1) | Rack/Strom (600-W-GPU!), iLO, Firmware, RAID, Ubuntu 24.04 LTS, Eintrag in Ansible-Basis-Setup (Security, Pakete, Nutzer) | `just run phy_srv_gpu01` läuft grün |
| **2. GPU-Stack** (Woche 12) | Rolle `nvidia_gpu`: Treiber, Container Toolkit, DCGM; **Burn-in unter Dauerlast** (SM120-Risiken, Deep Dive §1.6); MIG erst mal **aus** | `nvidia-smi` ok, 48 h-Lasttest ohne Reset |
| **3. Inference** (Woche 2) | vLLM-Container (NVIDIA-Build) mit gewähltem Modell, feste VRAM-Quote, Embedding-Server daneben; Benchmark mit Eval-Set | deutsche Antworten ok, Ziel-Parallelität erreicht |
| **4. UI + Auth** (Woche 23) | Open WebUI + pgvector, LDAP-Login, Reverse Proxy + TLS, vorkonfigurierter „Phytron-Assistent" | Login mit AD-Konto, Chat läuft |
| **5. Wissensbasis** (Woche 35) | CIFS-Mounts (ro), Extraktion inkl. OCR-Engine (Tika/Docling), oikb-Sync erst auf **einen Teilbestand**, Qualität prüfen, dann Vollindex (Dauer aus §2.1 abschätzen) | Fragen aus dem Eval-Set werden mit korrekten Quellen beantwortet |
| **6. Pilot → Rollout** (Woche 57) | 35 Pilotnutzer (gemischt), Feedback, dann alle 25; Onboarding-Einseiter (was kann es, was nicht — CAD/Scans!) | Abnahme durch Kunden |
| **7. Betrieb** | Monitoring (GPU/DCGM, Dienste), Backup (Konfig + DBs — Modelle nicht), Update-Runbook `manuals/`, Doku im README | Runbooks vorhanden |
| **später** | Jira & weitere Workloads; **erst dann MIG-Layout** festlegen (bis 4 Instanzen) statt jetzt raten | — |
## 4. Risiken
| Risiko | Umgang |
|---|---|
| ~~Storage zu klein für Index~~ | ✅ erledigt — 876 GB nutzbar, reicht (§2.3) |
| **Korpus 12× über der Planungsschwelle (§2.1)** | **Eingrenzung auf kuratierten Teilbestand (§2.6); ohne Kundenentscheid keine Phase 5** |
| ~166.000 Scan-PDFs → OCR-Aufwand & -Qualität | eigener Arbeitsblock, GPU-Konkurrenz zu vLLM einplanen; Erwartungen dämpfen |
| ~~SM120/Treiber-Kinderkrankheiten~~ | ✅ erledigt — Treiber 595 **open** läuft (proprietäre Module scheitern an `RmInitAdapter`) |
| Kein TLS zu Beginn (§2.2) | AD-Passwörter im Klartext im LAN; internes CA-Zertifikat nachrüsten, Reverse Proxy vorbereiten |
| Software-Stand veraltet bis Lieferung | Versionen **erst bei Installation pinnen**; Wiedereinstiegs-Checkliste |
| Erst-Indexierung dauert Tage | Teilbestand zuerst; Sync nachts; Dauer vorab abschätzen |
| RAM-Konkurrenz Extraktion/OCR vs. vLLM | vLLM hat feste VRAM-Quote; Indexer-Jobs drosseln (nice/cgroups); 128 GB reichen für OWUI-Stack |
| Datenschutz/Betriebsrat bei Chat-Logs | Früh klären (§2.2), Logging-Policy dokumentieren |
| Kein HA — ein Server | Erwartung managen: Wartungsfenster = Dienst weg; Backup-Restore-Runbook |
## 5. Wiedereinstiegs-Checkliste (wenn der Server da ist)
1. [x] Dieses Dokument + Deep Dive lesen; offene Punkte aus §2.2 abhaken
2. [x] Ergebnis der Share-Analyse vorliegen? → §2.1
3. [ ] Neu sichten & **dann erst pinnen**: vLLM-Version (RTX-PRO-6000-/SM120-Support),
Open WebUI + oikb Release Notes, Modell-Shortlist §2.5 — NVIDIA-Treiber ✅ (595-open)
4. [x] Storage-Entscheidung §2.3 → unkritisch, nichts nachzubestellen
5. [ ] Dann Phasenplan §3 von oben abarbeiten; jede Phase = Ansible-Commit + ggf.
Runbook unter `manuals/`
## 6. Bewusst offen gelassen (kein Jetzt-Entscheid nötig)
- Exaktes Modell & Quantisierung, vLLM-Flags, MIG-Layout, OCR-Engine-Wahl,
Monitoring-Stack-Details, Reverse-Proxy-Produkt — alles bei Umsetzung mit
aktuellem Stand entscheiden; Rahmen steht oben.
@@ -0,0 +1,127 @@
<#
.SYNOPSIS
Lists all SMB shares of a Windows file server incl. local path, size and
permissions — the basis for the include/exclude decision (projektplan §2.6).
.DESCRIPTION
Run ON the file server (or against a remote one via -ComputerName). Reports
per share: name, local path, description, share-level permissions, NTFS
groups, plus optional size/file count. Admin shares (C$, ADMIN$, IPC$) are
skipped unless -IncludeAdminShares is given.
Purely read-only. Requires local admin for Get-SmbShare (part of Windows
Server, no extra module needed).
.EXAMPLE
.\list-shares.ps1
.EXAMPLE
# with size per share — run this ON the file server, it then walks the local
# paths instead of the UNC paths (far faster). Still minutes to hours on
# millions of files; progress is printed every 50k files.
.\list-shares.ps1 -WithSize
.NOTES
Faster alternative when all shares live under one drive (as on Z-FILESERVER,
where everything sits under D:): run share-analysis.ps1 once with a matching
folder depth instead of measuring every share separately, e.g.
.\share-analysis.ps1 -Paths "D:" -FolderDepth 2
That produces size/count per D:\Abteilungen\<Share> in a single pass over
the disk, which is what the share list here maps onto.
#>
param(
[string]$ComputerName = $env:COMPUTERNAME,
[switch]$IncludeAdminShares,
[switch]$WithSize,
[string]$OutDir = (Join-Path (Get-Location) "share-list")
)
$ErrorActionPreference = 'Continue'
New-Item -ItemType Directory -Path $OutDir -Force | Out-Null
Write-Host "=== SMB shares on $ComputerName ===" -ForegroundColor Cyan
$shares = Get-SmbShare -CimSession $ComputerName -ErrorAction Stop
if (-not $IncludeAdminShares) {
$shares = $shares | Where-Object { -not $_.Name.EndsWith('$') }
}
$result = foreach ($sh in $shares) {
Write-Host (" {0,-25} {1}" -f $sh.Name, $sh.Path)
# share-level permissions
$sharePerms = try {
(Get-SmbShareAccess -Name $sh.Name -CimSession $ComputerName -ErrorAction Stop |
ForEach-Object { "$($_.AccountName)=$($_.AccessRight)" }) -join '; '
} catch { 'n/a' }
# NTFS permissions (top level only) — who actually has access
$ntfsPerms = try {
$uncPath = "\\$ComputerName\$($sh.Name)"
((Get-Acl -LiteralPath $uncPath -ErrorAction Stop).Access |
ForEach-Object { "$($_.IdentityReference)=$($_.FileSystemRights)" } |
Select-Object -Unique | Select-Object -First 10) -join '; '
} catch { 'n/a' }
$sizeGB = $null; $fileCount = $null
if ($WithSize) {
# Walk the LOCAL path when we are on the server itself — going through
# the UNC path (\\server\share) routes every single file through the SMB
# stack and is slower by orders of magnitude on large shares.
$scanPath = if ($ComputerName -eq $env:COMPUTERNAME -and $sh.Path) {
$sh.Path
} else {
"\\$ComputerName\$($sh.Name)"
}
$sw = [System.Diagnostics.Stopwatch]::StartNew()
$n = 0L; $bytes = 0L
Get-ChildItem -LiteralPath $scanPath -Recurse -File -Force -ErrorAction SilentlyContinue |
ForEach-Object {
$n++; $bytes += $_.Length
if ($n % 50000 -eq 0) {
Write-Host (" ... {0:N0} files, {1:N1} GB ({2:N0}s)" -f $n, ($bytes / 1GB), $sw.Elapsed.TotalSeconds) -ForegroundColor DarkGray
}
}
$sw.Stop()
$sizeGB = [Math]::Round($bytes / 1GB, 2)
$fileCount = $n
Write-Host (" {0:N0} files, {1:N1} GB in {2:N0}s" -f $n, ($bytes / 1GB), $sw.Elapsed.TotalSeconds) -ForegroundColor DarkGray
}
[PSCustomObject]@{
Name = $sh.Name
Path = $sh.Path
Description = $sh.Description
SharePermissions = $sharePerms
NtfsPermissions = $ntfsPerms
SizeGB = $sizeGB
FileCount = $fileCount
}
}
$csv = Join-Path $OutDir "smb-shares.csv"
$result | Export-Csv $csv -NoTypeInformation -Encoding UTF8
$result | Format-Table Name, Path, SizeGB, FileCount -AutoSize
# DFS namespaces (if used) — often the path users actually see
Write-Host "`n=== DFS namespaces ===" -ForegroundColor Cyan
try {
$roots = Get-DfsnRoot -ErrorAction Stop
if ($roots) {
$dfs = foreach ($r in $roots) {
Get-DfsnFolder -Path "$($r.Path)\*" -ErrorAction SilentlyContinue | ForEach-Object {
$t = Get-DfsnFolderTarget -Path $_.Path -ErrorAction SilentlyContinue
[PSCustomObject]@{ DfsPath = $_.Path; Targets = ($t.TargetPath -join '; ') }
}
}
$dfs | Export-Csv (Join-Path $OutDir "dfs-namespaces.csv") -NoTypeInformation -Encoding UTF8
$dfs | Format-Table -AutoSize
} else { Write-Host " none" }
} catch {
Write-Host " no DFS role / not available on this host"
}
Write-Host "`nResults in: $OutDir" -ForegroundColor Green
@@ -0,0 +1,235 @@
<#
.SYNOPSIS
Read-only analysis of SMB shares as LLM knowledge base (projektplan §2.1).
.DESCRIPTION
Walks one or more share paths and reports, per share:
- volume and file count per file type / category (extractable vs. not)
- size and count per top-level folder (for the include/exclude decision)
- modification-time distribution (sync frequency, data age)
- scanned-PDF ratio via sampling (PDFs without a text layer -> OCR need)
- duplicate/old-version candidates by name patterns
- long paths (> 240 chars) and non-ASCII names (umlauts etc.)
Writes CSVs plus a summary.txt into the output directory. Never writes to
the shares themselves. PowerShell 5.1 compatible; no external modules.
.EXAMPLE
.\share-analysis.ps1 -Paths '\\fileserver\projekte','\\fileserver\doku'
.EXAMPLE
.\share-analysis.ps1 -Paths 'D:\shares\projekte' -OutDir C:\temp\analysis -PdfSampleSize 300
#>
param(
[Parameter(Mandatory = $true)]
[string[]]$Paths,
[string]$OutDir = (Join-Path (Get-Location) ("share-analysis-" + (Get-Date -Format "yyyyMMdd-HHmmss"))),
# PDFs sampled per share for the text-layer check
[int]$PdfSampleSize = 200,
# How many folder levels to aggregate in the *-toplevel-folders.csv.
# 1 = D:\Abteilungen, 2 = D:\Abteilungen\AA (matches the share layout here).
[int]$FolderDepth = 1
)
$ErrorActionPreference = 'Continue'
New-Item -ItemType Directory -Path $OutDir -Force | Out-Null
# --- classification ---------------------------------------------------------
$categories = @{
'office' = @('.pdf', '.doc', '.docx', '.xls', '.xlsx', '.xlsm', '.ppt', '.pptx', '.txt', '.md', '.rtf', '.odt', '.ods', '.odp', '.csv', '.vsd', '.vsdx')
'email' = @('.msg', '.eml')
'image' = @('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tif', '.tiff', '.svg', '.heic', '.webp')
'cad' = @('.dwg', '.dxf', '.step', '.stp', '.iges', '.igs', '.sldprt', '.sldasm', '.slddrw', '.ipt', '.iam', '.catpart', '.catproduct', '.3ds', '.stl')
'archive' = @('.zip', '.rar', '.7z', '.tar', '.gz', '.bz2', '.iso')
'media' = @('.mp4', '.avi', '.mov', '.wmv', '.mp3', '.wav')
}
$extToCategory = @{}
foreach ($cat in $categories.Keys) {
foreach ($ext in $categories[$cat]) { $extToCategory[$ext] = $cat }
}
# name patterns that suggest duplicates / old versions
$dupePattern = '(?i)(kopie|copy|backup|_old|_alt\b|\.bak$|~\$|\(\d+\)\s*(\.[^.]+)?$)'
$summaryLines = New-Object System.Collections.Generic.List[string]
$summaryLines.Add("Share analysis $(Get-Date -Format 'yyyy-MM-dd HH:mm') — read-only")
$summaryLines.Add("Paths: $($Paths -join ', ')")
$summaryLines.Add("")
# --- helpers -----------------------------------------------------------------
function Test-PdfTextLayer {
# Heuristic: a PDF without any /Font reference in its first 4 MB most
# likely has no text layer (scanned). Also detects encrypted PDFs.
param([string]$Path)
try {
$fs = [System.IO.File]::Open($Path, 'Open', 'Read', 'ReadWrite')
try {
$len = [int][Math]::Min($fs.Length, 4MB)
$buf = New-Object byte[] $len
[void]$fs.Read($buf, 0, $len)
} finally { $fs.Close() }
$text = [System.Text.Encoding]::ASCII.GetString($buf)
if ($text -match '/Encrypt') { return 'encrypted' }
if ($text -match '/Font') { return 'text' }
return 'no-text-layer'
} catch {
return 'unreadable'
}
}
# --- per-share pass ----------------------------------------------------------
foreach ($root in $Paths) {
# Label used for the output filenames. Must not contain characters that are
# illegal in a filename — e.g. -Paths "D:" would otherwise produce
# "D:-file-types.csv" and Export-Csv fails with "path format not supported".
$shareName = ($root.TrimEnd('\') -split '[\\/]')[-1]
if (-not $shareName) { $shareName = 'root' }
foreach ($c in [System.IO.Path]::GetInvalidFileNameChars()) {
$shareName = $shareName.Replace($c, '_')
}
$shareName = $shareName.TrimEnd('_', '.', ' ')
if (-not $shareName) { $shareName = 'root' }
Write-Host "=== Analyzing '$root' ..." -ForegroundColor Cyan
if (-not (Test-Path -LiteralPath $root)) {
Write-Warning "Path not found or no access: $root"
$summaryLines.Add("[$shareName] SKIPPED — path not found or no access: $root")
continue
}
# aggregates (streaming — file objects are not kept in memory)
$extStats = @{} # ext -> @{Count; Bytes}
$topStats = @{} # top-level folder -> @{Count; Bytes}
$yearStats = @{} # mtime year -> count
$totalCount = 0L
$totalBytes = 0L
$dupeCount = 0L
$longPaths = 0L
$nonAscii = 0L
$now = Get-Date
$recency = @{ 'last 30 days' = 0L; 'last 90 days' = 0L; 'last 365 days' = 0L; 'older' = 0L }
# reservoir sample of PDF paths for the text-layer check
$pdfSample = New-Object System.Collections.Generic.List[string]
$pdfSeen = 0L
$rand = New-Object System.Random
$rootLen = $root.TrimEnd('\').Length
Get-ChildItem -LiteralPath $root -Recurse -File -Force -ErrorAction SilentlyContinue -ErrorVariable +enumErrors |
ForEach-Object {
$totalCount++
$totalBytes += $_.Length
$ext = $_.Extension.ToLowerInvariant()
if (-not $ext) { $ext = '(none)' }
if (-not $extStats.ContainsKey($ext)) { $extStats[$ext] = @{ Count = 0L; Bytes = 0L } }
$extStats[$ext].Count++
$extStats[$ext].Bytes += $_.Length
# folder relative to the share root, aggregated at -FolderDepth levels
$rel = $_.FullName.Substring($rootLen).TrimStart('\')
$parts = $rel.Split('\')
if ($parts.Count -le 1) {
$top = '(root)'
} else {
$n = [Math]::Min($FolderDepth, $parts.Count - 1)
$top = ($parts[0..($n - 1)] -join '\')
}
if (-not $topStats.ContainsKey($top)) { $topStats[$top] = @{ Count = 0L; Bytes = 0L } }
$topStats[$top].Count++
$topStats[$top].Bytes += $_.Length
$year = $_.LastWriteTime.Year
if (-not $yearStats.ContainsKey($year)) { $yearStats[$year] = 0L }
$yearStats[$year]++
$age = ($now - $_.LastWriteTime).TotalDays
if ($age -le 30) { $recency['last 30 days']++ }
elseif ($age -le 90) { $recency['last 90 days']++ }
elseif ($age -le 365) { $recency['last 365 days']++ }
else { $recency['older']++ }
if ($_.Name -match $dupePattern) { $dupeCount++ }
if ($_.FullName.Length -gt 240) { $longPaths++ }
if ($_.Name -match '[^\x00-\x7F]') { $nonAscii++ }
if ($ext -eq '.pdf') {
$pdfSeen++
if ($pdfSample.Count -lt $PdfSampleSize) {
$pdfSample.Add($_.FullName)
} else {
$i = $rand.Next(0, [int][Math]::Min($pdfSeen, [int]::MaxValue))
if ($i -lt $PdfSampleSize) { $pdfSample[$i] = $_.FullName }
}
}
if ($totalCount % 20000 -eq 0) {
Write-Host (" {0:N0} files, {1:N1} GB ..." -f $totalCount, ($totalBytes / 1GB))
}
}
# PDF text-layer sampling
Write-Host " Sampling $($pdfSample.Count) of $pdfSeen PDFs for text layer ..."
$pdfResults = @{ 'text' = 0; 'no-text-layer' = 0; 'encrypted' = 0; 'unreadable' = 0 }
foreach ($p in $pdfSample) { $pdfResults[(Test-PdfTextLayer $p)]++ }
# --- write per-share CSVs ---
$prefix = Join-Path $OutDir $shareName
$extStats.GetEnumerator() | ForEach-Object {
$cat = if ($extToCategory.ContainsKey($_.Key)) { $extToCategory[$_.Key] } else { 'other' }
[PSCustomObject]@{ Extension = $_.Key; Category = $cat; Count = $_.Value.Count; GB = [Math]::Round($_.Value.Bytes / 1GB, 2) }
} | Sort-Object GB -Descending | Export-Csv "$prefix-file-types.csv" -NoTypeInformation -Encoding UTF8
$topStats.GetEnumerator() | ForEach-Object {
[PSCustomObject]@{ Folder = $_.Key; Count = $_.Value.Count; GB = [Math]::Round($_.Value.Bytes / 1GB, 2) }
} | Sort-Object GB -Descending | Export-Csv "$prefix-toplevel-folders.csv" -NoTypeInformation -Encoding UTF8
$yearStats.GetEnumerator() | ForEach-Object {
[PSCustomObject]@{ Year = $_.Key; Count = $_.Value }
} | Sort-Object Year | Export-Csv "$prefix-mtime-years.csv" -NoTypeInformation -Encoding UTF8
# --- category rollup for the summary ---
$catBytes = @{}; $catCount = @{}
foreach ($e in $extStats.GetEnumerator()) {
$cat = if ($extToCategory.ContainsKey($e.Key)) { $extToCategory[$e.Key] } else { 'other' }
if (-not $catBytes.ContainsKey($cat)) { $catBytes[$cat] = 0L; $catCount[$cat] = 0L }
$catBytes[$cat] += $e.Value.Bytes
$catCount[$cat] += $e.Value.Count
}
$summaryLines.Add("[$shareName] $root")
$summaryLines.Add((" Total: {0:N0} files, {1:N1} GB" -f $totalCount, ($totalBytes / 1GB)))
foreach ($c in ($catBytes.Keys | Sort-Object { $catBytes[$_] } -Descending)) {
$summaryLines.Add((" {0,-8} {1,10:N0} files {2,10:N1} GB" -f $c, $catCount[$c], ($catBytes[$c] / 1GB)))
}
if ($pdfSample.Count -gt 0) {
$scannedPct = [Math]::Round(100 * $pdfResults['no-text-layer'] / $pdfSample.Count, 1)
$summaryLines.Add((" PDFs: {0:N0} total; sample of {1}: {2} with text, {3} WITHOUT text layer (~{4}% -> OCR), {5} encrypted, {6} unreadable" -f `
$pdfSeen, $pdfSample.Count, $pdfResults['text'], $pdfResults['no-text-layer'], $scannedPct, $pdfResults['encrypted'], $pdfResults['unreadable']))
}
$summaryLines.Add((" Duplicate/old-version name patterns: {0:N0} files" -f $dupeCount))
$summaryLines.Add((" Paths > 240 chars: {0:N0}; non-ASCII names: {1:N0}" -f $longPaths, $nonAscii))
$summaryLines.Add(" Modified: " + (($recency.GetEnumerator() | Sort-Object { @('last 30 days','last 90 days','last 365 days','older').IndexOf($_.Key) } |
ForEach-Object { "$($_.Key): $("{0:N0}" -f $_.Value)" }) -join ' | '))
$summaryLines.Add("")
}
if ($enumErrors) {
$summaryLines.Add(("NOTE: {0:N0} paths could not be read (access denied / path too long). Counts are lower bounds." -f $enumErrors.Count))
$enumErrors | ForEach-Object { $_.TargetObject } | Select-Object -First 50 |
Set-Content (Join-Path $OutDir "enumeration-errors-sample.txt") -Encoding UTF8
}
$summaryPath = Join-Path $OutDir "summary.txt"
$summaryLines | Set-Content $summaryPath -Encoding UTF8
Write-Host ""
Write-Host "Done. Results in: $OutDir" -ForegroundColor Green
Get-Content $summaryPath | Write-Host