5.8 KiB
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:
- ufw lässt Port 8800 nur aus dem LAN und über
wt0zu. - 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
- opencode (Terminal-Agent): docs/opencode.md
- Continue (VS-Code-Plugin): docs/continue.md
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.yamlwird 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
-
Den Ordner kopieren.
models/kann man mitnehmen, um den Download zu sparen. -
.envanpassen:ALLOWED_NETS(LAN-Subnetz, NetBird-Netz),RENDER_GID/VIDEO_GID,HOST_UID/HOST_GID. -
Je nach GPU das Image in
.envwählen (LLAMA_SWAP_TAG):GPU Tag-Variante AMD (ROCm) vXXX-rocm-bYYYY(wie hier)NVIDIA vXXX-cuda-bYYYY. Dann indocker-compose.ymldevices/group_add/security_optdurchdeploy.resources.reservations.devices(nvidia) ersetzen.herstellerunabhängig vXXX-vulkan-bYYYY(nur/dev/drinötig)nur CPU vXXX-cpu-bYYYYVerfügbare Tags: https://github.com/mostlygeek/llama-swap/pkgs/container/llama-swap
-
Threads (
--threadsim Makroserver) an die CPU anpassen. -
Firewall des Ziel-PCs entsprechend
scripts/firewall-ufw.shkonfigurieren.
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 |