Как установить и настроить LiteLLM на VPS
Со вторым и третьим провайдером моделей начинается зоопарк: свой SDK, свои коды ошибок, свои лимиты и свой счёт у каждого, а ключи расползаются по .env разработчиков. LiteLLM Proxy сводит это в один OpenAI-совместимый эндпоинт с общими ключами и бюджетами. Разбираем установку на VPS: Docker Compose с Postgres, config.yaml, виртуальные ключи, HTTPS и место, где ломается стриминг.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что делает LiteLLM Proxy и когда он оправдан
LiteLLM принимает запросы в формате OpenAI (POST /v1/chat/completions) и переводит их в формат нужного провайдера: OpenAI, Anthropic, Google, Mistral, Groq, Bedrock, локальных Ollama и vLLM. В коде остаётся один base_url, один ключ и одно имя модели, которое придумали вы.
- Один ключ на приложение — выпущенный шлюзом
sk-..., ключи провайдеров остаются на сервере. - Бюджеты — потолок в долларах, срок жизни ключа, лимит запросов в минуту: кончился бюджет, и запросы отбиваются, а не приходит счёт на 900 долларов.
- Фолбэки — провайдер отдал 429 или 500, запрос уходит на резервную модель.
- Учёт расходов — каждое обращение пишется в базу с токенами и ценой.
Честно о том, когда шлюз не нужен: при одном приложении и одном провайдере это лишнее звено, которое надо обновлять и чинить. И LiteLLM не запускает модели — для локального инференса за ним ставят Ollama или vLLM.
Что подготовить на VPS до установки
Система — Ubuntu 24.04 LTS или Debian 12, вход по SSH-ключу. Из софта нужен только Docker:
curl -fsSL https://get.docker.com | sh
systemctl enable --now docker
docker compose version
# Docker Compose version v2.39.4
Локация решается один раз. OpenAI, Anthropic и Google не обслуживают российские IP: с RU-адреса приходит не таймаут, а внятный отказ.
{"error":{"code":"unsupported_country_region_territory",
"message":"Country, region, or territory not supported",
"type":"request_forbidden"}}
Поэтому шлюз к зарубежным провайдерам ставят за пределами РФ. Для OpenAI и Anthropic практичнее всего США: прямая связность и чистые адреса без истории абузов — логика выбора разобрана в материале про VPS в США для доступа к нейросетям.
Фаервол: наружу только 443, порт 4000 остаётся локальным.
ufw allow 22/tcp
ufw allow 80,443/tcp
ufw enable
ufw status numbered
Здесь регулярная ловушка: Docker обходит ufw. С ports: - "4000:4000" контейнер пропишет правила в цепочку DOCKER-USER раньше фильтров ufw, и админка LiteLLM окажется открыта всему интернету, хотя ufw status показывает закрытый порт. Публикуйте порт только на loopback: 127.0.0.1:4000:4000, а снаружи проверьте nmap -Pn -p 4000 ваш_ip — должно быть filtered, а не open. A-запись домена заведите заранее.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть LiteLLMУстановка через Docker Compose с Postgres
Без базы LiteLLM работает, но тогда нет виртуальных ключей, бюджетов и веб-интерфейса. Ставим «шлюз + Postgres + Redis», мастер-ключ и соль генерируем сразу:
mkdir -p /opt/litellm && cd /opt/litellm
cat > .env <<EOF
LITELLM_MASTER_KEY=sk-$(openssl rand -hex 24)
LITELLM_SALT_KEY=sk-$(openssl rand -hex 32)
PG_PASSWORD=$(openssl rand -hex 16)
OPENAI_API_KEY=sk-proj-...
ANTHROPIC_API_KEY=sk-ant-...
EOF
chmod 600 .env
LITELLM_SALT_KEY — ключ, которым LiteLLM шифрует ключи провайдеров в базе. Запишите его в менеджер паролей сейчас: потеряли или сменили соль после добавления моделей через UI — расшифровать их нечем, и модели придётся заводить заново.
Теперь docker-compose.yml:
services:
litellm:
image: ghcr.io/berriai/litellm-database:main-stable
restart: unless-stopped
ports:
- "127.0.0.1:4000:4000"
env_file: .env
environment:
DATABASE_URL: postgresql://litellm:${PG_PASSWORD}@postgres:5432/litellm
REDIS_URL: redis://redis:6379
volumes:
- ./config.yaml:/app/config.yaml:ro
command: ["--config", "/app/config.yaml", "--port", "4000", "--num_workers", "2"]
depends_on:
postgres:
condition: service_healthy
postgres:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_DB: litellm
POSTGRES_USER: litellm
POSTGRES_PASSWORD: ${PG_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U litellm -d litellm"]
interval: 5s
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
pgdata:
Два момента экономят вечер. Образ именно litellm-database: в нём при старте прогоняются миграции Prisma. И condition: service_healthy обязателен — иначе контейнер стартует раньше базы и падает с Error: P1001: Can't reach database server at 'postgres':'5432'.
Минимальный config.yaml, чтобы шлюз поднялся:
model_list:
- model_name: gpt-4o
litellm_params:
model: openai/gpt-4o
api_key: os.environ/OPENAI_API_KEY
general_settings:
master_key: os.environ/LITELLM_MASTER_KEY
database_url: os.environ/DATABASE_URL
store_model_in_db: true
Запуск и проверка:
docker compose up -d
curl -s http://127.0.0.1:4000/health/liveliness
# "I'm alive!"
curl -s http://127.0.0.1:4000/health/readiness
# {"status":"connected","db":"connected","cache":null,"litellm_version":"1.77.3"}
db: connected означает, что база подхватилась и миграции прошли. Если там "db":"Not connected" — проверьте DATABASE_URL: хост базы в Compose это postgres, не localhost.
Без Docker тоже можно — pip install 'litellm[proxy]' в venv на Python 3.11 или 3.12 плюс unit для systemd; в zsh кавычки обязательны, иначе получите zsh: no matches found: litellm[proxy]. Этот путь разобран в пошаговой установке на Ubuntu 24.04.
Разбор config.yaml: модели, фолбэки, кэш
От минимального рабочий конфиг отличается алиасами, резервированием, кэшем и таймаутами.
model_list:
- model_name: fast
litellm_params:
model: openai/gpt-4o-mini
api_key: os.environ/OPENAI_API_KEY
rpm: 500
- model_name: smart
litellm_params:
model: anthropic/claude-sonnet-4-5
api_key: os.environ/ANTHROPIC_API_KEY
- model_name: smart
litellm_params:
model: openai/gpt-4o
api_key: os.environ/OPENAI_API_KEY
litellm_settings:
drop_params: true
num_retries: 2
request_timeout: 600
cache: true
cache_params:
type: redis
ttl: 600
router_settings:
routing_strategy: usage-based-routing-v2
redis_url: os.environ/REDIS_URL
fallbacks: [{"smart": ["fast"]}]
allowed_fails: 3
cooldown_time: 60
- Алиасы. Клиент просит модель
smart, а что за ней стоит — решаете вы: смена провайдера это правка одной строки. - Две записи с одним
model_name— пул: LiteLLM балансирует между ними и уводит трафик с той, что начала отдавать ошибки. Идентификаторы сверяйте с документацией провайдера: устаревший ID дастNotFoundError. drop_params: trueвыбрасывает параметры, которых не понимает провайдер: без него один и тот же код на OpenAI работает, а на Anthropic падает. Рядомrequest_timeout: 600— длинные ответы идут дольше стандартных 60 секунд.cache: trueс Redis отдаёт повторы бесплатно: полезно на одинаковых системных промптах, вредно там, где нужна вариативность.
После правки конфига нужен docker compose restart litellm: автоперечитывания нет. Модели, добавленные через веб-интерфейс, живут в базе и подхватываются без рестарта.
Виртуальные ключи, команды и бюджеты
Мастер-ключ из .env — это root, им администрируют. Потребителям выпускают виртуальные:
curl -sS http://127.0.0.1:4000/key/generate \
-H "Authorization: Bearer $LITELLM_MASTER_KEY" \
-H "Content-Type: application/json" \
-d '{"models":["fast","smart"],"max_budget":20,"budget_duration":"30d",
"rpm_limit":60,"key_alias":"telegram-bot"}'
В ответ придёт {"key":"sk-1a2b...","key_alias":"telegram-bot"}, и дальше приложение работает с ним как с обычным ключом OpenAI:
curl -sS https://llm.example.com/v1/chat/completions \
-H "Authorization: Bearer sk-1a2b..." \
-H "Content-Type: application/json" \
-d '{"model":"smart","messages":[{"role":"user","content":"ping"}]}'
/key/info покажет остаток бюджета, /key/update поменяет лимиты на лету, /key/delete отзовёт скомпрометированный ключ за секунду — без перевыпуска ключа провайдера. Веб-интерфейс живёт на /ui (логин задают UI_USERNAME и UI_PASSWORD), общий бюджет на отдел — /team/new.
Деталь, о которой узнают через полгода: таблица LiteLLM_SpendLogs растёт на каждый запрос — на десяти запросах в секунду это сотни мегабайт в месяц. Либо чистите по расписанию:
docker compose exec postgres psql -U litellm -d litellm -c \
"DELETE FROM \"LiteLLM_SpendLogs\" WHERE \"startTime\" < NOW() - INTERVAL '30 days';"
либо отключите их параметром disable_spend_logs: true в general_settings — агрегаты по ключам сохранятся.
HTTPS, стриминг и эксплуатация
Наружу шлюз выставляем через Nginx. Конфиг обычный, но с двумя обязательными строками:
server {
listen 443 ssl http2;
server_name llm.example.com;
ssl_certificate /etc/letsencrypt/live/llm.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/llm.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:4000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_buffering off;
proxy_read_timeout 600s;
}
}
proxy_buffering off — та самая строка, без которой «ломается стриминг»: обычные запросы работают, а при "stream": true клиент молча висит и получает весь ответ одним куском, потому что Nginx копил SSE-чанки. Второе — proxy_read_timeout 600s: со стандартными 60 секундами генерация обрывается с 504 Gateway Time-out ровно на минуте. Сертификат ставит certbot --nginx -d llm.example.com.
Бэкап. В базе ключи, бюджеты и расходы. Дамп в cron: docker compose exec -T postgres pg_dump -U litellm litellm | gzip > /var/backups/litellm-$(date +\%F).sql.gz. Храните рядом .env — без LITELLM_SALT_KEY дамп бесполезен.
Обновления. Тег main-latest двигается, фиксируйте версию (теги вида main-v1.77.3-stable): сначала pg_dump, потом docker compose pull && docker compose up -d и curl /health/readiness.
Задержка. Обработка в LiteLLM — единицы миллисекунд, остальное решает расстояние до провайдера. На VPS в Нью-Йорке разница между прямым вызовом API и вызовом через локальный шлюз в замерах curl -o /dev/null -s -w '%{time_total}\n' укладывалась в 8–15 мс. Шлюз в России плюс американский API — лишние 100–130 мс.
Если что-то не сходится — docker compose logs --tail=100 litellm и запуск с --detailed_debug. Типовые случаи разобраны в частых ошибках LiteLLM.
Какой сервер взять в MAATRIX под LiteLLM
Считаем честно. Контейнер LiteLLM с двумя воркерами держится в 500–700 МБ RSS, Postgres в простое — около 150 МБ, Redis — десятки мегабайт. CPU почти не нагружен: шлюз ждёт провайдера.
Минимум: 2 vCPU, 4 ГБ RAM, 40 ГБ NVMe. Хватает на связку «LiteLLM + Postgres + Redis + Nginx» и на 20–30 запросов в секунду. На 2 ГБ шлюз запустится только без базы: Postgres, Redis и пара воркеров под нагрузкой поймают OOM-killer.
Комфортно: 4 vCPU, 8 ГБ RAM, 80 ГБ NVMe. Когда шлюзом пользуется команда: кэш в Redis, spend-логи, веб-интерфейс, четыре воркера и запас на пики. Диск — под базу логов; расчёт памяти в материале сколько RAM нужно для LiteLLM Gateway.
Локация. Для шлюза к OpenAI, Anthropic и Google берите США: прямая связность и чистый IP без региональных блокировок. Если потребители в Европе и важно соседство с GDPR — подойдут UK (Лондон) или Франция, пинг до ЕС там 10–30 мс. Россия оправдана в одном случае: шлюз агрегирует российские модели и данные обязаны оставаться в РФ по 152-ФЗ.
Заказ занимает несколько минут: выбираете локацию и конфигурацию, отмечаете приложение LiteLLM — сервер приедет с поднятым Docker, останется положить свой config.yaml и ключи. Оплата — картами российских банков, по СБП, криптой или токеном MAAT; иностранная карта не нужна, даже если сервер в Нью-Йорке. Опишите профиль нагрузки — подберём конфигурацию без переплаты.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть LiteLLMОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Частые вопросы
Обязательно ли ставить Postgres?
Нет, LiteLLM запустится и без базы, читая модели из config.yaml. Но тогда не будет виртуальных ключей, бюджетов и учёта расходов.
Почему при stream: true ответ приходит одним куском?
Nginx буферизует SSE-поток: в location нужны proxy_buffering off и proxy_http_version 1.1, а proxy_read_timeout — 600s, иначе длинные генерации оборвутся на 504.
Можно ли поставить шлюз в России с зарубежными ключами?
Технически да, но запросы к OpenAI и Anthropic с российского IP отбиваются с unsupported_country_region_territory. Для них шлюз размещают в США или Европе.
Нужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.