Stack LLM marty (edge GPU)
Ce dossier déploie la stack GPU sur marty (WRX90, RTX PRO 5000 Blackwell, 48 Go VRAM) :
Internet → Traefik edge marty (:443)
├─ marty.nassim.netlib.re → Hermes WebUI :3000
├─ hermes.nassim.netlib.re → Hermes dashboard :9119 (+ /v1 → gateway :9000)
├─ app.nassim.netlib.re → Hermes Workspace :3002
└─ *.nassim.netlib.re → home-server via tunnel SSH (:18080)
Docker (réseau interne)
vLLM :8000 ← alias stable `marty-llm` + id réel du modèle actif
Hermes WebUI, Hermes Agent, Hermes Dashboard, Hermes Workspace
Contrainte VRAM : un seul gros modèle chargé à la fois (48 Go). La bascule se fait via le registre et le script de switch.
Accès dev sécurisé (poste local → marty)
vLLM écoute uniquement sur 127.0.0.1:8000 du serveur marty (tunnel SSH, pas d'exposition Internet directe).
| Port local (poste dev) | Cible marty | Usage |
|---|---|---|
:19000 | :9000 | Hermes gateway (profils + tools) |
:18000 | :8000 | vLLM OpenAI v1 (alias marty-llm) |
systemctl --user enable --now marty-llm-tunnel.service
source ~/.config/opencode/marty-remote.env
opencode -m marty-vllm/marty-llm
Clé API : VLLM_API_KEY dans .env de la stack. Voir marty remote.env.example.
Bascule de modèles
Registre : Configuration (models registry) — dépôts HF, args vLLM, tailles.
| ID | Modèle | VRAM ~ | Notes |
|---|---|---|---|
hermes-3 | Hermes-3-Llama-3.1-8B | 16 Go | Défaut, tool-calling |
qwen2.5 | Qwen2.5-32B-Instruct AWQ | 19 Go | Polyvalent |
qwen2.5-72b | Qwen2.5-72B-Instruct AWQ | 42 Go | ctx 8k, serré |
qwen3.6-35b | Qwen3.5-35B-A3B MoE GPTQ-Int4 | 25 Go | Raisonnement + tools (MoE 3B actifs/token) |
qwq | QwQ-32B AWQ | 19 Go | Raisonnement (remplace Kimi K2.5) |
gemma3 | Gemma-3-27B-it W4A16 | 18 Go | vLLM récent requis |
deepseek-r1 | DeepSeek-R1-Distill-Llama-70B AWQ | 40 Go | ctx 8k, serré ; Hermes compression override requis |
llama3.3 | Llama-3.3-70B-Instruct AWQ | 40 Go | ctx 8k, serré ; Hermes compression override requis |
Script (depuis ce poste, via ssh marty-public) :
Stockage local : poids sous <local dir> sur marty. Après pull/pull-all, switch ne recontacte pas HuggingFace.
Accès LAN (LLM_BIND_ADDR=0.0.0.0, UFW actif) :
- Hermes WebUI :
http://192.168.1.113:3000 - Hermes dashboard :
http://192.168.1.113:9119 - Hermes gateway :
http://192.168.1.113:9000 - Hermes Workspace :
http://192.168.1.113:3002 - Public (Traefik) :
https://marty.nassim.netlib.re,https://hermes.nassim.netlib.re,https://app.nassim.netlib.re
Hermes et les 34 profils `marty-*` pointent sur l'alias stable `marty-llm` — pas de reconfig Hermes à chaque bascule.
Déploiement complet Hermes + stack :
```bash
Importe automatiquement les profils marty-* depuis marty agents import.manifest. Mot de passe UI : HERMES_WORKSPACE_PASSWORD dans .env sur marty.
Déploiement serveur LLM distant derrière Traefik
Ce dossier prépare aussi la stack GPU d'un serveur LLM distant (historique home-server → marty) :
Traefik home-server (nassim.netlib.re)
-> IP privée du nouveau serveur
-> Hermes WebUI :3000
-> Hermes Agent :9000
-> vLLM reste interne au réseau Docker
Traefik reste sur home-server. Le nouveau serveur exécute uniquement Docker, vLLM, Hermes WebUI et Hermes Agent.
1. Préparer le nouveau serveur
Installer les prérequis NVIDIA/Docker sur le nouveau serveur :
sudo apt update
sudo apt install -y docker.io docker-compose-plugin nvidia-container-toolkit
sudo systemctl enable --now docker
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu22.04 nvidia-smi
Déployer ce dossier :
mkdir -p ~/deploy
ssh nouveau-serveur
cd ~/deploy/remote-llm-stack
cp .env.example .env
chmod 600 .env
Dans .env, définir au minimum :
LLM_BIND_ADDR: IP privée du nouveau serveur joignable depuishome-server.VLLM_API_KEY: secret long partagé entre Hermes WebUI/Hermes et vLLM.HERMES_WEBUI_PASSWORD: mot de passe UI Hermes WebUI (https://marty.nassim.netlib.re).
Créer les répertoires persistants :
mkdir -p models hermes cache/huggingface
Le modèle attendu par défaut est hermes 3, monté en lecture seule dans vLLM sous hermes 3.
2. Reprendre la configuration locale
Pour reprendre la configuration Hermes locale sans recopier de secrets dans Git :
rsync -a --delete ~/.hermes/ nouveau-serveur:~/deploy/remote-llm-stack/hermes/
ssh nouveau-serveur 'chmod -R go-rwx ~/deploy/remote-llm-stack/hermes'
Si tu préfères repartir proprement, initialise .env depuis le template local :
chmod 600 ~/deploy/remote-llm-stack/hermes/.env
Reporter ensuite les secrets Marty nécessaires : email, calendrier, Baserow, GitLab, Firecrawl, etc.
3. Lancer la stack
Sur le nouveau serveur :
cd ~/deploy/remote-llm-stack
docker compose pull
docker compose up -d
docker compose ps
Tests locaux depuis le nouveau serveur :
curl -fsS http://127.0.0.1:3000/health
curl -fsS http://127.0.0.1:9000/
docker compose exec vllm python3 -c "import urllib.request; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).read()"
docker compose exec hermes-webui python3 -c "import os, urllib.request; req=urllib.request.Request('http://vllm:8000/v1/models', headers={'Authorization':'Bearer '+os.environ['OPENAI_API_KEY']}); print(urllib.request.urlopen(req, timeout=5).status)"
Tests depuis home-server avec l'IP privée configurée dans .env :
curl -fsS http://IP_PRIVEE_NOUVEAU_SERVEUR:3000/
curl -fsS http://IP_PRIVEE_NOUVEAU_SERVEUR:9000/
4. Brancher Traefik sur home-server
Validation SSH du 2026-05-20 : sur home-server, Traefik tourne en conteneur Docker et expose 80/443. Le conteneur monte un fichier dynamique Configuration (dynamic) plutôt qu'un répertoire dynamique visible.
Ne pas remplacer le fichier dynamique existant sans sauvegarde. Fusionner le contenu de Configuration (dynamic llm stack.example) dans le fichier dynamique existant, ou adapter le montage Traefik pour charger un répertoire dynamique dédié.
Exemple si ton Traefik utilise un répertoire dynamic/file-provider :
sudo cp traefik/dynamic-llm-stack.example.yml /etc/traefik/dynamic/llm-stack.yml
sudo chmod 600 /etc/traefik/dynamic/llm-stack.yml
Exemple si ton Traefik utilise le fichier unique observé Configuration (dynamic) :
sudo cp /etc/traefik/dynamic.yml /etc/traefik/dynamic.yml.bak.$(date +%Y%m%d%H%M%S)
sudoedit /etc/traefik/dynamic.yml
Adapter dans la configuration dynamique :
- Les domaines
marty.netlib.reethermes.nassim.netlib.re. - Les URLs backend
http://10.0.0.42:3000et:9000. - Le hash
htpasswddu middlewarellm-basic-auth. - Le
certResolversi ton Traefik n'utilise pasletsencrypt.
Générer un hash Basic Auth :
htpasswd -nbB nassim 'mot-de-passe-long'
5. Sécurité réseau
La sécurité repose sur plusieurs couches :
- vLLM n'est pas publié sur l'hôte et reste joignable uniquement depuis le réseau Docker interne.
- Hermes WebUI utilise
HERMES_WEBUI_PASSWORDpour l'authentification (pas de Basic Auth Traefik surmarty.nassim.netlib.re). - Hermes dashboard / API (
hermes.nassim.netlib.re) : pas de Basic Auth Traefik — auth Hermes (session dashboard, Bearer v1). - Traefik applique Basic Auth uniquement sur
traefik.nassim.netlib.re(dashboard Traefik). - Les ports Docker doivent être bindés sur une IP privée via
LLM_BIND_ADDR. - Le firewall du nouveau serveur doit n'accepter
3000et9000que depuishome-server.
Après activation Traefik, valider que les routes non authentifiées ne passent pas :
curl -I https://marty.nassim.netlib.re # 302 → login WebUI (HERMES_WEBUI_PASSWORD)
curl -I https://hermes.nassim.netlib.re # 200 — pas de Basic Auth Traefik
curl -s https://hermes.nassim.netlib.re/health # 200
Exemple UFW sur le nouveau serveur :
sudo ufw default deny incoming
sudo ufw allow OpenSSH
sudo ufw allow from IP_PRIVEE_HOME_SERVER to any port 3000 proto tcp
sudo ufw allow from IP_PRIVEE_HOME_SERVER to any port 9000 proto tcp
sudo ufw enable
6. URLs prévues
https://marty.nassim.netlib.re-> Hermes WebUIhttps://hermes.nassim.netlib.re-> Hermes Agent
L'API vLLM n'est pas exposée par Traefik. Hermes WebUI et Hermes Agent l'utilisent via http://vllm:8000/v1 sur le réseau Docker interne.