Сколько RAM нужно для Telegram-бота
На вопрос «сколько RAM нужно для Telegram-бота» обычно отвечают «гигабайта хватит», и это правда ровно до дня, когда бот молча умирает в четыре утра, а в журнале ядра лежит строчка про oom-killer. Требования к серверу задаёт не число пользователей, а то, что бот держит в памяти между сообщениями: состояния диалогов, кэши, распакованные картинки, лишние импорты. Ниже — замеры по фреймворкам, пять причин раздувания процесса с 70 МБ до 600 и пороги, после которых гигабайта не хватит.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Сколько памяти реально ест бот: замеры, а не догадки
Начинать надо не с тарифа, а с цифры. Под systemd точнее всего смотреть на cgroup — она учитывает и дочерние процессы:
grep VmRSS /proc/$(pgrep -f 'bot.py')/status
systemctl show tgbot.service -p MemoryCurrent
cat /sys/fs/cgroup/system.slice/tgbot.service/memory.peak
Последняя строка важнее прочих: memory.current — это «сейчас», а memory.peak — максимум с момента старта юнита, и убивает бота именно пик. Оговорка: MemoryPeak у systemctl show появился только в systemd 256, а в Ubuntu 24.04 едет systemd 255 — там читайте файл cgroup напрямую, и только при MemoryAccounting=yes в [Service].
Замеры в простое: Ubuntu 24.04 LTS, 2 vCPU, long polling. Цифры — RSS, разброс зависит от версий зависимостей.
| Компонент | RSS в простое | Комментарий |
|---|---|---|
| Ubuntu 24.04 minimal после загрузки | 140–190 МБ | плюс 60–90 МБ, если оставлен snapd |
| Python 3.12, пустой интерпретатор | 11–14 МБ | базовая цена рантайма |
| aiogram 3.x на aiohttp | 60–75 МБ | самый экономный вариант на Python |
| python-telegram-bot 22.x на httpx | 75–95 МБ | стек httpx + h11 тяжелее aiohttp |
| grammY или Telegraf на Node 22 LTS | 45–65 МБ | сам node стартует с ~40 МБ |
| telebot v3 на Go | 12–20 МБ | статический бинарник |
| + SQLAlchemy 2 и asyncpg, пул на 5 | +30–45 МБ | |
| + Pillow (только импорт) | +18–25 МБ | пик обработки в разы выше |
| + pandas | +110–140 МБ | в боте почти всегда лишний |
PostgreSQL 17, shared_buffers = 128MB | 180–260 МБ | с разделяемыми сегментами |
| Redis 8 / Valkey, пустая база | 8–12 МБ | nginx на 2 воркера — ещё 12–18 МБ |
| Docker: dockerd + containerd | 120–180 МБ | плата за удобство деплоя |
Ловушка: RSS суммировать нельзя. Три Python-процесса делят одни страницы libc, и сумма завысит расход процентов на тридцать. Честная метрика — PSS, где разделяемые страницы делятся на число владельцев: sudo apt install -y smem && sudo smem -k -c "pid user pss rss command" -P bot.
Память сервера — это не только бот
Вторая ошибка — считать бота, а не всё, что крутится рядом. Смотреть надо не на строку used в free -h, а на MemAvailable из grep -E 'MemTotal|MemAvailable|SwapFree' /proc/meminfo: это оценка ядра, сколько можно занять прямо сейчас без ухода в swap.
На контейнерных VPS засада: на LXC и OpenVZ /proc/meminfo показывает память хоста, и free -h с честными 64 ГБ вас обманывает. Настоящий потолок даёт cat /sys/fs/cgroup/memory.max — слово max там означает KVM без лимита.
Базовая линия типового стека: 170 МБ система + 190 МБ бот + 220 МБ PostgreSQL + 20 МБ Redis + 15 МБ nginx — около 615 МБ до полезной работы. Про CPU и диск есть отдельный разбор: сколько ресурсов нужно VPS для телеграм-ботов.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть бот-стекПять причин, по которым бот раздувается
Первая и самая частая — состояния FSM в памяти процесса. В aiogram 3 по умолчанию работает MemoryStorage, у которого нет ни TTL, ни ограничения по размеру: каждый начавший диалог пользователь навсегда оставляет запись (chat_id, user_id) → dict. При 50 000 пользователей и типичных 2–4 КБ состояния это 100–200 МБ, которые не освободятся никогда: у разбираемого бота memory.peak за три недели дорос до 612 МБ. Лечение — хранилище в Redis со временем жизни:
from aiogram.fsm.storage.redis import RedisStorage
storage = RedisStorage.from_url(
"redis://127.0.0.1:6379/0", state_ttl=3600, data_ttl=86400,
)
После переезда: 190 МБ у бота плюс 22 МБ у Redis, и цифра перестала расти. Плата — ещё один сервис и потеря состояний при его перезапуске без persistence.
Вторая — файлы, которые едут через память. Bot API отдаёт через getFile документы до 20 МБ, но опасен не файл, а то, во что он разворачивается. JPEG 4000×3000 весит 3 МБ на диске и 36 МБ распакованным (4000 × 3000 × 3 байта), а resize или convert в Pillow держат исходник и результат одновременно — легко 100+ МБ на картинку. Пишите файл на диск потоком вместо io.BytesIO и не отключайте защиту от «бомб»: Image.MAX_IMAGE_PIXELS = 50_000_000, но не None.
Третья — /tmp в Ubuntu 24.04 и новее лежит на tmpfs. Это RAM, а не диск. Каждый tempfile.NamedTemporaryFile() без явного пути отъедает оперативную память, а размер tmpfs по умолчанию — половина RAM: на гигабайтной машине 500 МБ «диска» съедят всю свободную память до байта. Проверьте df -h /tmp: если там tmpfs, уводите временные файлы в /var/tmp.
Четвёртая — импорты ради одной функции. import pandas в боте, где нужен один read_csv раз в сутки, — стабильные 110–140 МБ в резиденте всё время работы, столько же стоит matplotlib. Переносите тяжёлое внутрь хендлера — модуль загрузится по факту вызова.
Пятая — воркеры и арены аллокатора. Вебхук через uvicorn с --workers 4 — четыре процесса Python: 4 × 70 ≈ 280 МБ вместо 70. Боту это не нужно: Telegram доставляет апдейты последовательно, конкурентность даёт asyncio, ставьте --workers 1. Отдельно glibc: аллокатор заводит до восьми арен на ядро, и приложение с пулом потоков (asyncio.to_thread, синхронный драйвер БД) держит куски, не возвращая их системе. На боте с восемью потоками строка Environment=MALLOC_ARENA_MAX=2 в юните дала 148 МБ RSS вместо 214 МБ. Честно: на однопоточном асинхронном боте эффект близок к нулю.
Как поймать OOM и утечку: диагностика по шагам
Сначала подтвердить диагноз. Ядро пишет о срабатывании oom-killer в свой буфер:
[12873.442019] python3 invoked oom-killer: gfp_mask=0x140cca, order=0, oom_score_adj=0
[12873.442158] Out of memory: Killed process 8412 (python3) total-vm:1289456kB, anon-rss:742308kB, file-rss:3072kB, UID:1001 oom_score_adj:0
Ключевая цифра — anon-rss:742308kB: 725 МБ анонимной памяти на момент убийства. Если бот на Node, ядро может быть ни при чём: V8 упирается в собственный лимит кучи и падает сам, с точным текстом FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory. Первая мера — флаг node --max-old-space-size=512 bot.js.
Дальше — отличить пик от утечки. Включите учёт командой systemctl set-property tgbot.service MemoryAccounting=yes и снимайте memory.peak раз в час. Ровная пила с возвратом к базовой линии — пики обработки, лечатся лимитами и потоковой записью. Монотонный рост без возврата — утечка, её ищут в коде через дифф снимков tracemalloc:
import tracemalloc
tracemalloc.start(10)
base = tracemalloc.take_snapshot()
# через час-другой:
for st in tracemalloc.take_snapshot().compare_to(base, 'lineno')[:10]:
print(st)
Верхние строки прямо назовут файл и номер строки, где память растёт. Если мало, memray attach <pid> даст профиль живого процесса без остановки бота. И про ложные срабатывания: колонка RES в top включает разделяемые страницы, поэтому «300 МБ» по top и 210 МБ по PSS — норма, а не расхождение. Причины падений, не связанные с памятью, разобраны отдельно: почему Telegram-бот падает после перезапуска.
Как ужать бота под 1 ГБ
Гигабайта хватает для продакшена, если убрать из него лишнее.
Снести snapd — на сервере это самые дешёвые 60–90 МБ: sudo apt purge -y snapd && sudo rm -rf /var/cache/snapd. Кто ещё занимает память, покажет systemd-cgtop -m --order=memory --iterations=1.
Ограничить журнал. В volatile-режиме journald готов занять до 10% RAM в /run/log/journal — на гигабайтной машине до 100 МБ tmpfs под логи. В /etc/systemd/journald.conf поставьте RuntimeMaxUse=32M, для постоянного журнала ещё и SystemMaxUse=200M.
Дать боту жёсткие рамки в юните, чтобы он не утащил за собой базу:
[Service]
MemoryAccounting=yes
MemoryHigh=280M
MemoryMax=350M
OOMPolicy=stop
Restart=always
RestartSec=5
Разница принципиальная: MemoryHigh — мягкий порог, на котором ядро отбирает страницы и процесс тормозит, но живёт; MemoryMax — жёсткий, при превышении процесс убивается. Ставьте с зазором, чтобы первой срабатывала мягкая.
Добавить страховку. Swap-файл на гигабайтном VPS обязателен, но для бота интереснее zram — сжатая область прямо в RAM, дающая на питоновской куче примерно тройное сжатие. Ставится пакетом systemd-zram-generator, в /etc/systemd/zram-generator.conf хватает секции [zram0] со строками zram-size = min(ram, 1024) и compression-algorithm = zstd. Минусы честно: zram платит процессором за каждый блок, на одноядерном тарифе это заметно, а дисковый swap добавляет сотни миллисекунд задержки. Ни то, ни другое не лечит утечку — только отодвигает падение.
Ужать базу. Для бота на пару тысяч пользователей SQLite в WAL-режиме закрывает задачу и стоит ноль дополнительной памяти. Если PostgreSQL нужен, на гигабайте поставьте shared_buffers = 64MB, max_connections = 20, work_mem = 4MB: дефолты рассчитаны на машину пожирнее.
Когда 1 ГБ действительно мало
Есть сценарии, где экономия заканчивается арифметикой.
- Бот + PostgreSQL + Redis + nginx. Базовая линия 615 МБ оставляет на гигабайте около 380 МБ на пики, а
pip installтяжёлого колеса сам съедает 300–400 МБ. Работает, но на грани; рабочий минимум здесь 2 ГБ. - Обработка изображений и видео. Пик Pillow на большой картинке — сотня-другая мегабайт, ffmpeg на транскодировании голосового — 50–150 МБ. Если задачи идут параллельно, умножайте. От 2 ГБ, комфортно на 4.
- Распознавание речи.
faster-whisperс модельюbaseв int8 занимает 400–500 МБ,small— 700 МБ–1 ГБ, и это только веса, без буферов. Меньше 4 ГБ браться не стоит. - Локальная языковая модель. 7B в квантовании Q4_K_M — 4,5–5 ГБ под веса плюс контекст, реальный минимум 8 ГБ, и на CPU она всё равно отвечает медленно. Дешевле ходить в чужой API; каркас бота под это разобран в материале про запуск Telegram-бота на Python на VPS.
- Несколько ботов на машине. Каждый — свой процесс со своим интерпретатором: пять ботов на aiogram это 350 МБ только под них.
Какой сервер брать в MAATRIX под Telegram-бота
Минимум — 1 vCPU / 1 ГБ RAM / 20 ГБ NVMe. Этого честно хватает боту на aiogram или grammY с SQLite, без обработки медиа и без PostgreSQL рядом. Обязательные условия: swap или zram, лимиты MemoryHigh/MemoryMax в юните и RuntimeMaxUse у журнала. Такой сервер держит несколько тысяч активных пользователей и не требует внимания месяцами.
Комфортная конфигурация — 2 vCPU / 4 ГБ RAM / 60 ГБ NVMe. Здесь бот, PostgreSQL, Redis и nginx с TLS живут одновременно, пики обработки картинок не загоняют систему в swap, остаётся место под второй бот и staging-копию. Диск берите с запасом ради журнала и бэкапов базы: No space left on device роняет бота не хуже нехватки памяти. Промежуточные 2 ГБ осмысленны, когда база уже нужна, а медиа ещё нет; память наращивается на работающем сервере, так что стартовать с меньшего — нормальная стратегия.
Локация — UK, Лондон. API-серверы Telegram стоят в Европе, и с лондонского узла время ответа до api.telegram.org держится в пределах 8–15 мс против 75–90 мс из Нью-Йорка. На память это не влияет, зато влияет на скорость реакции, особенно на вебхуках. Плюс европейская юрисдикция и чистый IP для внешних API. Берите RU, если аудитория российская и данные подпадают под 152-ФЗ, и US, если бот ходит в зарубежные ИИ-сервисы. Сравнение площадок — в статье про лучший VPS для Telegram-бота в Великобритании.
Оплата — картами российских банков, по СБП, криптовалютой или токеном MAAT; иностранная карта не нужна даже для лондонской площадки. Не уверены, в какой порог попадаете, — пришлите memory.peak вашего юнита и список соседей бота, подберём конфигурацию по факту.
Развернуть за пару минут
Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Развернуть бот-стекОбсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Частые вопросы
Хватит ли 512 МБ для простого бота?
Технически да: чистый поллинг-бот на Go или на aiogram без базы, со swap и лимитами в юните. Практически он уходит в OOM на первой же установке тяжёлой зависимости, поэтому разумный минимум — 1 ГБ.
Бот растёт на 5–10 МБ в час — это утечка?
Почти наверняка да, и первый подозреваемый — MemoryStorage для FSM или обычный dict вместо кэша с ограничением. Подтвердите диффом tracemalloc.take_snapshot().compare_to(...): верхние строки назовут файл и строку.
Хватит ли swap вместо докупки RAM?
Как страховка от редких пиков да, как рабочий режим нет: когда активные страницы Python-кучи уезжают на диск, ответы тормозят на сотни миллисекунд. Постоянно занятый swap — сигнал переходить на следующий тариф.
Нужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.