Aller au contenu
Développement medium

LiteLLM Proxy Server : gateway LLM pour l'entreprise

70 min de lecture

logo litellm

LiteLLM Proxy Server est une passerelle API qui unifie l'accès à plus de 100 LLM (OpenAI, Anthropic, Ollama, Azure...) via une interface compatible OpenAI. En entreprise, il résout trois problèmes majeurs : centraliser les clés API, contrôler les coûts (budgets par équipe), et restreindre l'accès (modèles autorisés, rate limits).

Ce guide vous accompagne de l'installation jusqu'à la mise en production. À la fin, vous saurez :

  • Déployer un proxy local avec Docker et Ollama
  • Créer des utilisateurs, équipes et clés virtuelles
  • Limiter l'accès par modèle, budget et rate limit
  • Monitorer la consommation en temps réel
  • Architecture : comment LiteLLM s'insère entre vos applications et les LLM
  • Lab Docker : proxy fonctionnel avec Ollama (gratuit, local)
  • Gestion des accès : utilisateurs, équipes, clés virtuelles
  • Restrictions : modèles autorisés, budgets, rate limits (RPM/TPM)
  • Production : Docker Compose avec PostgreSQL et persistance

Sans passerelle, chaque application détient ses propres clés API et les intègre à sa configuration. Le problème n'est pas technique mais organisationnel : personne ne sait combien de clés circulent, ni ce que chacune consomme, ni comment en révoquer une sans redéployer le service qui l'utilise. Le tableau ci-dessous met en regard cette situation et ce qu'apporte une passerelle unique.

Sans proxyAvec LiteLLM Proxy
Clés API éparpillées dans le codeUne seule clé par équipe
Impossible de suivre les coûtsBudget par user/team/clé
Dépendance à un fournisseurFallback automatique
Pas de rate limiting centraliséRPM/TPM par clé
FonctionnalitéOpen SourceEnterprise (250$/mois)
100+ LLM supportés
Clés virtuelles
Budgets & rate limits
Interface web
SSO/OIDC
Métriques Prometheus
Support SLA

Nous allons créer un lab complet sans clé API payante. Ollama exécute les modèles localement, LiteLLM Proxy les expose via une API compatible OpenAI.

Trois conteneurs et un service hôte composent le lab. Ollama tourne sur la machine hôte et sert les modèles ; LiteLLM Proxy s'intercale et expose une API compatible OpenAI ; PostgreSQL persiste les clés, les utilisateurs et les compteurs de dépense. Le point à retenir sur le schéma est le sens des flèches : vos applications ne parlent jamais directement au fournisseur de modèles, elles parlent au proxy, qui seul détient les identifiants et applique les quotas.

Architecture LiteLLM Proxy avec Docker et Ollama

  • Docker installé
  • Ollama installé (ollama.ai)
  • 4 Go de RAM disponibles pour les modèles

Ollama doit écouter sur toutes les interfaces pour être accessible depuis Docker :

Fenêtre de terminal
# Arrêter Ollama s'il tourne
pkill ollama
# Redémarrer en écoutant sur toutes les interfaces
OLLAMA_HOST=0.0.0.0 ollama serve &
# Vérifier
curl -s http://localhost:11434/api/tags | jq '.models[].name'

Deux modèles suffisent pour ce lab, et les prendre très petits est délibéré : l'objectif est de valider le routage, les clés et les quotas, pas la qualité des réponses. qwen2:0.5b tient en 352 Mo et répond en quelques centaines de millisecondes sur un CPU récent, llama3.2:1b sert de second modèle pour vérifier que les restrictions par clé distinguent bien deux cibles différentes.

Fenêtre de terminal
# Modèle léger pour les tests (352 Mo)
ollama pull qwen2:0.5b
# Modèle plus capable (1.3 Go)
ollama pull llama3.2:1b
# Vérifier les modèles disponibles
ollama list

Sortie attendue :

NAME ID SIZE MODIFIED
llama3.2:1b baf6a787fdff 1.3 GB 2 minutes ago
qwen2:0.5b 6f48b936a09f 352 MB 3 minutes ago

Créez un dossier pour le lab :

Fenêtre de terminal
mkdir -p ~/Projets/litellm-lab/config
cd ~/Projets/litellm-lab

Créez le fichier config/proxy-config.yaml :

# LiteLLM Proxy Server - Configuration Lab
model_list:
# Modèle principal
- model_name: llama
litellm_params:
model: ollama/llama3.2:1b
api_base: http://host.docker.internal:11434
# Modèle secondaire (plus rapide)
- model_name: qwen
litellm_params:
model: ollama/qwen2:0.5b
api_base: http://host.docker.internal:11434
# Alias pour compatibilité OpenAI
- model_name: gpt-3.5-turbo
litellm_params:
model: ollama/qwen2:0.5b
api_base: http://host.docker.internal:11434
- model_name: gpt-4
litellm_params:
model: ollama/llama3.2:1b
api_base: http://host.docker.internal:11434
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY
json_logs: true
router_settings:
routing_strategy: simple-shuffle
num_retries: 2
timeout: 120

LiteLLM Proxy démarre sans base de données, mais perd alors toute notion d'utilisateur, de clé virtuelle et de dépense : la gestion des accès nécessite PostgreSQL. Le conteneur est lancé ici avec un mot de passe en clair parce qu'il s'agit d'un lab local ; en production, ce mot de passe vient d'un fichier d'environnement non versionné et le port 5432 n'est pas publié sur l'hôte.

Fenêtre de terminal
docker run -d --name litellm-postgres \
-e POSTGRES_USER=litellm \
-e POSTGRES_PASSWORD=litellm_secure_pwd \
-e POSTGRES_DB=litellm \
-p 5432:5432 \
postgres:16-alpine

Vérification :

Fenêtre de terminal
docker ps | grep litellm-postgres
# Doit afficher le conteneur en cours d'exécution

Quatre paramètres de cette commande méritent votre attention. LITELLM_MASTER_KEY est la clé d'administration : elle crée toutes les autres clés et ne doit jamais être distribuée à une application. DATABASE_URL pointe vers le PostgreSQL lancé à l'étape précédente, sans quoi le proxy démarre mais perd clés et compteurs à chaque redémarrage. --add-host donne au conteneur une route vers Ollama sur l'hôte. Enfin, la configuration est montée en lecture seule (:ro) : le proxy n'a aucune raison de réécrire son propre fichier de modèles.

Fenêtre de terminal
docker run -d --name litellm-proxy \
-p 4000:4000 \
-v $(pwd)/config/proxy-config.yaml:/app/config.yaml:ro \
-e LITELLM_MASTER_KEY=sk-litellm-lab-2026-demo \
-e DATABASE_URL="postgresql://litellm:litellm_secure_pwd@172.17.0.1:5432/litellm" \
-e UI_USERNAME=admin \
-e UI_PASSWORD=AdminSecure2026 \
--add-host=host.docker.internal:host-gateway \
ghcr.io/berriai/litellm:main-latest \
--config /app/config.yaml --port 4000

Vérifier les logs :

Fenêtre de terminal
docker logs litellm-proxy --tail 20

Sortie attendue :

██╗ ██╗████████╗███████╗██╗ ██╗ ███╗ ███╗
██║ ██║╚══██╔══╝██╔════╝██║ ██║ ████╗ ████║
...
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:4000

Deux vérifications enchaînées, dans cet ordre. /v1/models confirme que le proxy a lu sa configuration : les quatre noms retournés sont ceux déclarés dans model_list, pas ceux d'Ollama. Puis /v1/chat/completions prouve que la chaîne complète fonctionne jusqu'au modèle. Si la première commande répond et pas la seconde, le problème est entre le conteneur et Ollama, pas dans la configuration du proxy.

Fenêtre de terminal
# Lister les modèles disponibles
curl -s http://localhost:4000/v1/models \
-H "Authorization: Bearer sk-litellm-lab-2026-demo" | jq '.data[].id'

Sortie attendue :

"llama"
"qwen"
"gpt-3.5-turbo"
"gpt-4"
Fenêtre de terminal
# Tester une complétion
curl -s http://localhost:4000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-litellm-lab-2026-demo" \
-d '{
"model": "qwen",
"messages": [{"role": "user", "content": "Dis bonjour en une phrase"}],
"max_tokens": 30
}' | jq '.choices[0].message.content'

Sortie attendue :

"Bonjour! Comment puis-je vous aider aujourd'hui?"

LiteLLM propose une hiérarchie à 3 niveaux : UtilisateursÉquipesClés. Chaque niveau peut avoir ses propres restrictions.

L'utilisateur porte l'identité et le budget d'une personne. Deux champs pilotent tout le reste : max_budget fixe le plafond de dépense en dollars et budget_duration la fenêtre sur laquelle il se réinitialise (30d ici). Le champ models restreint d'emblée les modèles atteignables, ce qui vaut mieux que de l'imposer clé par clé ensuite. Le bloc metadata est libre : il ressort dans les rapports de dépense et sert à ventiler les coûts par équipe ou par projet.

Fenêtre de terminal
curl -s http://localhost:4000/user/new \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-litellm-lab-2026-demo" \
-d '{
"user_id": "alice@example.com",
"user_email": "alice@example.com",
"max_budget": 5.0,
"budget_duration": "30d",
"models": ["qwen"],
"metadata": {"role": "developer", "department": "engineering"}
}' | jq '{user_id, max_budget, models}'

Sortie :

{
"user_id": "alice@example.com",
"max_budget": 5.0,
"models": ["qwen"]
}

L'équipe agrège les dépenses de plusieurs utilisateurs et porte les limites de débit partagées, ici 60 requêtes et 100 000 tokens par minute. Notez la valeur retournée : LiteLLM génère un team_id en UUID que vous devrez reporter dans les créations de clés. Le team_alias est le nom lisible affiché dans l'interface, il ne s'utilise pas comme identifiant dans l'API.

Fenêtre de terminal
curl -s http://localhost:4000/team/new \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-litellm-lab-2026-demo" \
-d '{
"team_alias": "ml-team",
"max_budget": 50.0,
"budget_duration": "30d",
"models": ["qwen", "llama"],
"tpm_limit": 100000,
"rpm_limit": 60,
"metadata": {"project": "ml-pipeline"}
}' | jq '{team_id, team_alias, max_budget, rpm_limit}'

Sortie :

{
"team_id": "0b71f785-5fec-48c0-a76d-87d3289fafb4",
"team_alias": "ml-team",
"max_budget": 50.0,
"rpm_limit": 60
}

Les clés virtuelles permettent de distribuer des accès sans partager la master key.

La clé est ce que vous distribuez réellement à une application. Elle est retournée une seule fois en clair, la base ne conserve ensuite qu'une empreinte : perdre la valeur oblige à en régénérer une. Le key_alias sert à la retrouver dans l'interface sans connaître sa valeur, ce qui en fait le seul moyen pratique de révoquer la bonne clé le jour où une application se comporte mal.

Fenêtre de terminal
curl -s http://localhost:4000/key/generate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-litellm-lab-2026-demo" \
-d '{
"key_alias": "dev-team-key",
"max_budget": 10.0,
"budget_duration": "30d",
"models": ["qwen", "llama"],
"metadata": {"team": "dev", "project": "demo"}
}' | jq '{key, max_budget, models}'

Sortie :

{
"key": "sk-Li3j_hWPrxs5MY3m0DBAJg",
"max_budget": 10.0,
"models": ["qwen", "llama"]
}

Cette clé est limitée à qwen et llama. Testons l'accès à gpt-4 :

Fenêtre de terminal
curl -s http://localhost:4000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-Li3j_hWPrxs5MY3m0DBAJg" \
-d '{"model": "gpt-4", "messages": [{"role": "user", "content": "test"}]}' \
| jq '.error'

Sortie (accès refusé) :

{
"message": "key not allowed to access model. This key can only access models=['qwen', 'llama']. Tried to access gpt-4",
"type": "key_model_access_denied",
"code": "401"
}

Créer une clé liée à un utilisateur et une équipe

Section intitulée « Créer une clé liée à un utilisateur et une équipe »

Rattacher la clé à un user_id et à un team_id permet d'imputer chaque appel aux deux niveaux à la fois. La conséquence pratique est celle décrite dans l'encadré ci-dessus : la clé s'arrête au plafond le plus bas des trois, ici les 2 dollars de la clé, même si l'équipe dispose encore de 48 dollars de budget.

Fenêtre de terminal
curl -s http://localhost:4000/key/generate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-litellm-lab-2026-demo" \
-d '{
"key_alias": "alice-personal-key",
"user_id": "alice@example.com",
"team_id": "0b71f785-5fec-48c0-a76d-87d3289fafb4",
"max_budget": 2.0
}' | jq '{key, user_id, team_id, max_budget}'

Le rate limiting protège contre les abus et contrôle la consommation.

Les valeurs choisies ici sont volontairement absurdes, 2 requêtes et 100 tokens par minute, pour rendre le blocage observable en trois commandes. En production, dimensionnez rpm_limit sur le débit nominal de l'application majoré d'une marge, et tpm_limit sur le budget mensuel divisé par le nombre de minutes ouvrées, faute de quoi la limite se déclenchera en pleine journée de pointe.

Fenêtre de terminal
curl -s http://localhost:4000/key/generate \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk-litellm-lab-2026-demo" \
-d '{
"key_alias": "rate-limited-key",
"rpm_limit": 2,
"tpm_limit": 100,
"models": ["qwen"]
}' | jq '{key, rpm_limit, tpm_limit}'

Sortie :

{
"key": "sk-9MwdeGpwsUJ9z0NPftXz4A",
"rpm_limit": 2,
"tpm_limit": 100
}

La vérification consiste à dépasser volontairement la limite. Trois requêtes envoyées coup sur coup contre une clé limitée à 2 requêtes par minute doivent produire deux réponses puis une erreur. Ce qu'il faut observer dans la sortie, c'est la forme du refus : LiteLLM ne coupe pas la connexion, il renvoie un message d'erreur structuré indiquant la limite atteinte et l'heure de réinitialisation, ce que votre application doit savoir interpréter pour réessayer plus tard.

Fenêtre de terminal
KEY="sk-9MwdeGpwsUJ9z0NPftXz4A"
for i in 1 2 3; do
echo "=== Requête $i ==="
curl -s http://localhost:4000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $KEY" \
-d '{"model": "qwen", "messages": [{"role": "user", "content": "1"}], "max_tokens": 5}' \
| jq -c '{ok: .choices[0].message.content, error: .error.message}'
done

Sortie :

=== Requête 1 ===
{"ok":"Hello! It seems like","error":null}
=== Requête 2 ===
{"ok":"Hello! How can I","error":null}
=== Requête 3 ===
{"ok":null,"error":"Rate limit exceeded... Current limit: 2, Remaining: 0. Limit resets at: ..."}

L'endpoint /spend/tags agrège les dépenses selon les tags posés sur les requêtes ou hérités des métadonnées de clé. C'est la donnée à extraire pour une refacturation interne : elle est stable, horodatée et exportable, alors que les graphiques de l'interface ne se copient pas dans un tableur. Sur un lab qui vient de démarrer, la réponse est un tableau vide, ce qui est normal tant qu'aucun appel facturé n'a eu lieu.

Fenêtre de terminal
curl -s http://localhost:4000/spend/tags \
-H "Authorization: Bearer sk-litellm-lab-2026-demo" | jq .

Accédez à l'interface web http://localhost:4000/ui pour visualiser :

  • Dépenses par modèle, utilisateur, équipe
  • Graphiques de consommation dans le temps
  • Alertes de dépassement de budget

Pour un déploiement de production, passez à Docker Compose avec persistance. Trois différences avec le lab comptent vraiment. Les images sont épinglées par digest plutôt que par tag, car main-latest suit chaque fusion sur la branche principale et rendrait votre proxy imprévisible ; relevez le digest courant avec docker buildx imagetools inspect <image> avant de le figer, et remontez-le sciemment. Le depends_on avec condition: service_healthy évite le démarrage en échec du proxy tant que la base n'accepte pas de connexion. Enfin, la configuration est montée en lecture seule et aucun secret n'est écrit dans le fichier, tout passe par le .env décrit juste après.

docker-compose.yml
services:
postgres:
image: postgres:16-alpine@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777
container_name: litellm-db
environment:
POSTGRES_USER: litellm
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-changeme}
POSTGRES_DB: litellm
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U litellm"]
interval: 5s
timeout: 5s
retries: 5
litellm:
image: ghcr.io/berriai/litellm:main-stable@sha256:a1745e629abfb17d434426ff48b115f54f4f4c4a0f5af241de569e93c63c411e
container_name: litellm-proxy
ports:
- "4000:4000"
environment:
LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}
DATABASE_URL: postgresql://litellm:${POSTGRES_PASSWORD:-changeme}@postgres:5432/litellm
UI_USERNAME: ${UI_USERNAME:-admin}
UI_PASSWORD: ${UI_PASSWORD}
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
volumes:
- ./config/proxy-config.yaml:/app/config.yaml:ro
depends_on:
postgres:
condition: service_healthy
command: ["--config", "/app/config.yaml", "--port", "4000"]
restart: unless-stopped
volumes:
postgres_data:

Le fichier .env porte l'intégralité des valeurs sensibles et ne doit jamais être versionné : ajoutez-le à .gitignore avant même de le créer. Générez la master key avec openssl rand -hex 32 plutôt que de la choisir à la main, et restreignez les droits du fichier avec chmod 600 .env pour qu'il ne soit lisible que par le compte qui lance Compose.

Fichier .env :

Fenêtre de terminal
LITELLM_MASTER_KEY=sk-your-secure-master-key-here
POSTGRES_PASSWORD=your-secure-db-password
UI_USERNAME=admin
UI_PASSWORD=YourSecureUIPassword!
OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...

Lancement :

Fenêtre de terminal
docker compose up -d
docker compose logs -f litellm

Les deux premières lignes sont les seules qui empêchent le proxy de démarrer, les trois autres se produisent une fois qu'il tourne. Une confusion revient souvent : Authentication Error vient de la master key, alors que key not allowed to access model vient d'une clé virtuelle correctement authentifiée mais restreinte. La première est un problème de configuration, la seconde le fonctionnement attendu.

SymptômeCause probableSolution
Cannot connect to host localhost:11434Ollama écoute sur 127.0.0.1Lancer avec OLLAMA_HOST=0.0.0.0
Unable to find Prisma binariesInstallation pip sans DockerUtiliser l'image Docker officielle
Rate limit exceededLimite atteinteAttendre le reset ou augmenter la limite
key not allowed to access modelModèle non autoriséAjouter le modèle à la clé
Authentication ErrorMaster key incorrecteVérifier LITELLM_MASTER_KEY

Prenez ces quatre commandes dans l'ordre : elles vont du plus général au plus précis. Les logs disent si le processus a démarré, la vérification PostgreSQL si la persistance répond, la liste des clés si l'état applicatif est cohérent, et le health check si le proxy joint réellement les modèles déclarés. C'est cette dernière qui distingue un proxy en bonne santé d'un proxy qui répond mais dont tous les backends sont injoignables.

Fenêtre de terminal
# Logs du proxy
docker logs litellm-proxy --tail 100
# Vérifier la connexion PostgreSQL
docker exec litellm-proxy psql $DATABASE_URL -c "SELECT 1"
# Lister toutes les clés (tokens hachés)
curl -s http://localhost:4000/key/list \
-H "Authorization: Bearer sk-litellm-lab-2026-demo" | jq .
# Health check
curl -s http://localhost:4000/health | jq .

Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  1. LiteLLM Proxy unifie l'accès à 100+ LLM via une API compatible OpenAI
  2. Hiérarchie 3 niveaux : User → Team → Key, chacun avec ses restrictions
  3. Restrictions par clé : modèles autorisés, budget, rate limits (RPM/TPM)
  4. Docker recommandé pour éviter les problèmes de dépendances (Prisma)
  5. PostgreSQL requis pour la persistance des clés, users et métriques
  6. Production : HTTPS obligatoire, master key sécurisée, backups

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn