1. Pourquoi self-héberger son inférence
Trois raisons reviennent dans chaque ticket que nous recevons sur la ligne GPU. La première est économique : une API au token facture chaque requête, y compris les prompts système répétés mille fois par jour par vos agents. Au-delà d'un certain volume, un GPU dédié à prix fixe coûte moins cher que la facture variable — nous chiffrons le point de bascule dans la FAQ. La deuxième est la confidentialité : un prompt envoyé à une API externe est un prompt journalisé quelque part, soumis à une politique de rétention que vous ne contrôlez pas. Sur votre machine, le prompt naît et meurt dans votre RAM.
La troisième est l'indépendance : pas de rate limit au milieu d'un batch, pas de dépréciation de modèle qui casse vos prompts du jour au lendemain, pas de compte suspendu par un algorithme de modération. Vous épinglez une version de modèle, vous la gardez tant qu'elle vous convient. En contrepartie — et c'est le deal du self-hosting — vous assumez la disponibilité, les mises à jour et le dimensionnement. C'est exactement le sujet de ce guide.
Note d'honnêteté : self-héberger ne rend pas un modèle magiquement meilleur. Un 8B local ne rivalisera pas avec les plus grands modèles propriétaires sur le raisonnement complexe. Le bon réflexe est de self-héberger les volumes (classification, extraction, résumé, agents outillés) et de garder une API externe pour les 5 % de requêtes vraiment difficiles.
2. Choisir son moteur d'inférence
Trois moteurs dominent l'inférence self-hosted en 2026, et ils ne répondent pas au même besoin. Le mauvais réflexe est d'en choisir un « pour toujours » ; le bon est de les faire cohabiter selon la charge.
- vLLM — le serveur de production. Batching continu, parallélisme tensoriel multi-GPU, API compatible OpenAI, quantization AWQ/GPTQ/FP8. C'est le moteur qui transforme un GPU en service multi-utilisateurs : trente requêtes simultanées partagent la même carte sans s'effondrer. Plus lourd à installer (Docker recommandé), gourmand en VRAM pour ses propres structures, mais imbattable en débit agrégé.
- Ollama — le chemin le plus court. Une commande pour installer, une pour télécharger un modèle, une API locale dans la minute qui suit. Parfait pour prototyper, pour un usage solo, ou comme backend local d'outils (continue.dev, Open WebUI). Sous le capot, c'est llama.cpp : les performances mono-flux sont bonnes, le multi-utilisateurs n'est pas son terrain.
- llama.cpp — le chirurgien. Le serveur
llama-serverexpose une API compatible OpenAI, lit les GGUF dans tous leurs niveaux de quantization, et permet l'offload partiel CPU+GPU quand un modèle dépasse la VRAM. C'est aussi le seul choix sérieux pour les formats exotiques (EXL2 via ses dérivés) ou pour presser le dernier token/seconde d'une carte.
Notre recommandation par défaut : Ollama pour valider votre cas d'usage le premier jour, vLLM dès que plus d'un utilisateur ou d'un agent tape sur le modèle, llama.cpp quand vous devez loger un modèle précis dans une VRAM précise. Les trois s'installent côte à côte sur nos images Ubuntu 24.04 + CUDA 12 sans conflit.
3. Dimensionner la VRAM : le calcul qui évite les mauvaises surprises
La règle de base tient en une ligne : la VRAM nécessaire ≈ nombre de paramètres × taille de chaque poids. En FP16/BF16, chaque paramètre pèse 2 octets — un modèle 8B occupe donc ~16 Go avant même le premier token. La quantization réduit ce poids : ~1 octet par paramètre en Q8, ~0,55–0,7 octet en 4 bits selon le format. À cela s'ajoutent le KV cache (la mémoire du contexte, qui grandit avec la longueur des conversations) et la marge du moteur lui-même : prévoyez 15 à 20 % de VRAM de tête, toujours.
| Taille de modèle | FP16 / BF16 | Q8 | 4 bits (Q4_K_M / AWQ) | Plan VPSPLEX conseillé |
|---|---|---|---|---|
| 7–8 B (Llama 3.1 8B, Mistral 7B) | ~16 Go | ~9 Go | ~5 Go | GPU-1 (24 Go), toutes quantizations |
| 13–14 B (Qwen 2.5 14B) | ~28 Go | ~15 Go | ~9 Go | GPU-1 en Q8/Q4 |
| 30–34 B (Qwen 2.5 32B, Yi 34B) | ~64 Go | ~34 Go | ~19 Go | GPU-1 en Q4, GPU-2 en Q8 |
| 70–72 B (Llama 3.3 70B, Qwen 72B) | ~140 Go | ~75 Go | ~40 Go | GPU-2 (48 Go) en Q4, GPU-3 (H100 80 Go) en Q8 |
| 100 B+ (DeepSeek-V3, grands MoE) | — | — | ~60 Go et plus | GPU-3, devis multi-GPU au-delà |
Poids du modèle seul, arrondis au Go supérieur. Ajoutez le KV cache : ~1 Go pour 8 000 tokens de contexte sur un 8B en FP16, ~5 Go sur un 70B — les architectures GQA/MQA des modèles récents divisent nettement cette facture.
L'erreur classique consiste à regarder uniquement la ligne « 4 bits » et à oublier le contexte : un 32B Q4 qui loge à l'aise dans 24 Go à 4 000 tokens de contexte déborde dès que vous montez à 32 000. Fixez --max-model-len (vLLM) ou --ctx-size (llama.cpp) à votre besoin réel, pas au maximum théorique du modèle — chaque millier de tokens de contexte inutilisé est de la VRAM rendue au batch.
4. Les formats quantifiés, sans le jargon inutile
La quantization compresse les poids du modèle en réduisant leur précision. La perte de qualité est mesurable mais modeste aux niveaux raisonnables : un Q8 est indiscernable du FP16 à l'usage, un Q4 bien fait (Q4_K_M, AWQ) coûte typiquement 1 à 2 points sur les benchmarks courants, et c'est le compromis que choisit presque tout le monde en production. En dessous de Q3, la dégradation devient visible dans les réponses longues — à réserver aux dépannages, pas à la production.
| Format | Moteur principal | Taille par milliard de paramètres | Quand le choisir |
|---|---|---|---|
| GGUF Q4_K_M / Q5_K_M / Q6_K / Q8_0 | llama.cpp, Ollama | 0,55 à 1,05 Go | Le standard universel — un seul fichier, offload CPU possible |
| AWQ (4 bits) | vLLM, TGI | ~0,6 Go | Meilleur débit en batch sur vLLM, kernels très optimisés |
| GPTQ (4 bits) | vLLM, TGI | ~0,6 Go | Équivalent AWQ, parfois le seul format dispo pour un modèle donné |
| FP8 (W8A8) | vLLM | ~1 Go | H100 : qualité quasi FP16 à moitié de VRAM, le réflexe sur GPU-3 |
| EXL2 (2–8 bits) | TabbyAPI / exllamav2 | variable au dizième de bit | Ajuster la taille au dixième de Go près sur une carte pleine |
Réflexe pratique : sur Hugging Face, cherchez d'abord le GGUF « Q4_K_M » (bartowski en maintient pour presque tous les modèles notables) ou l'AWQ officiel du lab. Évitez les repacks anonymes sans hash ni provenance — un fichier de poids est un fichier que vous exécutez, et la chaîne d'approvisionnement des modèles est devenue un vrai vecteur d'attaque en 2025.
5. Correspondance avec les plans GPU VPSPLEX
Concrètement, voici comment nos trois configurations couvrent le spectre. La GPU-1 (RTX 4090, 24 Go, 119 $/mois) est la machine de l'inférence quotidienne : 8B en Q8 avec un long contexte, 14B en Q6, 32B en Q4 — soit, en 2026, des modèles qui traitent correctement la synthèse, l'extraction, la classification et la plupart des tâches d'agents. La GPU-2 (2× RTX 4090, 48 Go, 229 $/mois) est le seuil des 70B en Q4 et du 32B en Q8 confortable, avec le parallélisme tensoriel de vLLM (--tensor-parallel-size 2) pour répartir le modèle sur les deux cartes.
La GPU-3 (H100 SXM5, 80 Go HBM3, sur devis) change de catégorie : 70B en FP8 à pleine vitesse, contextes très longs, fine-tuning complet, ou plusieurs modèles moyens servis simultanément sur la même carte. Sa bande passante mémoire (3,35 To/s contre 1 To/s pour une 4090) se sent exactement là où l'inférence est limitée : la génération token par token.
Nos GPU sont dédiés — la carte que vous louez est la carte que vous avez, sans voisin qui réclame ses cycles au milieu de votre batch. C'est aussi ce qui rend les chiffres de la section suivante reproductibles chez vous à ±5 %.
6. Déploiement : du provisionnement au premier token
Chaque GPU est livré avec Ubuntu 24.04, CUDA 12, les pilotes NVIDIA et le NVIDIA Container Toolkit. Vérifiez d'abord que la carte répond :
nvidia-smi
# Doit afficher : RTX 4090, 24564 MiB, pilote 560.x, CUDA 12.x
docker run --rm --gpus all nvidia/cuda:12.6.0-base-ubuntu24.04 nvidia-smi
# Confirme que Docker voit le GPU via le Container Toolkit
Voie production — vLLM en Docker
docker run -d --gpus all --name vllm \
-p 8000:8000 \
-v /data/hf:/root/.cache/huggingface \
vllm/vllm-openai:latest \
--model Qwen/Qwen2.5-32B-Instruct-AWQ \
--max-model-len 8192 \
--gpu-memory-utilization 0.92
# Suivi du chargement des poids (quelques minutes au premier run)
docker logs -f vllm
vLLM expose une API compatible OpenAI sur le port 8000. Testez-la comme vous testeriez n'importe quel endpoint :
curl -s http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "Qwen/Qwen2.5-32B-Instruct-AWQ",
"messages": [{"role": "user", "content": "Explique le KV cache en trois phrases."}],
"max_tokens": 200
}'
Le point de migration est là : n'importe quel code écrit pour l'API OpenAI fonctionne en changeant deux lignes — base_url vers votre serveur, api_key vers une valeur quelconque. Vos agents, vos scripts, vos outils tiers basculent sans réécriture.
Voie express — Ollama
curl -fsSL https://ollama.com/install.sh | sh
ollama pull llama3.1:8b-instruct-q8_0
ollama run llama3.1:8b-instruct-q8_0
# L'API écoute sur localhost:11434 — test :
curl -s http://localhost:11434/api/generate \
-d '{"model":"llama3.1:8b-instruct-q8_0","prompt":"Bonjour","stream":false}'
Voie chirurgicale — llama-server
# Binaire officiel CUDA, ou compilation : cmake -B build -DGGML_CUDA=ON
./llama-server \
-m /data/models/qwen2.5-32b-instruct-q4_k_m.gguf \
-ngl 99 \
--ctx-size 8192 \
--port 8080
# -ngl 99 = toutes les couches sur GPU ; baissez-le pour l'offload CPU partiel
7. Benchmarks : les chiffres mesurés sur notre ligne GPU
Mesures internes d'août 2026, sur machines de production à Rotterdam : vLLM 0.9, llama.cpp b6xxx, prompt de 512 tokens, génération de 256 tokens, température 0. « Décode » = vitesse de génération pour un utilisateur seul ; « agrégé » = somme des débits avec 32 requêtes simultanées sous vLLM. Ce sont les chiffres que vous reproduirez chez vous, pas des maximums de laboratoire.
| Configuration | Moteur | Décode (1 flux) | Débit agrégé (batch 32) |
|---|---|---|---|
| Llama 3.1 8B Q8 · GPU-1 | vLLM | 68 tok/s | ~1 900 tok/s |
| Llama 3.1 8B Q4_K_M · GPU-1 | llama.cpp | 74 tok/s | mono-flux |
| Qwen 2.5 14B Q6_K · GPU-1 | Ollama | 47 tok/s | ~600 tok/s |
| Qwen 2.5 32B AWQ · GPU-1 | vLLM | 26 tok/s | ~340 tok/s |
| Llama 3.3 70B Q4_K_M · GPU-2 | llama.cpp | 14 tok/s | mono-flux |
| Llama 3.3 70B FP8 · GPU-3 (H100) | vLLM | 31 tok/s | ~2 400 tok/s |
Deux enseignements. D'abord, l'écart mono-flux / agrégé : un 8B sert un utilisateur à 68 tok/s, mais trente-deux à ~60 tok/s chacun — le batching continu de vLLM est la seule raison pour laquelle un seul GPU peut faire office d'API pour toute une équipe. Ensuite, le 70B en Q4 sur 2× 4090 reste une expérience « confortable en lecture » (14 tok/s ≈ 10 mots/seconde) mais lente pour des agents qui enchaînent les appels : si votre charge est agentive, préférez un 32B rapide à un 70B lent — la qualité perdue se regagne souvent avec un meilleur prompt et des outils.
8. Mise en production : exposer l'API proprement
vLLM et llama-server n'ont pas vocation à écouter sur Internet en clair. Le montage que nous déployons : le moteur lié à 127.0.0.1, un reverse proxy Caddy devant (TLS automatique), une clé API exigée par le proxy ou par vLLM lui-même (--api-key), et UFW qui ne laisse passer que 443 :
llm.example.com {
reverse_proxy 127.0.0.1:8000
# TLS Let's Encrypt automatique ; journalisation désactivée si vous le souhaitez
}
ufw allow OpenSSH
ufw allow 443/tcp comment 'API LLM TLS'
ufw default deny incoming
ufw enable
# Le port 8000 reste injoignable de l'extérieur : proxy ou rien
- Supervision : le conteneur vLLM en
--restart unless-stopped, ou un service systemd pour llama-server. Un GPU dédié ne redémarre pas souvent, mais un OOM de VRAM arrive — le service doit repartir seul. - Surveillance :
nvidia-smi --query-gpu=utilization.gpu,memory.used --format=csv -l 60dans un log, ou le node exporter + dcgm-exporter si vous avez déjà une stack Prometheus. La métrique qui compte est la VRAM libre : sa disparition annonce l'OOM. - Snapshots : une fois le serveur au point, un snapshot (1 $/mois) fige l'état propre. Réinstaller une GPU-1 de zéro prend dix minutes ; retrouver votre configuration exacte peut prendre une journée.