Como instalar o LiteLLM como gateway de LLM
Aprenda a executar o LiteLLM em um VPS com endpoint compatível com OpenAI, chaves virtuais, limites por chave, fallbacks e imagens fixadas.
O que faz um gateway de LLM autoalojado
LiteLLM é um gateway de LLM de código aberto que aloja no seu próprio servidor: um endpoint HTTP para o qual todas as aplicações enviam pedidos. Depois, o gateway encaminha cada pedido para o fornecedor que deve responder. LLM significa modelo de linguagem de grande dimensão. O gateway utiliza a API de conclusões de chat da OpenAI (interface de programação de aplicações). Por isso, qualquer biblioteca cliente que já comunique com a OpenAI funciona com ele depois de alterar duas coisas: o URL base e a chave.
Essa camada de indireção é o objetivo. As aplicações deixam de armazenar as credenciais dos fornecedores. A troca de um modelo passa a exigir uma linha num ficheiro de configuração no servidor, em vez de uma alteração de código em cinco serviços. Como todas as chamadas passam pelo mesmo processo, existe um local para definir um orçamento e registar o que foi gasto.
Quando estiver em execução, terá:
- Um endpoint. As aplicações utilizam
https://gateway.example.com/v1e pedem um nome de modelo que definiu, comobulkoustrong. - Chaves virtuais. Cada aplicação recebe a sua própria chave, com a sua própria lista de modelos permitidos e o seu próprio limite de gastos. Pode revogar uma chave sem afetar as restantes.
- Fallbacks. Uma chamada falhada ou um prompt demasiado grande é automaticamente repetido com outro modelo.
- Um registo em log. Cada pedido escreve uma linha com o respetivo custo. Assim, é possível responder à pergunta "qual aplicação gastou esse valor?".
Por que executar o gateway por conta própria
Um router gerido tem a mesma estrutura, mas com o processo de outra entidade no meio de cada pedido. Executá-lo por conta própria mantém as chaves do seu fornecedor e o texto dos seus prompts num servidor sob o seu controlo. O custo é real: agora é responsável pelo componente de que todas as aplicações dependem. A última secção deste guia trata desse custo, porque é a parte que a maioria dos tutoriais omite.
O que é necessário
- Uma VPS (servidor privado virtual) com Ubuntu 24.04, Docker e o plugin Compose instalados.
- Um nome de domínio apontado para esse servidor, se máquinas externas acederem ao gateway por TLS (segurança da camada de transporte).
- Pelo menos uma chave de API de um fornecedor.
O gateway não executa inferência. Encaminha os pedidos e devolve as respostas em streaming, pelo que o uso de CPU acompanha o volume de pedidos, e não o tamanho do modelo. Uma máquina com 1 vCPU suporta várias aplicações internas sem problemas. O que aumenta é a base de dados, porque o gateway regista uma linha de consumo por pedido.
Escreva primeiro o config.yaml
O ficheiro de configuração define quais modelos um cliente pode solicitar. Há quatro secções de nível superior relevantes: model_list, litellm_settings, router_settings e general_settings.
model_list:
- model_name: bulk
litellm_params:
model: anthropic/claude-haiku-4-5
api_key: os.environ/ANTHROPIC_API_KEY
- model_name: strong
litellm_params:
model: anthropic/claude-sonnet-5
api_key: os.environ/ANTHROPIC_API_KEY
- model_name: strong
litellm_params:
model: openai/gpt-5.5
api_key: os.environ/OPENAI_API_KEY
litellm_settings:
num_retries: 2
request_timeout: 120
allowed_fails: 3
cooldown_time: 30
json_logs: true
set_verbose: false
router_settings:
fallbacks: [{"bulk": ["strong"]}]
context_window_fallbacks: [{"bulk": ["strong"]}]
general_settings:
background_health_checks: true
health_check_interval: 300model_name é o nome que os seus clientes enviam. litellm_params.model é o modelo real, escrito como provider/model. Dê aos modelos nomes associados à função, e não ao fornecedor. Uma aplicação que solicita bulk continua a funcionar quando decidir, no próximo mês, que bulk deve ser um modelo diferente.
api_key: os.environ/ANTHROPIC_API_KEY indica ao LiteLLM que deve ler essa variável em tempo de execução. A chave literal nunca aparece no ficheiro, o que é importante porque config.yaml é o ficheiro que coloca no repositório.
Duas entradas partilham o nome strong, intencionalmente. Quando mais de uma implementação tem o mesmo model_name, o router trata-as como equivalentes e tenta a outra quando a primeira falha. É assim que strong continua a funcionar quando um fornecedor tem um período de indisponibilidade.
num_retries: 2 repete a tentativa na mesma implementação quando ocorre um erro que permite nova tentativa. Um fallback só é ativado depois de esgotadas essas tentativas. allowed_fails: 3 com cooldown_time: 30 remove uma implementação da rotação durante 30 segundos depois de esta falhar 3 vezes. Assim, um fornecedor que devolve respostas 500 deixa de ser tentado em todos os pedidos.
fallbacks e context_window_fallbacks têm acionadores diferentes, e o segundo é o mais útil, embora seja frequentemente ignorado.
fallbacksé ativado quando a chamada principal falha.context_window_fallbacksé ativado quando o fornecedor rejeita o pedido por este exceder a janela de contexto desse modelo. Assim, um prompt demasiado grande é encaminhado para um modelo com espaço suficiente, em vez de devolver um erro ao cliente.
Também existe content_policy_fallbacks, para quando um fornecedor recusa o pedido por motivos relacionados com a política de conteúdo. Defina-o apenas se tiver um destino adequado para esses pedidos.
Implementar o LiteLLM numa VPS com Docker Compose
Crie um diretório com três ficheiros: config.yaml, docker-compose.yml e .env. O guia de início rápido oficial usa a etiqueta latest. Em vez disso, fixe uma etiqueta de versão para que docker compose up -d no próximo mês lhe forneça o mesmo gateway que fornece hoje e para que possa reverter a alteração numa única linha.
services:
litellm:
image: ghcr.io/berriai/litellm:v1.95.0
restart: unless-stopped
command: ["--config", "/app/config.yaml", "--num_workers", "1"]
ports:
- "127.0.0.1:4000:4000"
volumes:
- ./config.yaml:/app/config.yaml:ro
env_file: .env
depends_on:
db:
condition: service_healthy
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_USER: litellm
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
POSTGRES_DB: litellm
healthcheck:
test: ["CMD-SHELL", "pg_isready -U litellm"]
interval: 5s
timeout: 5s
retries: 10
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:O Compose lê .env duas vezes neste caso. Primeiro, para substituir ${POSTGRES_PASSWORD} dentro do próprio ficheiro Compose e, depois, através de env_file, para passar todas as variáveis para o contentor.
v1.95.0 era a versão atual em agosto de 2026. Consulte a página de versões do projeto e fixe a versão que estiver atual quando fizer a implementação. Cada versão publica uma assinatura, por isso pode verificar a imagem antes de confiar nela:
cosign verify --key https://raw.githubusercontent.com/BerriAI/litellm/v1.95.0/cosign.pub ghcr.io/berriai/litellm:v1.95.0A linha da porta é 127.0.0.1:4000:4000, que publica a porta apenas na interface de loopback. Se escrever 4000:4000, o seu gateway ficará acessível a partir de toda a Internet, porque o Docker adiciona as suas próprias regras à cadeia FORWARD do iptables e essas regras são avaliadas antes das regras do ufw. Por isso, ufw deny 4000 não o impede. Esta é a forma mais comum de um gateway autoalojado ficar exposto: consulte como o Docker publica diretamente uma porta do contentor, ignorando o ufw. O tráfego externo chega através do reverse proxy.
Mantenha as chaves dos provedores fora da imagem
O ficheiro .env contém todos os segredos. Ele é fornecido como ambiente durante a execução, por isso nunca é incorporado na imagem nem submetido ao controlo de versões.
LITELLM_MASTER_KEY=sk-REPLACE_ME
LITELLM_SALT_KEY=sk-REPLACE_ME_TOO
POSTGRES_PASSWORD=REPLACE_ME_AS_WELL
DATABASE_URL=postgresql://litellm:REPLACE_ME_AS_WELL@db:5432/litellm
STORE_MODEL_IN_DB=True
LITELLM_MODE=PRODUCTION
LITELLM_LOG=ERROR
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-proj-...Gere as duas chaves do LiteLLM com aleatoriedade real e, em seguida, restrinja as permissões do ficheiro:
printf 'sk-%s\n' "$(openssl rand -hex 32)"
chmod 600 .envLITELLM_MASTER_KEY é a credencial de administrador. Ela autentica a API de gestão e é a palavra-passe da Admin UI em /ui. Nenhuma aplicação deve ter acesso a ela.
LITELLM_SALT_KEY cifra as credenciais dos provedores armazenadas na base de dados. Defina-o uma vez e não o altere. Se o alterar mais tarde, as credenciais já armazenadas não poderão ser decifradas. Nesse caso, o gateway inicia normalmente, mas todas as chamadas para esses provedores falham na autenticação.
STORE_MODEL_IN_DB=True permite adicionar e editar modelos a partir da Admin UI sem alterar o config.yaml. Isso é conveniente, mas divide a fonte de verdade em duas. Decida qual é a fonte autoritativa e registe essa decisão junto da configuração.
A mesma razão para manter as chaves fora do ficheiro de configuração aplica-se a mantê-las fora das ferramentas que entrega a um agente. Manter os segredos dos provedores fora dos agentes de IA descreve esse padrão, e ficheiros env e segredos no Docker Compose explica os detalhes da implementação.
Inicie o serviço e monitorize o primeiro arranque:
docker compose up -d
docker compose logs -f litellmVerifique se está efetivamente a funcionar
Existem duas sondagens sem autenticação e uma com autenticação. Cada uma falha por motivos diferentes.
curl -s http://127.0.0.1:4000/health/liveliness
curl -s http://127.0.0.1:4000/health/readiness/health/liveliness não requer autenticação e responde com "I'm alive!" enquanto o processo está em execução. /health/readiness também não requer autenticação. Devolve um objeto JSON com "status": "healthy" e um campo db, ou 503 quando a base de dados está inacessível. Direcione a monitorização para a prontidão, porque a vivacidade permanece verde num gateway que não consegue consultar uma única chave virtual.
A verificação autenticada é a que comunica com os fornecedores:
curl -s http://127.0.0.1:4000/health \
-H "Authorization: Bearer $LITELLM_MASTER_KEY"Responde com os arrays healthy_endpoints e unhealthy_endpoints. Um modelo em unhealthy_endpoints com um erro de autenticação indica que a chave do fornecedor em .env está incorreta ou ausente. Esta é a falha que pretende encontrar agora. Como background_health_checks: true está definido, o proxy executa estas sondagens automaticamente a cada health_check_interval segundos e /health devolve o último resultado. Assim, consultar esse endpoint não envia um pedido de teste aos fornecedores a cada consulta.
Chaves virtuais e limites de orçamento por chave
Cada aplicação recebe a sua própria chave, criada com base na chave principal.
curl -s http://127.0.0.1:4000/key/generate \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H 'Content-Type: application/json' \
-d '{
"key_alias": "nightly-summariser",
"models": ["bulk"],
"max_budget": 5,
"budget_duration": "30d",
"rpm_limit": 60,
"tpm_limit": 200000
}'A resposta contém um campo key que começa por sk-. Essa string é o que a aplicação recebe. É a única informação que a aplicação recebe.
modelsé uma lista de permissões que define o que esta chave pode solicitar. A chave acima pode solicitarbulke nada mais.max_budget: 5combudget_duration: "30d"é de cinco dólares norte-americanos por 30 dias consecutivos. Depois disso, a chave deixa de funcionar.rpm_limitetpm_limitlimitam os pedidos por minuto e os tokens por minuto apenas para esta chave.key_aliasé o valor que irá reconhecer no registo de despesas seis semanas mais tarde. Defina-o sempre.
Quando o orçamento se esgota, a chamada falha com HTTP 401 e um corpo neste formato:
ExceededBudget: Current spend for token: 7.2e-05; Max Budget for Token: 2e-07O código de estado é o que torna isto confuso. Uma biblioteca cliente comunica 401 como um problema de autenticação. Por isso, o programador que lê o stack trace começa por verificar se a chave é válida. Registe o corpo da resposta juntamente com o código de estado. Caso contrário, o esgotamento do orçamento parecerá sempre uma credencial inválida.
Inspecione e ajuste as chaves através da mesma API de gestão:
curl -s "http://127.0.0.1:4000/key/info?key=sk-..." \
-H "Authorization: Bearer $LITELLM_MASTER_KEY"
curl -s -X POST http://127.0.0.1:4000/key/update \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H 'Content-Type: application/json' \
-d '{"key": "sk-...", "max_budget": 25}'Um orçamento imposto no gateway continua válido mesmo quando o problema está no próprio agente. Por isso, é a base do controlo de custos para agentes de IA num VPS.
Enviar tarefas em massa para um modelo barato
Aponte um cliente para o gateway. URL base, chave e nome do modelo:
curl -s http://127.0.0.1:4000/v1/chat/completions \
-H "Authorization: Bearer sk-<the virtual key>" \
-H 'Content-Type: application/json' \
-d '{
"model": "bulk",
"messages": [{"role": "user", "content": "Say hello in five words."}]
}'Qualquer biblioteca cliente da OpenAI funciona da mesma forma: defina base_url como https://gateway.example.com/v1 e api_key como a chave virtual.
A política de encaminhamento de config.yaml passa a aplicar-se sem que o chamador tenha conhecimento disso. Um pedido para bulk é encaminhado para o modelo barato. Se essa chamada falhar depois das tentativas configuradas, o pedido é repetido com strong. Se o prompt for demasiado longo para bulk, context_window_fallbacks encaminha-o para strong em vez de devolver um erro. Tarefas em massa, como uma classificação ou a criação de resumos de um backlog, usam o modelo barato por predefinição. Apenas os pedidos mais exigentes têm um custo superior.
É também neste cenário que um gateway se justifica para agentes que utilizam ferramentas. Um servidor MCP (model context protocol) no mesmo VPS e o agente que o controla podem apontar para um único endpoint. Assim, é possível mudar o modelo usado sem reimplementar nenhum dos dois.
Como saber se ocorreu um fallback?
Este é o modo de falha que custa dinheiro, porque nada parece estar avariado. Um fallback bem-sucedido devolve HTTP 200 com um corpo de resposta normal. O seu modelo barato pode ficar indisponível durante um dia, enquanto todas as chamadas são silenciosamente atendidas pelo modelo caro. A primeira evidência pode ser a fatura.
A evidência existe nos cabeçalhos da resposta. Peça-os:
curl -s -D - -o /dev/null http://127.0.0.1:4000/v1/chat/completions \
-H "Authorization: Bearer sk-<the virtual key>" \
-H 'Content-Type: application/json' \
-d '{"model":"bulk","messages":[{"role":"user","content":"ping"}]}' \
| grep -i '^x-litellm'x-litellm-model-groupindica o que o cliente solicitou.x-litellm-model-idindica a deployment que respondeu. Quando estes dois valores são diferentes, ocorreu um fallback.x-litellm-attempted-fallbacksex-litellm-attempted-retriesfazem a contagem. Numa chamada normal, ambos são 0.x-litellm-response-costindica o custo dessa chamada em dólares americanos.x-litellm-call-idé o identificador usado para encontrar a mesma chamada nos logs.
Registe x-litellm-attempted-fallbacks em todos os pedidos e gere um alerta quando deixar de ser 0. Esse único número distingue uma política de encaminhamento funcional de uma política que se tornou silenciosamente "usar sempre o modelo caro".
A versão completa deste processo é o tracing e merece uma configuração própria: Langfuse self-hosted para tracing de chamadas de agentes. O LiteLLM disponibiliza o callback, pelo que a integração requer duas linhas e as credenciais.
litellm_settings:
success_callback: ["langfuse"]
failure_callback: ["langfuse"]LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_HOST=https://langfuse.example.comDefina failure_callback e success_callback. Se ignorar esta configuração, os únicos traces conservados serão os das chamadas em que nada correu mal. Separadamente de tudo isto, o LiteLLM escreve uma linha de custos por pedido no Postgres, e a Admin UI em /ui lê essa tabela. A tabela cresce com o tráfego, por isso monitorize-a num disco pequeno.
Coloque o gateway atrás de um reverse proxy
Nada fora do servidor deve conseguir aceder à porta 4000. Termine o TLS no nginx ou no Caddy e encaminhe as ligações para o endereço de loopback.
location / {
proxy_pass http://127.0.0.1:4000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_read_timeout 600s;
}Duas dessas linhas são frequentemente omitidas. proxy_buffering off é importante porque uma resposta de streaming é composta por uma série de eventos enviados pelo servidor. Com o buffering ativado, o nginx mantém os blocos em memória até a resposta terminar. O cliente fica sem receber dados e depois recebe tudo de uma vez. proxy_read_timeout 600s é importante porque uma geração longa ultrapassa o valor predefinido de 60 segundos do nginx. Nesse caso, o cliente recebe um erro 504 e o log de erros regista upstream timed out (110: Connection timed out) while reading response header from upstream.
Para o certificado, Certbot com Let's Encrypt no nginx é o caminho mais curto. Se o servidor já disponibiliza vários contentores, Traefik à frente de várias aplicações Compose trata do encaminhamento e dos certificados num único local.
O gateway agora é um ponto único de falha
Seja claro sobre o que construiu. Todas as aplicações que gere dependem agora de um único container num único VPS. Enquanto estiver indisponível, nada pode chamar qualquer modelo, incluindo os providers que estão perfeitamente funcionais. Isto tem quatro consequências.
- Uma configuração inválida derruba tudo de uma vez.
restart: unless-stoppedreinicia um processo que terminou por falha e também reinicia repetidamente um container que não consegue analisar o config.yaml. Leiadocker compose logs litellmdepois de cada alteração de configuração e faça alterações quando tiver tempo para as monitorizar. - O Postgres fica no caminho dos pedidos. A consulta das virtual keys e o registo dos gastos utilizam ambos a base de dados.
/health/readinessdevolver 503 é o aviso de que o gateway está em execução, mas não consegue fazer nenhuma das duas operações. - Escale adicionando instâncias, não tornando uma instância maior. A orientação do próprio projeto é usar um worker por instância (
--num_workers 1) e várias instâncias a partilhar uma base de dados. Dois gateways pequenos atrás de um load balancer retiram o container único da equação. Não retiram a base de dados. - Faça cópias de segurança do que não pode regenerar. Isso inclui
config.yamle.env, juntamente com umpg_dumpda base de dados. PerderLITELLM_SALT_KEYtorna inúteis as credenciais encriptadas dos providers dentro desse dump. Por isso, o ficheiro de ambiente e o dump devem pertencer ao mesmo trabalho de backup: backups do restic para armazenamento externo.
A atualização consiste em editar a tag da imagem e executar docker compose up -d. Por predefinição, o LiteLLM executa prisma migrate deploy no arranque. O novo container migra o esquema da base de dados no primeiro arranque. Faça o dump antes de alterar a tag, porque voltar a usar a imagem antiga não desfaz uma migração que já tenha sido executada.
FAQ
O LiteLLM adiciona latência perceptível a todas as chamadas?
O projeto publica 8 ms no percentil 95 a 1000 pedidos por segundo, conforme indicado no seu README em agosto de 2026. Considere esse valor como uma métrica do fornecedor. O fator que realmente altera a latência é a distância de rede entre as aplicações e o gateway, porque foi acrescentada mais uma viagem de ida e volta a cada chamada. Execute o gateway na mesma região das aplicações que o chamam e meça a sua própria sobrecarga com o cabeçalho x-litellm-overhead-duration-ms numa resposta real.
Por que o streaming deixou de funcionar depois que coloquei o nginx à frente?
Porque o nginx armazena em buffer as respostas do upstream por predefinição, e uma conclusão em streaming é uma série de eventos enviados pelo servidor. Com proxy_buffering ativado, o nginx recolhe os blocos e só os envia quando a resposta termina. O cliente fica sem receber dados e depois recebe a resposta completa de uma só vez. Defina proxy_buffering off; no bloco location. Aumente proxy_read_timeout no mesmo bloco, porque uma geração longa pode ultrapassar o valor predefinido de 60 segundos do nginx e o cliente recebe um 504.
O que acontece quando uma chave virtual esgota o orçamento?
A chamada falha com HTTP 401 e um corpo no formato ExceededBudget: Current spend for token: 7.2e-05; Max Budget for Token: 2e-07. O 401 é enganador: uma biblioteca cliente apresenta-o como uma falha de autenticação, por isso as pessoas começam por verificar se a chave é válida em vez de lerem a mensagem. Registe o corpo da resposta juntamente com o código de estado. Confirme a posição real da chave com /key/info?key=sk-... usando a master key e aumente o limite com /key/update se o orçamento tiver sido definido demasiado baixo.
O gateway pode encaminhar para um modelo local e para modelos alojados?
Sim. O modelo local é mais uma entrada em model_list. Use o prefixo ollama_chat/ com um api_base, por exemplo model: ollama_chat/llama3.1 juntamente com api_base: http://ollama:11434. Dentro de um contentor, localhost refere-se a esse contentor. Por isso, use o nome do serviço Compose ou o endereço do host na rede Docker, nunca 127.0.0.1. Colocar o modelo local em funcionamento é uma tarefa separada. Consulte alojar um LLM com Ollama num VPS.