MAATRIX / Блог / Сколько RAM нужно для Listmonk

Сколько RAM нужно для Listmonk

MAATRIX

Listmonk выглядит настолько лёгким — один Go-бинарник плюс PostgreSQL — что многие берут для него минимальный VPS на 1 GB и потом удивляются, почему при отправке кампании на 50 тысяч адресов сервер начинает тормозить или вовсе падает по OOM. Разберём, куда реально уходит память в Listmonk, чем PostgreSQL под ним отличается от «просто ещё одной базы» и как посчитать RAM под свой размер списка рассылки, а не гадать по общей рекомендации из документации.

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

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

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

Из чего складывается потребление RAM в Listmonk

Архитектурно Listmonk — один из самых экономных self-hosted инструментов для email-маркетинга, и это не маркетинговая фраза, а следствие конкретного технического решения: весь бэкенд, API и фронтенд (SPA на Vue) отдаются одним статически собранным Go-бинарником без Node.js в рантайме, без Redis, без отдельного message broker. Единственная обязательная внешняя зависимость — PostgreSQL.

Память расходуется по трём принципиально разным сценариям использования:

  • Простой без активности — бинарник Listmonk в режиме ожидания (обслуживает админку и принимает вебхуки от почтовых провайдеров) занимает немного, обычно десятки-первые сотни мегабайт в зависимости от числа открытых соединений к API.
  • Отправка кампании — именно здесь память растёт заметнее всего: Listmonk вычитывает получателей пачками из PostgreSQL, рендерит шаблон под каждого (подстановка полей, трекинг-пиксели, персонализация), держит пул воркеров на отправку через SMTP. Чем выше concurrency и batch_size в настройках кампании, тем больше писем одновременно живёт в памяти в промежуточном состоянии.
  • PostgreSQL — отдельный процесс со своим бюджетом, который часто недооценивают: помимо самих подписчиков и списков там живут таблицы статистики открытий/кликов (campaign_views, link_clicks), растущие пропорционально не числу подписчиков, а числу отправленных писем и совершённых по ним действий.

Если считать честно, минимальная конфигурация — это Listmonk-бинарник и PostgreSQL на одной машине, а нагрузка от активной отправки кампаний — переменная величина сверху, которую нужно закладывать отдельно от базового простоя.

Сколько RAM нужно по размеру базы подписчиков

Официальная документация Listmonk называет скромные минимальные требования (проект действительно способен крутиться на 512 MB-1 GB для совсем маленьких списков), но реальный бюджет памяти сильнее зависит от того, сколько писем отправляется за раз и как часто, чем от голого количества подписчиков в базе. Ниже — ориентировочные цифры для сценария «рассылка кампаниями раз в несколько дней, без постоянного стрима транзакционных писем»: у вас может быть иначе в зависимости от частоты и объёма кампаний.

Размер базы подписчиковRAM: Listmonk (простой / во время отправки)RAM: PostgreSQLИтого, ориентир
до 5 000100-200 MB / 300-500 MB512 MB-1 GB1-2 GB
5 000-25 000150-300 MB / 500 MB-1 GB1-2 GB2-4 GB
25 000-100 000300-500 MB / 1-2 GB2-4 GB4-6 GB
100 000-500 000500 MB-1 GB / 2-4 GB4-8 GB8-12 GB
500 000+1-2 GB / 4-6 GB+8-16 GB, лучше отдельный серверсчитается индивидуально

Это именно ориентир, а не гарантия: если в шаблонах много персонализации, вложений или высокого concurrency на отправку, профиль нагрузки будет тяжелее, чем у той же базы с простыми текстовыми письмами раз в месяц. Практический подход тот же, что и для любого self-hosted сервиса: берите нижнюю границу диапазона под свой размер списка, ставьте мониторинг памяти с первого дня и наращивайте по факту, а не по табличке.

Для базы до 25-30 тысяч подписчиков вполне хватает одного VPS на 2-4 GB RAM с Listmonk и PostgreSQL в одном Docker Compose на одной машине — разносить их по разным серверам на этом масштабе избыточно.

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

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

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

PostgreSQL под Listmonk: почему база растёт быстрее, чем кажется

PostgreSQL для Listmonk — не «мелкая справочная таблица подписчиков», а база, которая активно пишет при каждой отправленной кампании. Кроме таблицы subscribers там живут таблицы статистики открытий и кликов (campaign_views, link_clicks) — при включённом трекинге каждое открытие письма и каждый клик по ссылке пишет строку в базу, и на активной рассылке с десятками тысяч адресов это ощутимый поток INSERT-ов за короткое окно после отправки. Плюс материализованные представления для дашборда статистики, которые Listmonk периодически пересчитывает — на больших объёмах эта операция кратковременно нагружает и CPU, и память PostgreSQL.

Дефолтная конфигурация PostgreSQL из коробки (shared_buffers = 128MB) рассчитана на слабое железо и не годится для базы с активными кампаниями. Базовые ориентиры для postgresql.conf:

# при выделенных под PostgreSQL 4 GB RAM
shared_buffers = 1GB          # обычно 25% от RAM, отданной под Postgres
effective_cache_size = 3GB    # 50-75% от RAM — подсказка планировщику, не резервирование
work_mem = 32MB                # запросы сегментации подписчиков (SQL-запросы для динамических списков) бывают тяжёлыми
maintenance_work_mem = 256MB
max_connections = 50           # Listmonk сам держит небольшой пул, 50 обычно достаточно

Отдельно стоит следить за autovacuum — таблицы статистики открытий и кликов на активной рассылке накапливают и удаляют строки заметно интенсивнее, чем справочник подписчиков, и без своевременного вакуума разрастаются в размерах на диске быстрее ожидаемого. Подробный разбор параметров и типичных проблем — в статье про тюнинг PostgreSQL и частые ошибки, а если PostgreSQL внезапно начал заполнять диск WAL-файлами после серии массовых рассылок — смотрите почему растёт WAL в PostgreSQL.

Если разворачиваете PostgreSQL впервые именно под этот проект, порядок установки — в статье как установить и настроить PostgreSQL на VPS.

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

Основной параметр, которым Listmonk управляет нагрузкой на отправку, — это concurrency (число параллельных воркеров отправки) и message_rate (лимит писем в секунду на один SMTP-сервер) в настройках кампании или в config.toml. По умолчанию значения консервативные, но именно их поднимают в первую очередь, когда хотят разослать 100 тысяч писем «побыстрее» — и именно это первым делом упирается в память при недостаточном запасе.

Логика простая: чем выше concurrency, тем больше писем одновременно находится в состоянии «выбрано из базы, отрендерено, ждёт ответа от SMTP» — каждое такое письмо занимает память, пока соединение с почтовым сервером не подтвердит отправку. При высоком concurrency и медленном ответе от внешнего SMTP-сервера (например, из-за rate limit на стороне провайдера) в памяти одновременно зависает пачка отрендеренных писем плюс накладные расходы на сами TCP/TLS-соединения.

Практические ориентиры:

  • Начинайте с concurrency: 5-10 и message_rate в пределах лимитов вашего SMTP-провайдера — почти всегда узкое место не память сервера, а лимиты внешнего SMTP на входящий поток писем в секунду.
  • Если письма содержат тяжёлые вложения или инлайн-изображения, отрендеренное письмо в памяти весит заметно больше текстового — закладывайте запас, а не считайте по «среднему» письму.
  • SMTP-релей лучше настраивать через собственный сервер, а не платный сторонний сервис с жёсткими квотами — как поднять массовую рассылку с VPS с нуля, разобрано в статье пошаговая настройка VPS под почтовую рассылку с нуля.
  • Динамические списки (подписчики, отобранные SQL-запросом) при большой базе — дополнительная нагрузка на PostgreSQL в момент старта кампании, когда формируется итоговая выборка получателей.

Docker Compose: лимиты памяти и типовой конфиг

Официальный способ разворачивания Listmonk — Docker Compose из двух сервисов: сам listmonk и postgres. Без явных лимитов памяти контейнеры по умолчанию могут выесть всю RAM хоста в момент пиковой отправки — это неприятный сюрприз именно тогда, когда кампания уже запущена и остановить её на середине неудобно.

Минимальный рабочий пример с лимитами под сервер на 4 GB:

services:
  listmonk:
    image: listmonk/listmonk:latest
    mem_limit: 1g
    mem_reservation: 512m
    environment:
      - LISTMONK_db__host=postgres
      - LISTMONK_db__port=5432
      - LISTMONK_db__user=listmonk
      - LISTMONK_db__password=listmonk_pass
      - LISTMONK_db__database=listmonk
      - LISTMONK_db__ssl_mode=disable
    depends_on:
      - postgres
    ports:
      - "9000:9000"
    restart: unless-stopped

  postgres:
    image: postgres:16-alpine
    mem_limit: 2g
    shm_size: 256mb
    environment:
      - POSTGRES_USER=listmonk
      - POSTGRES_PASSWORD=listmonk_pass
      - POSTGRES_DB=listmonk
    volumes:
      - ./pgdata:/var/lib/postgresql/data
    restart: unless-stopped

mem_limit — жёсткий потолок: контейнер, упёршийся в него, получит SIGKILL от ядра, а не мягко замедлится. Общие принципы выставления таких лимитов и типичные ошибки разобраны в статье про ресурсы и лимиты CPU и памяти в Docker — там же про то, почему лимит впритык к реальному потреблению хуже, чем лимит с запасом 20-30%.

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

docker stats --no-stream listmonk_listmonk_1 listmonk_postgres_1
free -h
dmesg -T | grep -i "killed process"
docker inspect listmonk_listmonk_1 --format='{{.State.OOMKilled}}'

Если последняя команда вернула true — сервис уже упирался в лимит именно в момент рассылки, и правильная реакция — снизить concurrency кампании и/или поднять mem_limit, а не считать падение случайностью.

Когда пора выносить PostgreSQL на отдельный сервер

Пока база укладывается в первые десятки тысяч подписчиков, Listmonk и PostgreSQL прекрасно живут на одной машине. Сигналы, что пора разделять:

  • PostgreSQL стабильно занимает больше половины доступной RAM хоста даже после тюнинга shared_buffers.
  • docker stats показывает, что при запуске крупной кампании оба контейнера одновременно упираются в лимиты, а не работает только один.
  • База перевалила за пару сотен тысяч и рассылки идут ежедневно или чаще — конкуренция за диск и память между записью статистики и обслуживанием админки становится заметной.
  • Бэкапы базы (а бэкапить базу подписчиков нужно обязательно, это ваш главный актив) заметно нагружают сервер во время снимка — вынос БД снимает эту конкуренцию с самой рассылкой.

Для базы от 300-500 тысяч подписчиков и активных ежедневных кампаний разумная схема — Listmonk отдельно, PostgreSQL отдельно, с возможностью нарастить диск и память под базу независимо от приложения. Если для рассылки важна юрисдикция сервера (репутация IP, требования к хранению данных подписчиков), стоит заранее прикинуть, где физически будет жить SMTP-релей — сравнение вариантов есть в статье США или Великобритания — где брать сервер для почтовой рассылки.

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

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

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

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

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

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

Хватит ли 1 GB RAM для теста Listmonk на 100-500 подписчиков?

Да, для ознакомительного запуска через Docker Compose с Listmonk и PostgreSQL на одной машине 1 GB технически достаточно. Но это конфигурация «попробовать», а не рабочая для регулярных кампаний — при первой же реальной рассылке даже на пару тысяч адресов лучше иметь запас минимум до 2 GB.

Растёт ли память Listmonk от общего числа подписчиков в базе?

Слабо в состоянии покоя — сам список подписчиков хранится в PostgreSQL, а не в памяти приложения. Заметнее память растёт от активности: числа одновременно отправляемых писем (concurrency) и объёма статистики открытий/кликов, которую нужно обрабатывать и агрегировать для дашборда.

Нужен ли Redis для Listmonk?

Нет, Listmonk не требует Redis или другого брокера сообщений — очередь отправки и состояние кампании держатся в PostgreSQL и в памяти самого процесса. Это одна из причин, почему стек получается заметно легче многих альтернатив с похожим функционалом.

Что будет, если памяти не хватит во время отправки большой кампании?

Ядро Linux по правилам OOM killer убьёт процесс с наибольшим потреблением — как правило, сам Listmonk. Кампания остановится на середине в статусе "paused" или "cancelled" в зависимости от момента сбоя, и часть писем может не отправиться, а часть — задвоиться при неаккуратном перезапуске. Поэтому важнее держать запас памяти и невысокий concurrency, чем экономить на последнем гигабайте перед крупной рассылкой.

Можно ли использовать Listmonk как замену Mailchimp без ежемесячной платы?

Да, это одна из основных причин его популярности: функционально Listmonk закрывает управление списками, сегментацию, шаблоны и статистику открытий/кликов, а стоимость сводится к аренде сервера вместо ежемесячной подписки, которая у Mailchimp растёт вместе с размером базы. Разница — придётся самому следить за репутацией SMTP и настройками сервера, это ложится на вас, а не на провайдера.

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

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

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