LocalAI/README.md
2026-10-15 00:01:26 +02:00

5.8 KiB
Raw Blame History

LocalAI – zentraler LLM-Server für opencode & Continue

Ein Docker-Compose-Stack, der auf alegab-pc (RX 6950 XT 16 GB, Ryzen 9 7900X3D, 62 GB RAM) eine OpenAI-kompatible API bereitstellt. Clients im Heim-LAN und über NetBird nutzen sie zum Programmieren, ganz ohne Cloud-KI.

Client-PC (opencode / VS Code + Continue) ──LAN / NetBird──┐
                                                           ▼
   caddy  (Host-Netz, :8800)   IP-Filter + API-Key
      └─► llama-swap  (nur 127.0.0.1:8080)   lädt/wechselt Modelle
             └─► llama-server (llama.cpp, ROCm) ─► GPU + RAM
                   Modelle: ./models (GGUF, Download von Hugging Face beim ersten Aufruf)
Modell-ID Zweck Verhalten
qwen3.5-35b-a3b Hauptmodell Chat/Agent (MoE) eines der Chat-Modelle gleichzeitig geladen
glm-4.7-flash Alternative Chat/Agent (MoE) 〃
qwen3.5-9b schnelles kleines Chat-Modell 〃
qwen2.5-coder-1.5b Autocomplete (FIM) für Continue dauerhaft geladen (GPU)
nomic-embed Embeddings für Continue @codebase dauerhaft geladen (CPU)

Server einrichten

cd ~/workspace/localAI
cp .env.example .env
sed -i "s/^LLM_API_KEY=.*/LLM_API_KEY=$(openssl rand -hex 32)/" .env
chmod 600 .env
$EDITOR .env            # ALLOWED_NETS, GIDs (getent group render video), UID/GID prüfen
sudo ./scripts/firewall-ufw.sh   # einmalig: Port 8800 für LAN + wt0 in ufw öffnen
./scripts/start.sh               # prüfen, starten, Modelle vorladen, Verbindungsdaten zeigen

scripts/start.sh legt eine fehlende .env beim ersten Start auch selbst an: mit neuem API-Key, UID/GID und den GIDs von render/video. Weitere Optionen:

Aufruf Wirkung
./scripts/start.sh starten, Autocomplete + Embeddings + Hauptmodell vorladen
./scripts/start.sh --no-preload nur starten
./scripts/start.sh --model glm-4.7-flash anderes Chat-Modell vorladen
./scripts/start.sh --show-key API-Key im Klartext anzeigen (für Client-Setup)
./scripts/start.sh --pull Images vorher aktualisieren

Nach einem Reboot startet der Stack von selbst: Der Docker-Dienst ist aktiviert, und die Container haben restart: unless-stopped. Autocomplete und Embeddings lädt llama-swap per hooks.on_startup.preload ebenfalls von selbst, das Chat-Modell kommt beim ersten Request dazu.

Der erste Aufruf eines Modells lädt es von Hugging Face herunter (Hauptmodell ~22 GB). Danach läuft alles offline.

Warum Caddy im Host-Netz?

Ports, die Docker veröffentlicht, umgehen ufw. Deshalb ist llama-swap nur an 127.0.0.1 gebunden. Caddy läuft mit network_mode: host, dort greift ufw, und Caddy sieht die echte Quell-IP. Daraus ergeben sich zwei Schutzschichten:

  1. ufw lässt Port 8800 nur aus dem LAN und über wt0 zu.
  2. Caddy prüft zusätzlich die Quell-IP (ALLOWED_NETS) und den Bearer-Token (LLM_API_KEY).

Die API läuft über plain HTTP. Über NetBird ist das unkritisch (WireGuard verschlüsselt den Tunnel), im Heim-LAN ist es akzeptabel.

Clients einrichten

Schnelltest von jedem Client:

curl -H "Authorization: Bearer <KEY>" http://<server>:8800/v1/models

Betrieb

docker compose ps                     # Status
docker compose logs -f llama-swap     # Modell-Laden, Requests
docker compose logs -f caddy          # Zugriffe
docker compose restart                # nach Änderungen an .env / Caddyfile
  • llama-swap/config.yaml wird automatisch neu geladen, ein Neustart ist nicht nötig.
  • Web-Oberfläche von llama-swap (Modellstatus, Logs): http://127.0.0.1:8080/ui, nur lokal auf dem Server erreichbar.
  • VRAM-Auslastung: rocm-smi --showmeminfo vram

Modell hinzufügen

Einen Eintrag in llama-swap/config.yaml anlegen, z. B.

  mein-modell:
    cmd: ${server} ${agent-ctx} -hf <hf-user>/<repo>-GGUF:Q4_K_M
    ttl: 3600

Den Eintrag unter routing.router.settings.groups.chat.members aufnehmen. Danach die Client-Configs (client/opencode.json, client/continue/config.yaml) um die neue Modell-ID ergänzen.

GPU-Layer und MoE-Auslagerung in den RAM verteilt llama.cpp automatisch (--fit, Standard: an). --fit-target 3072 hält ~3 GB VRAM für das Autocomplete-Modell frei.

Auf einen anderen PC übertragen

  1. Den Ordner kopieren. models/ kann man mitnehmen, um den Download zu sparen.

  2. .env anpassen: ALLOWED_NETS (LAN-Subnetz, NetBird-Netz), RENDER_GID/VIDEO_GID, HOST_UID/HOST_GID.

  3. Je nach GPU das Image in .env wählen (LLAMA_SWAP_TAG):

    GPU Tag-Variante
    AMD (ROCm) vXXX-rocm-bYYYY (wie hier)
    NVIDIA vXXX-cuda-bYYYY. Dann in docker-compose.yml devices/group_add/security_opt durch deploy.resources.reservations.devices (nvidia) ersetzen.
    herstellerunabhängig vXXX-vulkan-bYYYY (nur /dev/dri nötig)
    nur CPU vXXX-cpu-bYYYY

    Verfügbare Tags: https://github.com/mostlygeek/llama-swap/pkgs/container/llama-swap

  4. Threads (--threads im Makro server) an die CPU anpassen.

  5. Firewall des Ziel-PCs entsprechend scripts/firewall-ufw.sh konfigurieren.

Dateien

Datei Inhalt
docker-compose.yml Dienste llama-swap + caddy
.env / .env.example Secrets, Netze, Ports, GIDs, Image-Tags
llama-swap/config.yaml Modelle, Startparameter, Gruppen
caddy/Caddyfile IP-Filter, API-Key, Reverse Proxy
client/opencode.json Vorlage für opencode-Clients
client/continue/ Vorlage für Continue-Clients
scripts/start.sh Start mit Prüfungen, Preload und Statusausgabe
scripts/firewall-ufw.sh ufw-Freigaben
PLAN.md ursprünglicher Umsetzungsplan