MAATRIX / Блог / Как установить и настроить Tabby на VPS

Как установить и настроить Tabby на VPS

Как установить и настроить Tabby на VPS

MAATRIX

GitHub Copilot и подобные ассистенты отправляют каждую строку кода на чужой сервер — для проекта под NDA или закрытым контрактом это часто прямой запрет, а платить за подписку в валюте из России — отдельная головная боль. Tabby — открытый self-hosted аналог, который живёт целиком на вашей инфраструктуре и отдаёт автодополнение кода прямо в VSCode или JetBrains. Разберём установку по шагам вместе с проблемой, о которую спотыкается почти каждый, кто ставит Tabby не на GPU-сервер, а на обычный VPS.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Что такое Tabby и зачем самому хостить автодополнение кода

Tabby (TabbyML) — открытый (Apache 2.0) self-hosted аналог GitHub Copilot на Rust: движок автодополнения кода, чат по контексту репозитория и поиск по кодовой базе в одном бинарнике. На момент публикации у проекта около 33,8 тысячи звёзд на GitHub и почти 3700 коммитов — не заброшенный форк, а сервис с релизами каждые несколько недель (последний стабильный — v0.32.0 от 25 января 2026 года, с генерик-OAuth и индексацией нескольких веток репозитория разом).

Три вещи отличают его от облачных ассистентов:

  • Код не покидает периметр. Запросы на автодополнение уходят на ваш сервер и обратно, а не в API OpenAI или Microsoft — важно, если контракт запрещает передавать исходники третьим лицам.
  • Не требует внешних сервисов. Пользователи, история и проиндексированные репозитории по умолчанию лежат в SQLite внутри одного контейнера — без обязательной внешней базы или облачной подписки.
  • OpenAPI-интерфейс. Тот же сервер отдаёт Swagger UI на /swagger-ui/ — им проверяют, что сервис жив, и через него же интегрируют внутренние скрипты.

Официальные расширения есть для VSCode, семейства JetBrains и Vim/Neovim; агент подключается к серверу по HTTP и требует Node.js не ниже версии 18.0.0 — на более старом рантайме расширение просто не стартует. Обратная сторона self-hosting очевидна: сервер и его обслуживание — на вас, включая не самый очевидный запуск на CPU, о котором дальше.

Почему готовая docker-команда падает на обычном VPS

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

docker run -d --name tabby --gpus all -p 8080:8080 -v $HOME/.tabby:/data \
  registry.tabbyml.com/tabbyml/tabby \
  serve --model StarCoder-1B --chat-model Qwen2-1.5B-Instruct --device cuda

Флаг --gpus all — не украшение: он требует NVIDIA Container Toolkit и, собственно, видеокарты в сервере. У большинства VPS её нет — GPU в аренде стоит заметно дороже CPU-тарифа, и для одного разработчика это избыточно. Логичный шаг — убрать --gpus all и заменить --device cuda на --device cpu:

docker run -d --name tabby -p 8080:8080 -v $HOME/.tabby:/data \
  registry.tabbyml.com/tabbyml/tabby \
  serve --model StarCoder-1B --device cpu

Именно это не работает. Контейнер стартует и тут же падает, а docker logs tabby показывает:

/opt/tabby/bin/tabby: error while loading shared libraries: libcuda.so.1: cannot open shared object file: No such file or directory

Причина в самом образе: точка входа по умолчанию — единый бинарник, слинкованный с библиотекой CUDA, а --device cpu меняет только то, на чём считает модель, а не то, какой файл запускается. Бинарник ищет libcuda.so.1 при старте процесса, ещё до разбора аргументов — и на сервере без NVIDIA-драйвера падает с этой ошибкой независимо от --device. Проблема не единичная: с ней сталкивались и на голом --device cpu (issue #2082 в репозитории проекта), и на связке --chat-device cpu с моделью для эмбеддингов (issue #4229, где тот же libcuda.so.1 валит дочерний процесс llama-server с кодом выхода 127). Запрос добавить отдельный CPU-тег образа или переменную CPU_ONLY=true (issue #4377) мейнтейнеры закрыли пометкой «not planned» — решать вопрос приходится на своей стороне.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Установка на CPU-сервере: обход бага с libcuda.so.1

Рабочий обход есть, и пересобирать образ не нужно. Внутри того же контейнера лежит второй бинарник — CPU-сборка без линковки с CUDA, /opt/tabby/bin/tabby-cpu. Достаточно подменить точку входа:

docker run -d --name tabby --restart unless-stopped \
  -p 127.0.0.1:8080:8080 \
  -v /opt/tabby/data:/data \
  --entrypoint /opt/tabby/bin/tabby-cpu \
  registry.tabbyml.com/tabbyml/tabby \
  serve --model StarCoder2-3B --chat-model Qwen2.5-Coder-1.5B-Instruct --device cpu

Для Docker Compose (каталог /opt/tabby, файл compose.yml) то же самое пишется декларативно:

services:
  tabby:
    image: registry.tabbyml.com/tabbyml/tabby
    container_name: tabby
    entrypoint: ["/opt/tabby/bin/tabby-cpu"]
    command: ["serve", "--model", "StarCoder2-3B",
              "--chat-model", "Qwen2.5-Coder-1.5B-Instruct", "--device", "cpu"]
    restart: unless-stopped
    ports:
      - "127.0.0.1:8080:8080"
    volumes:
      - /opt/tabby/data:/data
cd /opt/tabby && docker compose up -d && docker compose logs -f tabby

Модель проект скачивает сам при первом запуске, и на CPU-сервере важен не только выбор модели, но и то, что вообще влезет на диск. Мы сверили реальные размеры GGUF-файлов из встроенного реестра (квантование Q8_0, а не привычный по Ollama Q4_K_M — на том же числе параметров это примерно вдвое больше):

МодельЛицензияВес файла (Q8_0)
StarCoder-1BBigCode OpenRAIL-M1,3 ГБ
DeepSeekCoder-1.3BDeepSeek License1,4 ГБ
StarCoder2-3BBigCode OpenRAIL-M3,2 ГБ
Qwen2.5-Coder-1.5B-InstructApache 2.01,9 ГБ
Qwen2.5-Coder-7BApache 2.08,1 ГБ

Для CPU без ускорения на старте разумна StarCoder2-3B — заметно точнее 1B-моделей и ещё не тянет за собой RAM, характерную для 7B. Если сеть до Hugging Face с сервера нестабильна, у Tabby есть встроенный обход: переменная TABBY_REGISTRY=modelscope переключает загрузку на зеркало ModelScope, а TABBY_MODEL_CACHE_ROOT задаёт каталог для весов — удобно вынести их на отдельный смонтированный том.

Готовность видна в логе по строке Listening on 0.0.0.0:8080; до неё curl http://127.0.0.1:8080 просто вернёт connection refused. Открыв в браузере адрес сервера (порт смотрит только на 127.0.0.1, так что либо SSH-туннель, либо см. раздел про Nginx ниже), вы увидите форму регистрации аккаунта — первый зарегистрированный пользователь автоматически получает роль владельца со всеми правами администратора.

config.toml: репозитории, модель через Ollama и системный промпт

Файл конфигурации Tabby по умолчанию не создаёт — его нужно завести самому, /opt/tabby/data/config.toml (тот же каталог, что смонтирован в /data). После правки конфигурация подхватывается перезапуском: docker compose restart tabby.

Контекст по репозиториям. Чтобы Tabby подсказывал с оглядкой на структуру проекта, а не только на открытый файл:

[[repositories]]
name = "backend"
git_url = "file:///data/repos/backend"

Путь должен существовать именно внутри контейнера — держите его на томе /data или пробросьте отдельным -v, иначе после пересоздания контейнера индекс укажет в никуда.

Инференс через Ollama вместо встроенного движка. У самого Tabby на CPU есть шероховатости из прошлого раздела, а у чат-модели путь ещё длиннее — на эмбеддингах чаще всего и падает на том же libcuda.so.1. Практичнее развести ответственность: инференс отдать Ollama (она привычно ставится на CPU-серверы и в каталоге приложений MAATRIX есть готовой сборкой), а Tabby оставить интерфейсом для IDE. Для завершения кода нужен формат FIM-разметки конкретной модели — сам Tabby его не угадывает:

[model.completion.http]
kind = "ollama/completion"
model_name = "qwen2.5-coder:7b"
api_endpoint = "http://127.0.0.1:11434"
prompt_template = "<|fim_prefix|>{prefix}<|fim_suffix|>{suffix}<|fim_middle|>"

[model.chat.http]
kind = "openai/chat"
model_name = "qwen2.5-coder:7b"
api_endpoint = "http://127.0.0.1:11434/v1"

Деталь, на которой теряют время: у Ollama-эндпоинта completion — без /v1 в конце, у OpenAI-совместимого чата — обязательно с /v1. Перепутали — получите 404 page not found вместо ответа модели. И prompt_template свой под каждое семейство: у CodeLlama это <PRE> {prefix} <SUF>{suffix} <MID>, у DeepSeek-Coder — <|fim▁begin|>{prefix}<|fim▁hole|>{suffix}<|fim▁end|>, у Qwen2.5-Coder и CodeGemma — <|fim_prefix|>.... Значение берётся из карточки модели, а не подбирается на глаз: неверные FIM-токены не выдают ошибку, модель просто хуже держит контекст и чаще обрывает вставку на середине слова.

Системный промпт для чата меняется в секции [answer]:

[answer]
system_prompt = "Ты — ассистент разработки в компании. Отвечай по-русски и указывай файл, если он есть в контексте."

Сколько потоков CPU реально нужно инференсу

Если вы вынесли инференс в Ollama, следующий вопрос — сколько ядер ей отдавать. Интуиция подсказывает «чем больше потоков, тем быстрее», и это не так: мы прогнали qwen2.5:7b в Q4_K_M на своём сервере с AMD EPYC 9554 (16 vCPU — это 8 физических ядер с Hyper-Threading, Ollama 0.33.1) при разных значениях num_thread:

num_threadГенерация, ток/сОбработка промпта, ток/с
25,712,6
47,631,0
87,661,7
167,468,0
320,3510,9

Честная оговорка: это qwen2.5:7b, обычная инструктивная модель, а не qwen2.5-coder:7b, но по параметрам и квантованию (Q4_K_M) они идентичны, а скорость генерации на CPU определяется прежде всего пропускной способностью памяти, а не тем, на каком корпусе дообучена модель, — для оценки coder-версии замер переносится напрямую.

Отсюда два практичных вывода для VPS. Первый: генерация выходит на полку уже на 4 потоках — дальше упор идёт в память, а не в процессор, и num_thread 16 почти ничего не добавляет к num_thread 4. Второй и более важный: num_thread 32 на машине с 16 vCPU обрушивает генерацию в двадцать раз, до 0,35 ток/с — это цена конкуренции потоков за физические ядра, когда их назначили больше, чем есть в железе. Разумная настройка — num_thread, равный числу физических ядер, максимум на один больше; выставлять число vCPU целиком (то есть считая Hyper-Threading) почти всегда хуже, чем взять вдвое меньше.

Разброс по моделям на том же стенде при num_thread 16: qwen2.5:3b — 34,1 ток/с, mistral:7b — 12,1, llama3.1:8b — 12,8, qwen2.5:7b — 7,4, gemma2:9b — 8,8. Правило простое: чем меньше параметров, тем быстрее генерация и меньше RAM — qwen2.5:3b в Q4_K_M занимает в памяти 2,2 ГБ против 5,1 ГБ у qwen2.5:7b и 5,6 ГБ у llama3.1:8b. Для автодополнения, где ответ нужен за доли секунды между нажатиями клавиш, а не в диалоге, стоит начинать с модели поменьше — qwen2.5-coder:3b даёт заметно более резвые подсказки, чем 7B, ценой чуть более простых завершений.

Подключаем IDE и готовим сервер к продакшену

VSCode и JetBrains. Ставите официальное расширение Tabby из маркетплейса, в настройках указываете Endpoint — адрес сервера — и Token, персональный токен из веб-интерфейса после входа под своим пользователем. Для VSCode обязателен Node.js не ниже 18.0.0 — на более старом расширение не поднимется, и в Output → Tabby будет тихая ошибка про рантайм, а не про сеть.

Если статус в строке состояния — «Disconnected», порядок проверки такой:

  • откройте http://адрес-сервера:8080/swagger-ui/ в браузере — не открылось, значит дело не в IDE, а в сервере или сети до него;
  • сверьте endpoint и token — настройки IDE перекрывают то, что написано в клиентском конфиге;
  • учтите, что агент Tabby не работает через HTTP-прокси: если в компании прокси обязателен, проксируйте сам эндпоинт через Nginx, а не тащите системный прокси в настройки агента.

Ручной вызов подсказки — Alt+\ в VSCode и Ctrl+\ в JetBrains и Vim. Логи агента лежат на машине разработчика: ~/.tabby-client/agent/logs.

Наружу — только через Nginx с TLS. Публиковать 8080 напрямую в интернет не стоит: держите -p 127.0.0.1:8080:8080 в конфиге контейнера (иначе Docker пропишет порт в iptables в обход UFW, и ufw deny 8080 его не остановит), а снаружи — реверс-прокси с сертификатом:

server {
    listen 443 ssl http2;
    server_name tabby.example.com;
    ssl_certificate     /etc/letsencrypt/live/tabby.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/tabby.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 300s;
        proxy_buffering off;
    }
}

proxy_buffering off важен для чата — ответы стримятся по мере генерации, и с буферизацией разработчик увидит текст только целиком после паузы. В настройках сервера (админка → General) укажите настоящий external_url — по нему строятся ссылки в приглашениях и колбэки авторизации; ошибётесь с адресом — приглашения будут вести в никуда.

Регистрация закрыта по умолчанию: после того как создан первый (владелец) аккаунт, самостоятельная регистрация новых пользователей не работает, пока вы явно не разрешите домен почты в настройках или не начнёте раздавать приглашения вручную — случайный человек с URL сервера учётку себе не заведёт.

Бэкап и обновления. Всё состояние — конфиг, база пользователей и токенов, индекс репозиториев — лежит в /opt/tabby/data; веса моделей там же, но их можно не бэкапить, а просто скачать заново. Перед обновлением стоит свериться со списком изменений на GitHub: в v0.32.0, например, добавили генерик-OAuth и переработали приглашения по почте, и конфиги от старых версий иногда требуют правок.

docker compose stop tabby
tar -czf /backup/tabby-$(date +%F).tar.gz --exclude='*/models/*' -C /opt/tabby data
docker compose start tabby

Какой сервер под Tabby брать в MAATRIX

Tabby — не прокси вроде LiteLLM, который живёт сетью: модель должна целиком поместиться в оперативную память, и это определяет всё дальнейшее.

Минимум. Для одного разработчика на CPU с моделью уровня StarCoder2-3B (3,2 ГБ на диске) хватит 2 vCPU, 4 ГБ RAM, 20 ГБ NVMe. Ограничение честное: на 2 ГБ RAM модель не влезет вместе с ОС, Docker-демоном и служебными процессами Tabby — первый же запрос на автодополнение положит контейнер по OOM. И на этой конфигурации не стоит ждать мгновенных подсказок при каждом нажатии клавиши: у слабого CPU заметно время уходит уже на обработку промпта, особенно на длинных файлах.

Комфортный вариант. Команда из нескольких разработчиков, чат вдобавок к автодополнению, модель Qwen2.5-Coder-7B (8,1 ГБ, Apache 2.0) через Ollama-бэкенд из раздела выше — берите 4–8 vCPU, 16 ГБ RAM, 60–80 ГБ NVMe. Ядра сверх четырёх, судя по нашему замеру, почти ничего не добавляют скорости одного запроса, но нужны, чтобы несколько разработчиков не ждали друг друга в очереди — параллельные запросы разводятся по ядрам, а не по потокам одной генерации. Диск с запасом — под несколько версий моделей сразу и бэкапы индекса.

Локация — Великобритания, Лондон. Логика простая: сервер с автодополнением стоит держать рядом с командой, которая им пользуется — лишние 60–80 мс на каждый запрос между нажатием клавиши и подсказкой ощущаются острее, чем в фоновом чате. Загрузка моделей с Hugging Face и обновления самого Tabby с GitHub с британского адреса идут ровно, без сюрпризов, которые иногда случаются на российских маршрутах до крупных CDN. Если в контуре компании действует 152-ФЗ и в коде или тестовых данных встречаются персональные данные российских пользователей — берите RU-локацию, благо сам Tabby ставится в любой из них одинаково.

Про установку без иллюзий. Tabby в каталоге приложений apps.maatrix.io пока нет: сервер приезжает чистым, с Ubuntu 24.04, и разворачивается по инструкции из этой статьи — от docker run до первого коммита в config.toml уходит от силы четверть часа. Зато Ollama, которую мы использовали как бэкенд инференса, в каталоге есть готовой сборкой: отмечаете её при заказе — и получаете рабочий инстанс в кабинете без единой команды в терминале, останется только подставить её адрес в [model.completion.http].

Оплата — картами российских банков, по СБП, криптовалютой или токеном MAAT. Сразу после выдачи доступов: закрыть порт 8080 фаерволом, поднять Nginx с TLS, зарегистрировать владельца — и только потом звать в проект остальных.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Частые вопросы

Обязательно ли покупать GPU-сервер для Tabby?

Нет. Встроенный CPU-бэкенд запускается через /opt/tabby/bin/tabby-cpu и тянет модели уровня StarCoder2-3B, а для более крупных моделей и чата разумнее вынести инференс в Ollama на том же сервере — GPU для комфортной работы одного-двух разработчиков не нужен.

После --device cpu контейнер всё равно падает с libcuda.so.1: cannot open shared object file. Что делать?

Обычный --device cpu меняет только режим инференса, а не бинарник: точка входа по умолчанию всё ещё слинкована с CUDA. Замените entrypoint на /opt/tabby/bin/tabby-cpu в docker run или в compose.yml — это CPU-бинарник внутри того же образа.

Код, который дописала модель Tabby, можно использовать в коммерческом продукте?

Зависит от модели, не от Tabby. Qwen2.5-Coder — Apache 2.0, без ограничений. StarCoder/StarCoder2 — лицензия BigCode OpenRAIL-M с оговорками об ответственном использовании. DeepSeek-Coder — собственная DeepSeek License. Перед продакшеном сверьтесь с текстом лицензии конкретной модели, а не с лицензией самого Tabby (он — Apache 2.0).

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.