04.09.2026

Как собрать AI-контур на VPS: подписки ChatGPT и Claude, OmniRoute, LiteLLM и разработка без VPN

server one
HOSTKEY

Обновляем систему и устанавливаем базовые пакеты

Мы вошли на VPS под пользователем root, поэтому в следующих командах sudo использовать не будем.

Начнём с обновления системы и установки нескольких пакетов, которые понадобятся нам дальше:

apt update && apt upgrade -y
apt install -y ca-certificates curl git gnupg jq unzip

После обновления проверим, установлены ли на сервере Docker и Docker Compose:

docker -v && docker compose version

Если Docker отсутствует, терминал вернёт ошибку примерно такого вида:

docker: command not found

Если Docker установлен, но не хватает Compose, первая команда покажет версию Docker, а вторая завершится ошибкой.

Виртуальные серверы в Европе, США и России

Широкая линейка серверов от недорогих до профессиональных.

Устанавливаем Docker и Docker Compose

Docker будем устанавливать из официального репозитория. Пакеты из стандартного репозитория Ubuntu могут отставать по версиям, а вместе с официальным репозиторием мы сразу получим Docker Engine, Buildx и современный Compose Plugin.

Сначала добавляем официальный GPG-ключ Docker:

apt update
apt install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
 -o /etc/apt/keyrings/docker.asc

chmod a+r /etc/apt/keyrings/docker.asc

Теперь подключаем официальный репозиторий Docker:

tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

Обновляем список доступных пакетов:

apt update

И устанавливаем Docker Engine вместе с необходимыми дополнениями:

apt install -y \
 docker-ce \
 docker-ce-cli \
 containerd.io \
 docker-buildx-plugin \
 docker-compose-plugin

Включаем автоматический запуск Docker вместе с системой и сразу запускаем сервис:

systemctl enable --now docker

Повторно проверяем версии:

docker -v && docker compose version

Дополнительно запустим тестовый контейнер:

Устанавливаем Codex или Claude Code

Теперь установим на VPS AI-инструмент, с которым будем работать непосредственно во время разработки.

Здесь выбирайте вариант в зависимости от имеющейся подписки:

  • для подписки ChatGPT устанавливаем Codex;
  • для подписки Claude устанавливаем Claude Code;
  • если есть обе подписки, можно установить оба инструмента — друг другу они не мешают.

Устанавливать и авторизовывать CLI нужно под тем же пользователем, под которым мы будем работать через VS Code. В нашем случае это root. Данные авторизации сохраняются в домашней директории пользователя, поэтому после перехода на другого пользователя вход пришлось бы выполнять повторно.

Вариант 1. Устанавливаем Codex

Для Linux OpenAI рекомендует нативный установщик Codex CLI:

curl -fsSL https://chatgpt.com/codex/install.sh | sh

Проверяем, что команда появилась в системе:

codex --version

Поскольку мы работаем на удалённом VPS без графического интерфейса, обычная браузерная авторизация может не получить обратный ответ от локального браузера. Поэтому используем вход по коду устройства, но перед этим убедитесь, что в вашем аккаунте разрешен вход по коду устройства:

Далее вводим:

codex

Затем выбираем вход по коду устройства:

Codex покажет ссылку и одноразовый код. Открываем ссылку в браузере на своём компьютере, входим в аккаунт ChatGPT с активной подпиской и вводим полученный код.

После успешной авторизации создадим тестовую рабочую директорию:

mkdir -p ~/ai-workspace/test
cd ~/ai-workspace/test

Запускаем Codex:

codex

При первом запуске Codex может попросить подтвердить доверие к текущей директории. Подтверждаем и отправляем простое тестовое сообщение:

Привет, ты тут? Кто ты?

Если Codex ответил, значит установка завершена, подписка подхватилась, а обращения к моделям с зарубежного IP работают.

Чтобы завершить работу с Codex, нажимаем Ctrl+C или вводим команду выхода.

Вариант 2. Устанавливаем Claude Code

Для Claude Code Anthropic также рекомендует нативный установщик:

curl -fsSL https://claude.ai/install.sh | bash

Проверяем установленную версию:

claude --version

Если команда вывела номер версии Claude Code, запускаем клиент:

claude

При первом запуске Claude Code предложит авторизоваться. Для этого потребуется подписка Claude Pro, Max, Team или Enterprise. Бесплатный аккаунт Claude.ai доступ к Claude Code не предоставляет.

Поскольку мы запускаем Claude Code внутри SSH-сессии, браузер на сервере автоматически не откроется. Если это произойдёт, нажимаем c, копируем ссылку авторизации и открываем её в обычном браузере на своём компьютере.

После входа браузер либо автоматически завершит авторизацию, либо покажет код, который нужно вставить обратно в терминал. При успешном входе Claude Code выведет сообщение:

Login successful

После входа отправляем то же тестовое сообщение:

Привет, ты тут? Кто ты?

Если Claude Code ответил, значит клиент установлен, подписка подключена и можно переходить к нормальной работе.

На этом этапе у нас уже есть всё необходимое: зарубежный VPS, вход по короткому SSH-алиасу, Docker и AI-инструмент, авторизованный через нашу подписку.

Работать с сервером через обычный терминал уже можно, но постоянно редактировать файлы командами nano или vim, вручную переключаться между директориями и держать несколько SSH-окон не слишком удобно. Поэтому дальше подключим VPS к VS Code через расширение Remote SSH и превратим сервер в полноценную среду разработки, которая визуально почти не отличается от локальной.

Подключаемся к VPS через VS Code Remote SSH

Отлично. Надеюсь, что к этому моменту на вашем сервере уже появился доступ к Codex или Claude Code — в зависимости от того, какой сервис вы выбрали. А возможно, вы установили сразу оба варианта, что ещё лучше.

Работать с AI-ассистентом через обычный SSH-терминал уже можно, но для повседневной разработки этого всё-таки мало. Хочется видеть дерево проекта, открывать несколько файлов одновременно, пользоваться поиском, Git и остальными привычными инструментами.

Поэтому сейчас мы настроим полноценную среду для удалённой разработки:

  • интерфейс VS Code будет работать на нашем компьютере;
  • файлы проекта будут храниться на VPS;
  • команды, терминал, Codex и Claude Code будут запускаться на VPS;
  • обмен данными с AI-сервисами также будет происходить со стороны сервера.

В результате мы получим привычный редактор на локальном компьютере, но фактически будем работать внутри удалённого сервера.

Устанавливаем Remote — SSH

Сначала устанавливаем Visual Studio Code, если его ещё нет на вашем компьютере.

После этого открываем раздел расширений и находим Remote — SSH. Обратите внимание на издателя расширения: это должна быть компания Microsoft.

Устанавливаем расширение и нажимаем F1. На некоторых ноутбуках потребуется сочетание Fn + F1. Также палитру команд можно открыть через Ctrl + Shift + P в Windows и Linux или Cmd + Shift + P в macOS.

В появившейся строке начинаем вводить:

Remote-SSH: Connect to Host...

Выбираем найденную команду. Именно такой порядок подключения описан в официальной документации VS Code Remote SSH.

В выпадающем списке должен появиться SSH-алиас, который мы настроили ранее. В нашем случае это:

ai_vps_llm

Выбираем его.

VS Code откроет новое окно и подключится к серверу. Убедиться в этом можно по индикатору в левом нижнем углу: там должна появиться надпись примерно такого вида:

SSH: ai_vps_llm

Если VS Code спросит, доверяете ли вы содержимому сервера, подтверждаем доверие — разумеется, только если это действительно наш VPS.

Теперь нажимаем Open Folder. Вместо стандартного окна выбора папки на локальном компьютере VS Code предложит указать путь на удалённом сервере.

Выбираем тестовую папку, которую создали ранее. Например:

/root/ai-workspace/test

После открытия папки мы можем редактировать находящиеся на VPS файлы так, будто они лежат на нашем компьютере. При этом сохраняться все изменения будут непосредственно на сервере.

Теперь у нас есть два варианта работы с Codex и Claude Code:

  1. Через встроенный терминал VS Code.
  2. Через официальные расширения для VS Code.

Рассмотрим оба.

Вариант 1. Работаем через встроенный терминал

Для первого варианта у нас уже всё готово.

В верхнем меню VS Code выбираем:

Terminal → New Terminal

Откроется терминал удалённого сервера. При желании можно проверить текущую папку:

pwd

После этого запускаем нужный инструмент. Для Codex:

codex

Для Claude Code:

claude

На этом всё — можно ставить задачу, просить AI-ассистента изучать файлы проекта, писать код, запускать команды и проверять результат.

Главное здесь то, что сам процесс codex или claude работает не на нашем компьютере, а на VPS. Соответственно, запросы к сервису отправляются с IP-адреса сервера.

Локальный IP-адрес компьютера в этой схеме не участвует в подключении AI-ассистента к провайдеру. При этом использование сервиса, конечно, должно соответствовать правилам выбранного провайдера, условиям подписки и законодательству вашей страны: наличие VPS само по себе эти требования не отменяет.

Вариант 2. Используем расширение VS Code

Работа через терминал — простой и надёжный вариант. Но Codex и Claude Code также можно открыть в виде отдельной панели внутри VS Code.

Здесь есть важный нюанс. Расширение необходимо устанавливать именно в удалённую среду, к которой мы подключились по SSH.

Когда открыта удалённая сессия, в разделе расширений VS Code может показывать отдельную кнопку:

Install in SSH: ai_vps_llm

После установки расширение должно появиться в группе примерно с таким названием:

SSH: ai_vps_llm — Installed

Ищем и устанавливаем нужные расширения:

  • официальное расширение Codex от OpenAI;
  • официальное расширение Claude Code от Anthropic.

Codex

Claude Code

После установки в интерфейсе VS Code появятся дополнительные значки для открытия Codex и Claude Code.

Покажу дальнейшую работу на примере Codex.

Поскольку ранее мы уже авторизовались в Codex CLI на этом же сервере и под тем же пользователем, расширение обычно сможет использовать сохранённые данные авторизации. CLI и расширение Codex совместно используют данные входа на одном хосте — это отдельно описано в документации OpenAI по авторизации Codex.

Если окно входа всё-таки появится, выбираем авторизацию через ChatGPT и завершаем её в браузере.

Открыть панель можно через значок Codex либо через палитру команд:

Codex: Open Codex Sidebar

После этого открываем проект, формулируем задачу и начинаем работать. Codex будет видеть файлы удалённой папки и сможет предлагать или вносить изменения в проект. Подробнее эта схема описана в официальной документации Codex для IDE.

С Claude Code логика работы будет похожей: открываем его панель, передаём задачу и работаем с файлами проекта. Однако расширение Claude Code содержит собственный компонент для работы с ассистентом и при первом запуске может отдельно запросить вход через браузер — даже если до этого мы уже авторизовались в терминальном клиенте. Это нормальное поведение, описанное в документации Anthropic.

На этом настройку удалённой среды разработки можно считать законченной. Дальше подключаем Git, GitHub или GitLab и работаем с проектом практически так же, как на локальном компьютере.

Только учитывайте, что Git-команды теперь тоже выполняются на VPS. Поэтому SSH-ключи или другие данные для доступа к приватным репозиториям нужно будет отдельно настроить на сервере. Они не копируются с локального компьютера автоматически.

От удалённой разработки — к собственному LLM-шлюзу

Мы получили удобную среду, в которой можно запускать Codex и Claude Code на VPS, редактируя файлы через привычный интерфейс VS Code.

Но пока эта схема решает в основном одну задачу — персональную разработку с помощью CLI или расширений.

Для интеграции нейросетей в приложения этого уже недостаточно. Нам потребуется единая точка входа, через которую смогут работать:

  • собственные приложения;
  • Telegram-боты;
  • AI-агенты;
  • фоновые скрипты;
  • внутренние сервисы;
  • инструменты автоматизации;
  • другие разработчики или члены команды.

При этом хочется не прописывать отдельные адреса и ключи для каждого AI-провайдера, а централизованно управлять моделями, доступом, лимитами и расходами.

Для решения этой задачи мы развернём на VPS собственный LLM-шлюз на базе LiteLLM.

Что такое LiteLLM

LiteLLM — это инструмент, который позволяет обращаться к большому количеству AI-провайдеров через единый OpenAI-совместимый интерфейс.

LiteLLM можно использовать как библиотеку внутри Python-проекта, но нас в рамках статьи интересует другой режим — LiteLLM Proxy, который также называют AI Gateway.

В этом режиме LiteLLM запускается как отдельный сервис и становится промежуточным слоем между нашими приложениями и поставщиками моделей.

Упрощённо схема выглядит так:

Приложение → LiteLLM на VPS → OpenAI, Anthropic, Gemini или другой провайдер

Приложению больше не нужно знать, к какому именно провайдеру оно обращается. Оно отправляет стандартный OpenAI-совместимый запрос на наш сервер, а LiteLLM определяет, куда его направить. Сама отправка запроса, как вы поняли, будет выполнена с зарубежного IP-адреса, а, следовательно, мы так обойдем региональные ограничения доступа.

Например, со стороны приложения мы указываем:

  • адрес нашего LiteLLM;
  • выданный нами ключ;
  • условное имя модели.

Внутри LiteLLM это имя можно связать с конкретной моделью OpenAI, Anthropic, Gemini, локальным сервером или другим OpenAI-совместимым API.

Зачем нам нужен LiteLLM

Во-первых, мы получаем единую точку входа. Вместо нескольких разных API можно использовать один адрес:

https://наш-домен/v1

Во-вторых, LiteLLM позволяет хранить ключи провайдеров централизованно. Приложениям не обязательно передавать настоящий ключ OpenAI или другого сервиса — вместо него можно создать отдельный виртуальный ключ для конкретного проекта или пользователя.

В-третьих, через LiteLLM можно:

  • назначать отдельные ключи приложениям и пользователям;
  • устанавливать бюджеты и ограничения;
  • отслеживать расходы;
  • собирать логи запросов;
  • создавать понятные названия и алиасы моделей;
  • переключать приложение между провайдерами без изменения его кода;
  • настраивать повторные попытки и резервные модели;
  • распределять запросы между несколькими моделями или провайдерами;
  • управлять всем этим через одну конфигурацию.

Например, сегодня наш внутренний сервис может обращаться к модели OpenAI, а завтра мы переключим тот же алиас на другую модель. Если формат ответа остаётся OpenAI-совместимым, код приложения менять не потребуется.

Чего LiteLLM не делает

Здесь важно сразу провести границу.

LiteLLM не предоставляет собственные модели, не создаёт бесплатные токены и не превращает подписку ChatGPT или Claude в официальный API-ключ.

Если мы подключаем к LiteLLM официальный API-ключ OpenAI, запросы оплачиваются по правилам OpenAI API. То же самое относится к другим провайдерам.

LiteLLM в нашей схеме отвечает за маршрутизацию и централизованное управление. Способ подключения конкретного источника моделей — это уже отдельный слой.

Именно поэтому позднее нам понадобится OmniRoute. Он будет отвечать за работу с авторизацией подписочных сервисов, а LiteLLM станет центральным шлюзом, через который мы объединим доступные модели в один OpenAI-совместимый API.

Проще говоря:

  • OmniRoute подключает подписочные источники;
  • LiteLLM объединяет источники и управляет доступом к ним;
  • наши приложения работают с одним адресом и одним понятным протоколом.

Далее мы развернём LiteLLM на VPS, подключим к нему официальный API-ключ OpenAI и проверим первый запрос через собственный OpenAI-совместимый endpoint.

Привязываем домен к VPS

Прежде чем поднимать LiteLLM и OmniRoute, привяжем к серверу доменные имена. Технически оба сервиса можно открыть и по IP-адресу, но тогда придётся либо работать по обычному HTTP, либо отдельно бороться с сертификатами. Домен сразу даёт нам нормальный HTTPS, понятные адреса для API и аккуратные конфигурации клиентов.

Я использую домен, зарегистрированный в REG.RU, но регистратор здесь вообще не важен. Подойдёт любой сервис, в котором можно управлять DNS-записями. Более того, покупать новый домен необязательно: можно создать два поддомена у уже существующего.

В примере будут использоваться два адреса:

  • litellm-hk.yakvenalex.ru — веб-интерфейс и единый API LiteLLM;
  • omni-hk.yakvenalex.ru — веб-интерфейс и API OmniRoute.

В панели управления DNS создаём для каждого поддомена A-запись и указываем в ней публичный IP нашего VPS. Если используется корневой домен без поддомена, логика та же: A-запись должна вести на сервер.

Проверить, что записи уже обновились, можно прямо с сервера:

dig +short litellm-hk.yakvenalex.ru
dig +short omni-hk.yakvenalex.ru

Обе команды должны вернуть IP нашего VPS. Обновление DNS иногда занимает несколько минут, а в отдельных случаях — несколько часов. Пока адрес указывает не на тот сервер, переходить к выпуску сертификата рано.

Разворачиваем LiteLLM

Теперь развернём LiteLLM Proxy за nginx и HTTPS. В результате получится следующий стек: LiteLLM принимает OpenAI-совместимые запросы и даёт веб-админку, Postgres хранит модели и виртуальные ключи, nginx принимает внешний трафик, а certbot выпускает и автоматически продлевает сертификат Let's Encrypt.

Установим необходимые пакеты:

sudo apt update
sudo apt install -y nginx certbot python3-certbot-nginx openssl dnsutils

Создаём отдельную папку проекта:

sudo mkdir -p /opt/my_projects/lite_llm
cd /opt/my_projects/lite_llm
  1. Готовим секреты в .env

Секреты не будем размазывать по Docker-конфигу. Они будут лежать в отдельном файле .env, который не должен попадать в Git.

Сначала сгенерируем четыре случайных значения:

openssl rand -hex 24
openssl rand -hex 24
openssl rand -hex 24
openssl rand -hex 24

Одно значение используем для пароля админки, второе — для master key, третье — для salt key, четвёртое — для Postgres. Создаём файл .env:

# Вход в веб-интерфейс
UI_USERNAME=admin
UI_PASSWORD=<случайный_пароль>

# Root-ключ LiteLLM. Значение должно начинаться с sk-
LITELLM_MASTER_KEY=sk-<случайная_строка>

# Ключ шифрования токенов моделей
LITELLM_SALT_KEY=sk-<случайная_строка>

# Postgres
POSTGRES_USER=litellm
POSTGRES_PASSWORD=<случайный_пароль>
POSTGRES_DB=litellm

Закрываем доступ к файлу для остальных пользователей и добавляем его в .gitignore:

chmod 600 .env
printf '.env\n' > .gitignore

Важно. LITELLM_SALT_KEY нельзя менять после добавления моделей. LiteLLM шифрует им учётные данные провайдеров в базе. Если заменить ключ, сохранённые токены перестанут расшифровываться.

2. Создаём config.yaml

В минимальном конфиге включаем хранение моделей в Postgres:

general_settings:
 store_model_in_db: true
 store_prompts_in_spend_logs: false

litellm_settings:
 drop_params: true
 set_verbose: false

Параметр store_model_in_db позволяет добавлять модели через веб-интерфейс и не терять их после перезапуска. store_prompts_in_spend_logs: false нужен, чтобы по умолчанию не сохранять тексты промптов в логах расходов. drop_params: true отбрасывает параметры, которые конкретный провайдер не поддерживает.

3. Создаём docker-compose.yml

Поднимем два контейнера. LiteLLM будет запускаться только после того, как Postgres пройдёт healthcheck. Порт 4000 привязываем к 127.0.0.1: снаружи к нему будет обращаться только nginx.

services:
 litellm:
   image: ghcr.io/berriai/litellm:main-stable
   container_name: litellm
   restart: unless-stopped
   ports:
     - "127.0.0.1:4000:4000"
   volumes:
     - ./config.yaml:/app/config.yaml:ro
   command: ["--config", "/app/config.yaml", "--port", "4000"]
   environment:
     LITELLM_MASTER_KEY: ${LITELLM_MASTER_KEY}
     LITELLM_SALT_KEY: ${LITELLM_SALT_KEY}
     UI_USERNAME: ${UI_USERNAME}
     UI_PASSWORD: ${UI_PASSWORD}
     DATABASE_URL: postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
     STORE_MODEL_IN_DB: "True"
   depends_on:
     postgres:
       condition: service_healthy
   healthcheck:
     test: ["CMD-SHELL", "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:4000/health/liveliness')\" || exit 1"]
     interval: 30s
     timeout: 10s
     retries: 5
     start_period: 40s

 postgres:
   image: postgres:16-alpine
   container_name: litellm-db
   restart: unless-stopped
   environment:
     POSTGRES_USER: ${POSTGRES_USER}
     POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
     POSTGRES_DB: ${POSTGRES_DB}
   volumes:
     - litellm_pg_data:/var/lib/postgresql/data
   healthcheck:
     test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
     interval: 10s
     timeout: 5s
     retries: 5

volumes:
 litellm_pg_data:

Запускаем сервисы и проверяем их состояние:

docker compose up -d
docker compose ps
docker compose logs --tail=100 litellm

В списке должны быть два запущенных контейнера: litellm и litellm-db. Если LiteLLM ещё имеет статус starting, подождите полминуты и повторите docker compose ps.

4. Прячем LiteLLM за nginx

Создаём конфигурацию /etc/nginx/sites-available/litellm:

server {
   listen 80;
   listen [::]:80;
   server_name litellm-hk.yakvenalex.ru;

   client_max_body_size 50m;

   location / {
       proxy_pass http://127.0.0.1:4000;
       proxy_http_version 1.1;
       proxy_set_header Host $host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
       proxy_buffering off;
       proxy_cache off;
       proxy_read_timeout 3600s;
       proxy_send_timeout 3600s;
   }
}

Включаем сайт и перечитываем конфигурацию:

sudo ln -s /etc/nginx/sites-available/litellm /etc/nginx/sites-enabled/litellm
sudo nginx -t
sudo systemctl reload nginx

proxy_buffering off особенно важен для потоковых ответов: без него nginx может накапливать части ответа вместо того, чтобы сразу отдавать их клиенту.

5. Выпускаем HTTPS-сертификат

На этом этапе A-запись уже должна указывать на VPS, а порты 80 и 443 должны быть открыты. Выпускаем сертификат Let's Encrypt:

sudo certbot --nginx -d litellm-hk.yakvenalex.ru \
  --non-interactive --agree-tos -m you@example.com --redirect

Замените домен и электронную почту на свои. Certbot сам дополнит nginx-конфиг, включит редирект с HTTP на HTTPS и создаст таймер автопродления.

Проверяем таймер и тестовый выпуск сертификата:

systemctl status certbot.timer --no-pager
sudo certbot renew --dry-run

Добавляем первую модель в LiteLLM

Открываем веб-интерфейс:

У меня это: https://litellm-hk.yakvenalex.ru/ui/

Входим под UI_USERNAME и UI_PASSWORD из файла .env.

Переходим в Models → Add Model. LiteLLM поддерживает множество провайдеров, но сначала подключим обычный официальный API OpenAI.

Заполняем поля:

  • Provider — OpenAI;
  • Model — модель, доступная вашему API-проекту; в моём примере gpt-5.4-nano;
  • Mode — оставляем значение по умолчанию;
  • OpenAI API Key — официальный API-ключ OpenAI;
  • API Base — оставляем стандартным.

Сначала нажимаем Test Connect. Если тест прошёл успешно, добавляем модель кнопкой Add Model.

Проверяем API

Для первой отладки можно выполнить запрос с master key. Сам ключ в статью и на скриншоты, разумеется, не вставляю:

curl https://litellm-hk.yakvenalex.ru/v1/chat/completions \
 -H "Authorization: Bearer sk-<master-key>" \
 -H "Content-Type: application/json" \
 -d '{
   "model": "gpt-5.4-nano",
   "messages": [
     {"role": "system", "content": "Ты дружелюбный ассистент. Отвечай кратко."},
     {"role": "user", "content": "Привет! Назови три факта о Луне."}
   ]
 }'

Важно. Master key даёт административный доступ к прокси. Он подходит для короткой проверки, но его нельзя раздавать приложениям и хранить в клиентском коде.

Создаём виртуальный ключ

Переходим в Virtual Keys или API Keys и нажимаем Create New Key. Даём ключу понятное имя, ограничиваем его нужной моделью и при необходимости задаём бюджет и срок действия.

После создания LiteLLM покажет значение sk-.... Копируем его сразу и храним как обычный секрет. Теперь повторяем запрос уже с виртуальным ключом:

curl https://litellm-hk.yakvenalex.ru/v1/chat/completions \
 -H "Authorization: Bearer sk-<virtual-key>" \
 -H "Content-Type: application/json" \
 -d '{
   "model": "gpt-5.4-nano",
   "messages": [
     {"role": "user", "content": "Привет! Назови три факта о Луне."}
   ]
 }'

Вызов работает так же, но теперь его можно отозвать независимо от остальных клиентов. А в разделе Usage появятся запрос, использованная модель, количество токенов, задержка и стоимость — если для модели доступен корректный прайсинг.

Итак, официальный API OpenAI уже доступен через наш домен и единый виртуальный ключ. Но это пока только половина контура: официальный API оплачивается отдельно и никак не связан с лимитами подписки ChatGPT или Claude. Для подписочных источников поднимем второй сервис — OmniRoute.

Поднимаем OmniRoute

Внимание! Это важно. Мы переходим к методам, которые не являются официальными. Все действия с подпиской вы выполняете на свой страх и риск. Этот способ не одобрен, и он может привести к блокировке аккаунта.

OmniRoute — self-hosted AI-шлюз с веб-интерфейсом и OpenAI-совместимым API. Он умеет подключать провайдеров по обычным API-ключам и через OAuth, хранить несколько подключений, следить за квотами, автоматически обновлять OAuth-токены и отдавать модели через один endpoint.

Важно. OmniRoute не извлекает из подписки официальный API-ключ Anthropic или OpenAI. Мы авторизуем в шлюзе собственную подписочную учётную запись, а затем создаём локальный ключ OmniRoute для доступа к этому шлюзу. Лимиты, правила использования и условия провайдера при этом никуда не исчезают. Такой доступ не стоит выдавать третьим лицам или использовать как основу публичного коммерческого API без отдельной проверки условий сервиса.

В нашем случае схема будет простой:

  1. Подписка Claude Code или ChatGPT остаётся у провайдера.
  2. OmniRoute хранит OAuth-авторизацию и обновляет её токены.
  3. Мы создаём собственный клиентский ключ OmniRoute.
  4. Клиент вызывает https://omni-hk.yakvenalex.ru/v1 в OpenAI-совместимом формате.
  5. При желании LiteLLM ставится поверх OmniRoute и становится единой точкой доступа ко всем источникам.

Исходный код и актуальная документация проекта находятся в репозитории OmniRoute. В примере используем официальный Docker-образ diegosouzapw/omniroute:latest.

1. Создаём каталог и .env

sudo mkdir -p /opt/my_projects/omni/data
cd /opt/my_projects/omni
sudo chown -R 1000:1000 data

OmniRoute хранит SQLite-базу, настройки и зашифрованные подключения в /app/data. На хосте это будет папка ./data. Владелец UID 1000 нужен контейнеру для записи.

Генерируем несколько независимых секретов:

openssl rand -hex 32
openssl rand -hex 32
openssl rand -hex 32
openssl rand -hex 32
openssl rand -hex 32

Создаём .env и подставляем разные значения в каждое поле:

# Первый вход в dashboard. После запуска пароль меняем в интерфейсе
INITIAL_PASSWORD=<сложный_первичный_пароль>

# Секреты приложения. Не использовать одно значение повторно
JWT_SECRET=<случайная_строка_1>
API_KEY_SECRET=<случайная_строка_2>
STORAGE_ENCRYPTION_KEY=<случайная_строка_3>
STORAGE_ENCRYPTION_KEY_VERSION=v1
MACHINE_ID_SALT=<случайная_строка_4>
OMNIROUTE_WS_BRIDGE_SECRET=<случайная_строка_5>

# Приложение и хранилище
PORT=20128
NODE_ENV=production
HOSTNAME=0.0.0.0
DATA_DIR=/app/data
STORAGE_DRIVER=sqlite
APP_LOG_TO_FILE=true
AUTH_COOKIE_SECURE=true
REQUIRE_API_KEY=true

# Публичный адрес
BASE_URL=https://omni-hk.yakvenalex.ru
NEXT_PUBLIC_BASE_URL=https://omni-hk.yakvenalex.ru

# Rate limiter и кэш
REDIS_URL=redis://redis:6379
chmod 600 .env
printf '.env\ndata/\n' > .gitignore

Важно. JWT_SECRET, API_KEY_SECRET, STORAGE_ENCRYPTION_KEY и MACHINE_ID_SALT после начала работы не меняем без процедуры миграции. Иначе можно потерять активные сессии или возможность расшифровать сохранённые подключения. Для восстановления понадобятся и папка data, и исходный .env.

2. Создаём docker-compose.yml

В базовом сценарии OmniRoute использует один основной HTTP-порт 20128: через него работают и dashboard, и API /v1. Мы публикуем его только наlocalhost. Redis вообще не получает внешнего порта.

services:
 omniroute:
   image: diegosouzapw/omniroute:latest
   container_name: omniroute
   restart: unless-stopped
   stop_grace_period: 40s
   env_file: .env
   depends_on:
     redis:
       condition: service_healthy
   ports:
     - "127.0.0.1:20128:20128"
   volumes:
     - ./data:/app/data
   healthcheck:
     test: ["CMD", "node", "healthcheck.mjs"]
     interval: 30s
     timeout: 5s
     retries: 3
     start_period: 20s

 redis:
   image: redis:7-alpine
   container_name: omniroute-redis
   restart: unless-stopped
   command: redis-server --save 60 1 --loglevel warning
   volumes:
     - redis-data:/data
   healthcheck:
     test: ["CMD", "redis-cli", "ping"]
     interval: 10s
     timeout: 5s
     retries: 3

volumes:
 redis-data:
   name: omniroute-redis-data

stop_grace_period: 40s здесь не декоративный параметр. OmniRoute использует SQLite в WAL-режиме, поэтому контейнеру нужно дать время корректно завершить запись перед остановкой.

Запускаем и проверяем:

docker compose up -d
docker compose ps
docker compose logs --tail=100 omniroute
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:20128/

Корневая страница может ответить 200 или перенаправлением на dashboard. Главное, чтобы контейнер был healthy, а в логах не было ошибок доступа к SQLite.

3. Настраиваем nginx и HTTPS

Создаём /etc/nginx/sites-available/omniroute:

server {
   listen 80;
   listen [::]:80;
   server_name omni-hk.yakvenalex.ru;

   client_max_body_size 100m;

   location / {
       proxy_pass http://127.0.0.1:20128;
       proxy_http_version 1.1;
       proxy_set_header Host $host;
       proxy_set_header X-Real-IP $remote_addr;
       proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       proxy_set_header X-Forwarded-Proto $scheme;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection "upgrade";
       proxy_buffering off;
       proxy_cache off;
       proxy_read_timeout 600s;
       proxy_send_timeout 600s;
   }
}
sudo ln -s /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
sudo nginx -t
sudo systemctl reload nginx

Выпускаем сертификат:

sudo certbot --nginx -d omni-hk.yakvenalex.ru \
  --non-interactive --agree-tos -m you@example.com --redirect

После этого открываем:

У меня: https://omni-hk.yakvenalex.ru

Первый вход и подключение Claude Code

На странице входа вводим INITIAL_PASSWORD из .env. После первого входа сразу переходим в настройки и меняем пароль на новый.

Теперь открываем раздел Providers. В текущей версии он доступен из левого меню; прямой адрес выглядит так:

https://omni-hk.yakvenalex.ru/dashboard/providers

В списке много вариантов: обычные провайдеры с API-ключом, бесплатные тарифы сторонних сервисов и OAuth-подключения инструментов разработки. Нас интересует Claude Code, поэтому выбираем его и нажимаем Add Connection.

OmniRoute сформирует OAuth-ссылку. Открываем её, входим в собственную учётную запись Anthropic и подтверждаем доступ. Если страница провайдера недоступна из вашей текущей сети, этап авторизации придётся пройти из сети, в которой она открывается. После подключения обычные запросы уже будет отправлять VPS.

При удалённом развёртывании OAuth иногда завершается не автоматическим возвратом в dashboard, а страницей с callback-адресом. В этом случае копируем полный URL из адресной строки — вместе с параметрами code и state — и вставляем его в поле ручного завершения авторизации в OmniRoute.

После успешного подключения OmniRoute покажет доступные для этой учётной записи модели и информацию о квоте. Имена моделей имеют префикс провайдера. Например:

cc/claude-opus-4-6

Конкретный список зависит от версии OmniRoute и моделей, доступных вашей подписке, поэтому копируйте имя непосредственно со страницы своего подключения.

Создаём клиентский ключ OmniRoute

Открываем раздел API Manager или Endpoints. В текущем интерфейсе страница управления ключами доступна по адресу:

https://omni-hk.yakvenalex.ru/dashboard/api-manager

Создаём новый ключ, даём ему понятное имя и копируем значение sk-.... Это и есть наш локальный ключ шлюза. Он не является официальным ключом Anthropic и действует только на нашем домене OmniRoute.

Проверяем OpenAI-совместимый endpoint:

curl -s -X POST "https://omni-hk.yakvenalex.ru/v1/chat/completions" \
 -H "Authorization: Bearer sk-<omniroute-key>" \
 -H "Content-Type: application/json" \
 -d '{
   "model": "cc/claude-opus-4-6",
   "messages": [
     {"role": "user", "content": "Привет, как дела?"}
   ],
   "stream": false
 }'

Если в ответ пришёл JSON с сообщением модели, связка подписка → OAuth → OmniRoute → OpenAI-совместимый API работает.

Подключаем OmniRoute к LiteLLM

Сейчас у нас уже есть два рабочих endpoint. LiteLLM обслуживает официальный API, а OmniRoute — подписочную авторизацию. Но смысл нашего контура как раз в том, чтобы клиентам не приходилось знать о двух разных адресах. Поэтому добавим OmniRoute в LiteLLM как OpenAI-совместимого провайдера.

В LiteLLM открываем Models → Add Model и заполняем поля:

  • Model Name — публичный алиас, например claude-opus-subscription;
  • Provider — OpenAI-Compatible;
  • Provider Model — openai/cc/claude-opus-4-6;
  • API Base — https://omni-hk.yakvenalex.ru/v1;
  • API Key — созданный клиентский ключ OmniRoute.

Префикс openai/ нужен LiteLLM, чтобы отправить запрос на совместимый endpoint через OpenAI-клиент. Пользователи при этом будут вызывать короткий публичный алиас claude-opus-subscription.

Нажимаем Test Connect, добавляем модель и проверяем уже единый endpoint LiteLLM:

curl https://litellm-hk.yakvenalex.ru/v1/chat/completions \
 -H "Authorization: Bearer sk-<litellm-virtual-key>" \
 -H "Content-Type: application/json" \
 -d '{
   "model": "claude-opus-subscription",
   "messages": [
     {"role": "user", "content": "Привет! Ответь одной строкой."}
   ]
 }'

Теперь приложение знает только адрес LiteLLM и свой виртуальный ключ. За этим адресом могут одновременно находиться официальный OpenAI API, Claude Code через OmniRoute и любые другие источники. Модель переключается значением model, а ключи, лимиты и логи централизованно управляются в LiteLLM.

Эксплуатация и резервные копии OmniRoute

Для повседневного управления достаточно нескольких команд:

cd /opt/my_projects/omni
docker compose ps
docker compose logs -f omniroute
docker compose restart omniroute
docker compose pull && docker compose up -d

Перед обновлением делаем резервную копию. Копировать нужно и данные, и .env; без исходных ключей шифрования одна SQLite-база может оказаться бесполезной.

cd /opt/my_projects/omni
docker compose stop omniroute
sudo tar czf /root/omniroute-backup-$(date +%F).tgz data .env docker-compose.yml
docker compose start omniroute

Самые частые проблемы выглядят так:

  • HTTP 500 и ошибки SQLite — проверьте права на ./data и владельца UID 1000;
  • после перезапуска разлогинивает — вероятно, изменился JWT_SECRET;
  • подключения перестали расшифровываться — проверьте STORAGE_ENCRYPTION_KEY и API_KEY_SECRET;
  • nginx отдаёт 502 — проверьте docker compose ps и логи omniroute;
  • не сохраняется cookie за HTTPS — проверьте AUTH_COOKIE_SECURE=true и X-Forwarded-Proto;
  • долгий ответ обрывается — увеличьте proxy_read_timeout и proxy_send_timeout.

Важно. Не публикуйте наружу порты 20128 и 6379. Для внешнего доступа достаточно nginx на 443. Файл .env не коммитим, master key LiteLLM не вставляем в приложения, а клиентские ключи отзываем сразу после утечки.

Что у нас получилось

К этому моменту VPS выполняет сразу три роли: служит удалённой средой разработки, принимает подписочные подключения через OmniRoute и отдаёт единый управляемый API через LiteLLM. Мы можем создавать отдельные ключи для приложений, ограничивать модели и бюджеты, а затем смотреть все вызовы в одном журнале.

Remote SSH удобен, когда проект действительно должен жить и выполняться на сервере. Но иногда хочется оставить исходники на своём компьютере и всё равно вайбкодить через собственный endpoint — без постоянного удалённого окна VS Code. В следующем разделе настроим локальный Qwen Code или OpenCode, укажем ему адрес нашего LiteLLM и проверим, как выглядит тот же рабочий процесс уже без Remote SSH.

Вайбкодинг на локальной машине через Qwen Code

До сих пор мы рассматривали два сценария. Сначала запускали Codex или Claude Code непосредственно на VPS, затем работали с тем же сервером через VS Code Remote SSH. Оба варианта удобны, но у них есть общее свойство: исходники и AI-инструмент живут на удалённой машине.

Теперь сделаем наоборот. Проект, Git, терминал и VS Code останутся на домашнем компьютере, а к моделям локальный агент будет обращаться через наш HTTPS-endpoint. VPN для самого инструмента при такой схеме не нужен: клиент соединяется с нашим доменом, а дальше запрос обрабатывает зарубежный VPS.

В качестве агента возьмём Qwen Code. По логике работы он близок к Claude Code и Codex: запускается в терминале, читает файлы проекта, предлагает изменения, выполняет команды с подтверждением и умеет работать из VS Code. При этом он не привязан только к моделям Qwen и позволяет подключить произвольный OpenAI-совместимый провайдер.

Тот же принцип работает и с OpenCode. Его короткую конфигурацию я покажу в конце раздела, но основную демонстрацию проведём в Qwen Code.

Устанавливаем Qwen Code

Актуальные варианты установки собраны в официальной инструкции Qwen Code. Выбирайте команду для своей платформы.

Linux и macOS, быстрый установщик:

curl -fsSL https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen-standalone.sh | bash

Windows PowerShell:

irm https://qwen-code-assets.oss-cn-hangzhou.aliyuncs.com/installation/install-qwen-standalone.ps1 | iex

После быстрого установщика перезапускаем терминал. Есть и универсальный вариант через npm, но для него нужен Node.js 22 или новее:

npm install -g @qwen-code/qwen-code@latest

Проверяем установку:

qwen --version

Подключаем собственный эндпоинт

Открываем терминал в папке локального проекта и запускаем Qwen Code:

cd /path/to/local/project
qwen

При первом запуске появится мастер авторизации. Если Qwen Code уже был настроен, тот же мастер можно открыть в любой момент командой /auth.

/auth

Выбираем Custom Provider, затем OpenAI-compatible. Этот вариант подходит и для Claude-моделей: значение имеет не компания-разработчик модели, а протокол endpoint, через который мы к ней обращаемся.

Далее мастер запросит Base URL. Для прямого подключения к OmniRoute вводим:

https://omni-hk.yakvenalex.ru/v1

На следующем шаге вставляем клиентский ключ OmniRoute, который создали в API Manager. Master key LiteLLM и секреты из .env здесь не нужны.

sk-<omniroute-client-key>

После этого Qwen Code предложит перечислить модели через запятую. Имена берём со страницы Providers или Endpoints в OmniRoute — вместе с префиксом cc/. Например:

cc/claude-sonnet-4-6,cc/<точное-имя-модели-2>,cc/<точное-имя-модели-3>

В моём случае я добавляю три модели. Не копируйте названия вслепую из статьи: OmniRoute и сами провайдеры обновляются, поэтому точный идентификатор лучше взять из собственного dashboard.

На следующих экранах можно включить reasoning, разрешить обработку изображений и указать размер контекстного окна. Эти параметры не делают модель мощнее сами по себе: включаем только те возможности, которые действительно поддерживают выбранная модель и наш upstream. Если сомневаетесь, сначала оставьте значения по умолчанию.

Выбираем модель и проверяем работу

После сохранения открываем переключатель моделей:

/model

В списке должны появиться все модели, которые мы только что добавили. Выбираем рабочую — в моём примере это cc/claude-sonnet-4-6.

Для первого теста не стоит сразу поручать агенту переписывать половину проекта. Попросим его сначала изучить репозиторий без изменений:

Изучи структуру проекта. Ничего не меняй. Коротко объясни, как он устроен, какие команды запускают приложение и тесты.

Если агент прочитал локальные файлы и вернул осмысленный ответ, проверяем изменение на небольшой задаче:

Добавь в README короткий раздел «Локальный запуск». Сначала покажи предлагаемый diff и меняй файл только после моего подтверждения.

Qwen Code должен показать изменение и запросить разрешение. Это важнее простого ответа «Привет»: мы проверяем всю цепочку — чтение локального проекта, вызов модели через OmniRoute, возврат tool call и применение изменения на домашней машине.

Если вместо прямого OmniRoute хочется сохранить централизованные ключи, бюджеты и логи LiteLLM, в мастере указываем другой набор параметров:

  • Base URL — https://litellm-hk.yakvenalex.ru/v1;
  • API key — виртуальный ключ LiteLLM;
  • Model — публичный алиас LiteLLM, например claude-opus-subscription.

Для повседневной работы это даже удобнее: Qwen Code знает только клиентский ключ LiteLLM, а реальный источник модели можно заменить на сервере, не перенастраивая локальную машину.

Работаем из локального VS Code

Если терминальный интерфейс не нравится, устанавливаем официальный плагин Qwen Code Companion. Делать это нужно в обычном локальном окне VS Code, а не в экземпляре, подключённом к VPS через Remote SSH.

Расширение доступно в Visual Studio Marketplace. Проверяем название Qwen Code Companion и издателя qwenlm.

Открываем панель по иконке Qwen или через палитру команд: Qwen Code: Open. Расширение использует локальную конфигурацию Qwen Code. Если ранее созданный провайдер не появился, запускаем Qwen Code: Run и повторяем /auth уже из встроенного терминала расширения.

Выбираем ту же модель cc/claude-sonnet-4-6 и повторяем короткую проверку. Теперь дифф, история и контекст открытых файлов отображаются прямо в интерфейсе VS Code.

Альтернатива: OpenCode

Если вам ближе OpenCode, архитектура не меняется. Он также поддерживает OpenAI-совместимых провайдеров. После установки запускаем /connect, выбираем Other, задаём идентификатор omniroute и сохраняем клиентский ключ.

/connect
# Provider: Other
# Provider ID: omniroute
# API key: sk-<omniroute-client-key>

Затем создаём в папке проекта opencode.json:
{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "omniroute": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "My OmniRoute",
      "options": {
        "baseURL": "https://omni-hk.yakvenalex.ru/v1"
      },
      "models": {
        "cc/claude-sonnet-4-6": {
          "name": "Claude Sonnet via OmniRoute"
        }
      }
    }
  }
}

Командой /models выбираем omniroute/cc/claude-sonnet-4-6. Чтобы пустить OpenCode через LiteLLM, достаточно заменить baseURL, сохранить виртуальный ключ LiteLLM под тем же provider ID и указать публичный алиас модели.

Важно. Локальный агент получает доступ к файлам и терминалу вашего компьютера. Не включайте безусловное автоматическое подтверждение команд в незнакомых репозиториях, не храните API-ключи в opencode.json и внимательно просматривайте diff перед применением.

Вместо заключения

В начале статьи у нас был один довольно бытовой вопрос: можно ли перестать перенастраивать VPN и прокси каждый раз, когда очередному AI-инструменту понадобился доступ к модели? Теперь ответ получился не теоретическим — мы собрали рабочую схему и прошли её от пустого VPS до локального AI-агента.

Если коротко, контур разделён на понятные слои:

  • VPS даёт стабильную зарубежную точку выхода и среду для удалённой разработки;
  • nginx и Let's Encrypt дают нормальные домены и HTTPS;
  • OmniRoute подключает OAuth-авторизацию подписочных сервисов и выдаёт локальный API;
  • LiteLLM объединяет источники, создаёт клиентские ключи, лимиты и логи;
  • Codex и Claude Code работают прямо на VPS через Remote SSH;
  • Qwen Code и OpenCode используют тот же контур с локального компьютера.

Главный плюс здесь даже не в том, что исчезает VPN. Важнее, что доступ к моделям перестаёт быть набором случайных настроек в пяти приложениях. У нас появляется одна точка управления: можно добавить новую модель, выдать отдельный ключ проекту, ограничить бюджет, посмотреть расход или отозвать скомпрометированный доступ, не трогая остальные приложения.

При этом важно не приписывать схеме того, чего она не делает. Подписка не превращается в официальный API-ключ, лимиты провайдера не исчезают, а правила использования продолжают действовать. OmniRoute и LiteLLM дают контроль над маршрутом и доступом, но не отменяют тарифы, квоты и ответственность за учётные данные.

Дальше этот контур можно развивать под себя: добавить резервные модели и fallback, разнести ключи по проектам, подключить мониторинг, настроить регулярные резервные копии или ограничить dashboard отдельной авторизацией. Но базовая система уже готова — и для ежедневной разработки, и для тестовых AI-интеграций.

Надеюсь, теперь вместо очередного набора костылей у вас появится инфраструктура, которую вы понимаете, контролируете и можете спокойно перестраивать под следующую модель или инструмент.

Виртуальные серверы в Европе, США и России

Широкая линейка серверов от недорогих до профессиональных.

Другие статьи

25.08.2026

JupyterLab на GPU-сервере: полное руководство по настройке для команды в 2026 году

Пошаговое руководство по развертыванию JupyterLab на GPU-сервере. Настройка JupyterHub, драйверов NVIDIA, безопасности (Nginx/SSL), лимитов ресурсов и мониторинга.

09.08.2026

NVIDIA RTX PRO 5000 Blackwell с 72 Гб видеопамяти. Есть ли смысл переплачивать за «половинку» флагмана?

RTX PRO 5000 Blackwell на 72 Гб: золотая середина для локальных ИИ-моделей или переоцененный апгрейд? Разбираемся в нашем обзоре.

01.08.2026

Большие модели и цена миллиона токенов

Китайские модели дешевле или это ловушка? Разбираемся, как не переплачивать за токены и почему цена в прайсе — еще не вся правда о расходах на ИИ.

20.07.2026

Внутренняя документация, которую никто не читает. Как сделать, чтобы читали (на примере ONLYOFFICE Workspace)

Документация умирает не от лени сотрудников, а из-за неудобства и потери доверия к данным. Разбираем «два кита» качественной базы знаний: удобство использования и контроль актуальности. Показываем на примере ONLYOFFICE Workspace, как превратить хаос в работающий процесс с помощью шаблонов, ролевой модели доступа и дисциплины пересмотра.

17.07.2026

Что такое AI-агенты: создание, примеры и возможности в 2026 году

Вокруг AI-агентов сейчас слишком много шума, но мало кто объясняет, почему без жестких лимитов они способны за ночь сжечь весь бюджет. Разбираем архитектуру, стандарт MCP и актуальные фреймворки 2026 года. Показываем рабочий код и честно говорим о скрытых расходах и рисках, о которых молчат в презентациях.

Upload