Whisper локально на своём сервере: пошаговая установка
Облачный API транскрибации берёт деньги за каждую минуту и требует залить туда запись целиком — интервью, разговор с клиентом, внутреннюю планёрку. Whisper локально на своём сервере закрывает оба вопроса разом: аудио никуда не уходит, а предел по объёму упирается только в процессорное время, которое вы уже оплатили. Ниже — путь от чистой Ubuntu до рабочего HTTP-эндпоинта: что ставить, куда падают модели, почему pip install openai-whisper съедает несколько гигабайт диска и как не получить в расшифровке фразу «Субтитры сделал DimaTorzok», которой в записи не было.
Содержание
- Что даёт локальный запуск и чем за него платят
- Какую сборку ставить: openai-whisper, faster-whisper или whisper.cpp
- Подготовка сервера: пакеты, Python и ловушка PEP 668
- Установка и первая расшифровка
- Модели: где лежат, сколько весят и как уйти в офлайн
- Из скрипта в сервис: systemd, HTTP-эндпоинт и Nginx
- Какой сервер под локальный Whisper брать в MAATRIX
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что даёт локальный запуск и чем за него платят
Три причины, по которым Whisper поднимают у себя, а не дёргают чужой эндпоинт.
Данные не покидают периметр. Записи разговоров, приёмов и собеседований — персональные данные, а часто ещё и предмет NDA. Файл живёт на вашем диске и в логи стороннего провайдера не попадает. Для российского юрлица это 152-ФЗ, для европейского клиента — GDPR.
Счёт не растёт вместе с объёмом. Поминутная тарификация облачной транскрибации держится в районе полуцента за минуту: на разовых файлах копейки, на 300–500 часах в месяц — заметная статья расходов. Свой сервер стоит одинаково, прогнали вы через него десять часов или тысячу. Заодно исчезают 429 Too Many Requests.
Теперь честная вторая половина.
- Скорость на CPU скромная. Whisper — трансформер с авторегрессивным декодером, без видеокарты большие модели считаются заметно медленнее. Цифры зависят от числа ядер и набора инструкций: меряйте на своём материале, а не верьте чужим таблицам.
- Диаризации в коробке нет. Whisper отвечает, «что сказано», но не «кто сказал». Разделение по спикерам — отдельный слой (WhisperX с pyannote), а pyannote требует токена Hugging Face и принятия лицензии: полностью офлайновым такой стенд не сделать.
- Никакого SLA. Обновления, мониторинг, место под кэш и разбор упавшего сервиса — на вас. Качество на звонке в 8 кГц или на двух говорящих поверх друг друга тоже вытягиваете вы: параметрами и предобработкой, а не установкой пакета.
Какую сборку ставить: openai-whisper, faster-whisper или whisper.cpp
«Whisper» — модель, а не программа. Реализаций несколько, и от выбора зависит, что вы поставите и сколько это займёт места.
| Сборка | Что тянет за собой | Формат моделей | Кому подходит |
|---|---|---|---|
openai-whisper | PyTorch и весь его хвост | .pt из кэша OpenAI | эталонное поведение, эксперименты |
faster-whisper | CTranslate2, PyAV, без torch | CTranslate2 с Hugging Face | обычный VPS, продакшен |
whisper.cpp | ничего, один бинарник | ggml-*.bin, квантованные | маленькие машины, ARM, отсутствие Python |
openai-whisper — референсная реализация. Пакет нумеруется датой релиза, а не семвером: версии выглядят как 20240930 или 20250625. Засада — PyTorch: обычное pip install openai-whisper тянет с PyPI сборку torch с прицепом CUDA-библиотек NVIDIA, и на VPS с диском 20 ГБ установка заканчивается так:
ERROR: Could not install packages due to an OSError: [Errno 28] No space left on device
Лечится тем, что CPU-сборку torch ставят заранее с отдельного индекса:
/opt/whisper/venv/bin/pip install torch --index-url https://download.pytorch.org/whl/cpu
/opt/whisper/venv/bin/pip install openai-whisper
faster-whisper — та же модель в формате CTranslate2. Torch не нужен вовсе: зависимости — ctranslate2, tokenizers, huggingface_hub, onnxruntime и av. Аудио читается через PyAV, системный ffmpeg не обязателен. На CPU включается int8: модель занимает примерно вдвое меньше памяти, чем в float16. Для сервера это выбор по умолчанию.
whisper.cpp — реализация на C++ поверх ggml, без Python вообще. Один исполняемый файл, модели квантуются до q5_0 и q8_0, живёт на слабых машинах и ARM. Расплата — жёсткий формат входа: только WAV 16 кГц моно.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть WhisperПодготовка сервера: пакеты, Python и ловушка PEP 668
Дальше — Ubuntu 24.04 LTS или Debian 12, команды одинаковые. Ставим базу:
apt update && apt install -y python3-venv python3-pip ffmpeg git
python3 --version # 24.04 → 3.12.x, Debian 12 → 3.11.x
ffmpeg -version | head -1
Первая же попытка поставить пакет глобально упрётся в PEP 668:
error: externally-managed-environment
× This environment is externally managed
╰─> To install Python packages system-wide, try apt install python3-xyz
Это не поломка, а защита системного Python от затирания apt-пакетов. Ответ — виртуальное окружение, а не --break-system-packages, который выйдет боком при обновлении дистрибутива:
mkdir -p /opt/whisper && cd /opt/whisper
python3 -m venv venv
/opt/whisper/venv/bin/pip install -U pip wheel
Заведите отдельного пользователя: сервис, принимающий чужие файлы, не должен ходить под root.
adduser --system --group --home /opt/whisper whisper
mkdir -p /var/lib/whisper/{hf,uploads,out} && chown -R whisper:whisper /var/lib/whisper
Перед установкой посмотрите на железо: CTranslate2 выбирает набор инструкций при старте, и на процессоре без AVX2 скорость будет заметно ниже.
lscpu | grep -o -m1 -E 'avx2|avx512f'
nproc && free -g && df -h /
Если avx2 в выводе нет, вы на очень старом или урезанном виртуальном CPU; CT2_VERBOSE=1 заставит CTranslate2 напечатать при загрузке выбранный набор инструкций. И отдельно про swap: он не спасает нехватку RAM под веса, а лишь превращает загрузку в перемалывание диска.
Установка и первая расшифровка
Ставим faster-whisper и сразу проверяем версии — пригодятся, когда что-то пойдёт не так:
/opt/whisper/venv/bin/pip install faster-whisper
/opt/whisper/venv/bin/python - <<'EOF'
import ctranslate2, faster_whisper
print("ctranslate2", ctranslate2.__version__, "| faster-whisper", faster_whisper.__version__)
EOF
Минимальный рабочий скрипт /opt/whisper/run.py:
from faster_whisper import WhisperModel
model = WhisperModel(
"large-v3-turbo",
device="cpu",
compute_type="int8",
cpu_threads=4,
download_root="/opt/whisper/models",
)
segments, info = model.transcribe(
"meeting.m4a",
language="ru",
vad_filter=True,
condition_on_previous_text=False,
beam_size=5,
)
print(f"язык: {info.language}, длительность: {info.duration:.1f} c")
for s in segments:
print(f"[{s.start:7.2f} -> {s.end:7.2f}] {s.text.strip()}")
Одна деталь сбивает с толку почти всех: transcribe() возвращает генератор. Вызов отрабатывает мгновенно, а распознавание стартует, только когда вы пошли по segments циклом — «расшифровка заняла доли секунды» значит лишь то, что вы ещё не начали итерироваться.
Про compute_type: на CPU берите int8. Запрос float16 без подходящего устройства CTranslate2 понизит сам, предупредив строкой «Requested float16 compute type, but the target device or backend do not support efficient float16 computation». На GPU наоборот — float16 или int8_float16.
Тот же файл через CLI референсной сборки, сразу в субтитры:
/opt/whisper/venv/bin/whisper meeting.mp3 --model turbo --language ru \
--output_format srt --output_dir /var/lib/whisper/out --fp16 False
Без --fp16 False на процессорной машине увидите FP16 is not supported on CPU; using FP32 instead — не ошибка, лишняя строка в логе.
Вариант на whisper.cpp, если Python не нужен:
git clone https://github.com/ggml-org/whisper.cpp && cd whisper.cpp
cmake -B build && cmake --build build -j --config Release
./models/download-ggml-model.sh large-v3-turbo-q5_0
ffmpeg -i meeting.m4a -ar 16000 -ac 1 -c:a pcm_s16le meeting.wav
./build/bin/whisper-cli -m models/ggml-large-v3-turbo-q5_0.bin -f meeting.wav -l ru -osrt
Два момента, на которых спотыкаются по старым мануалам. Бинарник теперь называется whisper-cli, а не main — команды вида ./main -m ... файл не найдут. И вход обязан быть WAV 16 кГц моно: подсунете mp3 — получите error: failed to open 'meeting.mp3' as WAV file, так что строка с ffmpeg не для красоты.
Что покрутить, когда текст выходит странным. Whisper обучался в том числе на субтитрах с видеохостингов и на тишине дописывает оттуда мусор: в русских расшифровках всплывают «Субтитры сделал DimaTorzok», «Редактор субтитров А.Синецкая» или бесконечное «Продолжение следует...». Лечится тремя настройками: vad_filter=True выкидывает участки без речи до декодера, condition_on_previous_text=False не даёт зациклиться на собственном выводе, initial_prompt с вашими терминами поднимает точность на узкой лексике.
Модели: где лежат, сколько весят и как уйти в офлайн
Обещание «локально» выполняется, только когда сервису не нужен интернет на старте. Значит, надо знать, куда сборки складывают веса.
| Модель | Параметров | Вес .pt | Вес ggml f16 | VRAM по документации OpenAI |
|---|---|---|---|---|
| tiny | 39M | ~75 МБ | ~78 МБ | ~1 ГБ |
| base | 74M | ~142 МБ | ~148 МБ | ~1 ГБ |
| small | 244M | ~466 МБ | ~488 МБ | ~2 ГБ |
| medium | 769M | ~1,5 ГБ | ~1,5 ГБ | ~5 ГБ |
| large-v3 | 1550M | ~2,9 ГБ | ~3,1 ГБ | ~10 ГБ |
| large-v3-turbo | 809M | ~1,6 ГБ | ~1,6 ГБ | ~6 ГБ |
Это размеры опубликованных файлов и требования из документации, а не замеры производительности. Там же сказано, что turbo — урезанный по числу слоёв декодера вариант large-v3: заметно быстрее при близком качестве распознавания, но хуже переводит речь на английский. Для расшифровки русского turbo почти всегда верный выбор.
Пути кэша различаются:
openai-whisper—~/.cache/whisper, переопределяется черезXDG_CACHE_HOMEили флаг--model_dir;faster-whisper— кэш Hugging Face~/.cache/huggingface/hub, переопределяется черезHF_HOMEили аргументdownload_root=;whisper.cpp— каталогmodels/рядом с бинарником.
Отсюда классическая поломка при переезде в systemd: под юнитом с User=whisper домашний каталог другой, кэш «пропадает», и сервис при каждом старте заново качает три гигабайта либо падает, если сеть закрыта. Задавайте HF_HOME в юните явно.
Скачать модель заранее, в нужный каталог:
/opt/whisper/venv/bin/pip install -U "huggingface_hub[cli]"
HF_HOME=/var/lib/whisper/hf /opt/whisper/venv/bin/huggingface-cli download \
Systran/faster-whisper-large-v3 --local-dir /opt/whisper/models/large-v3
В свежих версиях библиотеки та же команда пишется как hf download. Дальше передавайте в WhisperModel путь к каталогу вместо имени модели, добавляйте local_files_only=True, а в окружение сервиса — HF_HUB_OFFLINE=1. Иначе библиотека на каждом старте стучится на huggingface.co за обновлениями, и в закрытом контуре вы получите:
OSError: We couldn't connect to 'https://huggingface.co' to load this file
Отдельная боль при установке из России: openai-whisper тянет чекпойнты с openaipublic.azureedge.net, faster-whisper — с huggingface.co, и оба адреса из РФ регулярно недоступны: скачивание падает с urllib.error.URLError либо висит до таймаута. Зарубежный сервер снимает вопрос без прокси.
Из скрипта в сервис: systemd, HTTP-эндпоинт и Nginx
Скрипт в консоли — демонстрация. Рабочая установка живёт под systemd и отвечает по HTTP.
[Unit]
Description=Whisper transcription service
After=network-online.target
[Service]
User=whisper
Group=whisper
WorkingDirectory=/opt/whisper
Environment=HF_HOME=/var/lib/whisper/hf
Environment=HF_HUB_OFFLINE=1
Environment=OMP_NUM_THREADS=4
ExecStart=/opt/whisper/venv/bin/python -m uvicorn app:api --host 127.0.0.1 --port 9000
Restart=on-failure
RestartSec=5
MemoryMax=6G
[Install]
WantedBy=multi-user.target
Дальше systemctl daemon-reload, systemctl enable --now whisper и journalctl -u whisper -f: именно там видно, нашлась ли модель. MemoryMax выставляйте осознанно — иначе при нехватке памяти ядро прибьёт процесс, а в journalctl -k останется Out of memory: Killed process ... (python3).
Писать обёртку с нуля не обязательно, есть готовые:
- whisper-asr-webservice — контейнер с REST API, порт 9000, движок переключается переменной
ASR_ENGINE; - speaches (бывший faster-whisper-server) — эндпоинт
/v1/audio/transcriptions, совместимый с OpenAI: клиент переезжает с облака сменой одногоbase_url; - wyoming-faster-whisper — голосовой ввод для Home Assistant, порт 10300.
docker run -d --name whisper -p 127.0.0.1:9000:9000 \
-e ASR_ENGINE=faster_whisper -e ASR_MODEL=large-v3-turbo \
-v /var/lib/whisper/hf:/root/.cache/huggingface \
onerahmet/openai-whisper-asr-webservice:latest
curl -sS -F "audio_file=@meeting.m4a" \
"http://127.0.0.1:9000/asr?task=transcribe&language=ru&output=srt" -o meeting.srt
Наружу сервис выставляют только через обратный прокси, и там две обязательные правки: транскрибация — большой файл и долгий ответ.
location /asr/ {
proxy_pass http://127.0.0.1:9000/;
client_max_body_size 1024m;
proxy_request_buffering off;
proxy_read_timeout 1800s;
proxy_send_timeout 1800s;
}
Без client_max_body_size часовая запись получит 413 Request Entity Too Large, а без увеличенных таймаутов — 504 Gateway Time-out ровно через минуту, пока расшифровка ещё идёт.
Про параллелизм честно: одна загруженная модель обслуживает одну расшифровку за раз, вторая задача просто отбирает у первой потоки. Правильная схема — очередь (Celery, RQ или семафор на один слот) с ответом 202 и идентификатором задачи.
Минимум гигиены: сервис слушает 127.0.0.1, порт закрыт (ufw allow 22/tcp, ufw deny 9000/tcp), на прокси — токен в заголовке и лимит размера файла, а загруженное аудио не копится вечно: find /var/lib/whisper/uploads -type f -mmin +120 -delete по крону.
Какой сервер под локальный Whisper брать в MAATRIX
Приложение whisper есть в каталоге apps.maatrix.io и разворачивается автоматически при заказе сервера — на Ubuntu и на Debian, вручную запускать ничего не нужно. Адрес сервиса и ключи появляются в личном кабинете, раздел «Доступ». Команды выше нужны не для установки, а чтобы понимать, что внутри.
Честный минимум: 2 vCPU, 4 ГБ RAM, 40 ГБ NVMe. Арифметика простая: репозиторий Systran/faster-whisper-large-v3 занимает на диске около 3 ГБ в float16, при compute_type="int8" веса квантуются на загрузке и в памяти помещаются примерно вдвое компактнее. small и base на такой машине живут спокойно, large-v3-turbo в int8 — впритык, без запаса на второй процесс. Ограничение называю прямо: на двух ядрах длинные записи считаются долго. Это конфигурация под разовые файлы, а не под конвейер.
Комфортный вариант: 4–8 vCPU, 8–16 ГБ RAM, 80 ГБ NVMe. Здесь одновременно помещаются large-v3-turbo в int8, очередь задач, Nginx и кэш моделей на 10–20 ГБ. Появляется смысл поднять cpu_threads до числа реальных ядер вместо дефолтных четырёх. Видеокарта нужна, когда речь про часы аудио ежедневно: документация OpenAI даёт ориентиры по видеопамяти — порядка 6 ГБ для turbo и около 10 ГБ для large-v3, то есть карта на 8–12 ГБ VRAM задачу закрывает.
Локация — Великобритания, Лондон. Два довода. Первый: беспрепятственный доступ к Hugging Face при первой установке — веса тянутся напрямую, без прокси и URLError. Второй: близость к пользователям — пинг до Европы и европейской части России измеряется десятками миллисекунд. Если по 152-ФЗ материалы обязаны храниться в России — берите RU-локацию: Whisper офлайн-модель, ей безразлично, где стоять.
Оплата — картой российского банка, по СБП, криптовалютой или токеном MAAT: зарубежная карта для сервера в Лондоне не требуется.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть WhisperОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Частые вопросы
Можно ли обойтись без видеокарты?
Да, большинство сценариев так и живут: на CPU берите faster-whisper с compute_type="int8" и моделью large-v3-turbo или small. GPU нужен, когда объём переваливает за несколько часов аудио в день.
Откуда в расшифровке фразы, которых не было в записи?
Галлюцинации на тишине и шуме: модель дописывает мусор из корпуса субтитров, вроде «Субтитры сделал DimaTorzok» или «Продолжение следует...». Включите vad_filter=True, поставьте condition_on_previous_text=False и уберите длинные паузы — в связке это снимает почти все случаи.
Как сделать, чтобы сервер вообще не ходил в интернет?
Скачайте модель заранее через huggingface-cli download в постоянный каталог, передавайте в WhisperModel путь вместо имени и добавьте local_files_only=True, а в systemd-юнит — Environment=HF_HUB_OFFLINE=1 и явный HF_HOME. Проверка: остановите сервис, обрежьте доступ фаерволом и стартуйте заново.
Нужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.