Установка Listmonk на Ubuntu 24.04: пошаговое руководство
Разверните Listmonk v6.2.0 на Ubuntu 24.04 с использованием PostgreSQL и systemd. В статье разобрана настройка config.toml, подключение SMTP и реальные затраты на доставляемость.
Что необходимо для self-hosted рассылки на базе Listmonk
Listmonk — это self-hosted менеджер рассылок и списков почтовых адресов: один бинарный файл на Go, одна база данных PostgreSQL, один конфигурационный файл и один юнит systemd. Небольшой VPS справляется с этой задачей без усилий, так как Listmonk хранит базу подписчиков и очередь кампаний, но не занимается непосредственной отправкой почты. Программа передаёт каждое сообщение SMTP-серверу (simple mail transfer protocol), поэтому показатель доставляемости зависит от репутации этого сервера, а не от самого программного обеспечения.
В этом руководстве описывается установка Listmonk v6.2.0, актуального релиза на июль 2026 года, на Ubuntu 24.04. Вам потребуется VPS с публичным IP-адресом, доменное имя, которое вы контролируете, и PostgreSQL версии 12 или новее. Установка занимает около часа. Формирование репутации отправителя требует нескольких недель, и этот аспект рассматривается ближе к концу руководства.
Установка PostgreSQL и создание базы данных
В репозиториях Ubuntu 24.04 поставляется PostgreSQL 16, версия которой полностью соответствует требованиям Listmonk.
sudo apt update
sudo apt install -y postgresql curl
sudo systemctl enable --now postgresqlСоздайте роль и базу данных в рамках одной сессии psql. Флаг -v ON_ERROR_STOP=1 заставляет psql завершить работу при первой же ошибке, чтобы опечатка не привела к созданию частично настроенной базы данных.
sudo -u postgres psql -v ON_ERROR_STOP=1 <<'SQL'
CREATE USER listmonk WITH PASSWORD 'pick-a-long-random-password';
CREATE DATABASE listmonk OWNER listmonk;
SQLФлаг OWNER listmonk — это не формальность. При установке схемы создаются таблицы, типы данных, индексы и функции, поэтому роль должна быть владельцем базы данных. Если указать Listmonk базу данных, владельцем которой является другая роль, установка прервется с ошибкой permission denied, даже если вы уже выполнили GRANT CONNECT.
Перед продолжением убедитесь, что база данных создана.
sudo -u postgres psql -tAc "SELECT datname FROM pg_database WHERE datname='listmonk';"Эта команда выведет listmonk. Пустая строка означает, что инструкция CREATE не была выполнена, поэтому проверьте вывод psql еще раз.
Установка бинарного файла Listmonk
Listmonk выпускает статический бинарный файл для каждой архитектуры. Сначала проверьте свою, так как бинарный файл amd64 на ARM VPS — это файл, который ядро откажется выполнять.
dpkg --print-architecture
cd /tmp
curl -fsSLO https://github.com/knadh/listmonk/releases/download/v6.2.0/listmonk_6.2.0_linux_amd64.tar.gz
tar -xzf listmonk_6.2.0_linux_amd64.tar.gz
sudo install -m 755 listmonk /usr/bin/listmonk
listmonk --versionНа ARM VPS замените amd64 на arm64 в имени файла. listmonk --version вывод версии программы — ваше первое доказательство того, что бинарный файл соответствует архитектуре машины.
Генерация config.toml и ограничение доступа
--new-config записывает config.toml в текущую рабочую директорию. Именно поэтому cd находится внутри sh -c, а не перед sudo.
sudo install -d -m 750 /etc/listmonk
sudo sh -c 'cd /etc/listmonk && listmonk --new-config'Сгенерированный файл невелик. В разделе [app] параметр address = "localhost:9000" привязывает HTTP-сервер только к loopback-интерфейсу, поэтому панель администратора недоступна из интернета, пока вы не установите перед ней reverse proxy. Оставьте эту строку без изменений. В разделе [db] вы найдете host = "localhost", port = 5432, user = "listmonk", database = "listmonk" и ssl_mode = "disable". Эти значения по умолчанию уже соответствуют созданной вами базе данных, поэтому единственная строка, которую необходимо изменить — это пароль.
ssl_mode = "disable" подходит, пока Postgres слушает loopback на том же узле, так как этот трафик не покидает пределы машины. Перенесите базу данных на другой хост и установите require, иначе пароль будет передаваться по сети в открытом виде.
Отредактируйте строку с паролем в разделе [db], чтобы она соответствовала роли, затем создайте сервисную учетную запись и ограничьте доступ к файлу для всех остальных пользователей.
sudo useradd --system --home-dir /var/lib/listmonk --create-home --shell /usr/sbin/nologin listmonk
sudo chown -R root:listmonk /etc/listmonk
sudo chmod 640 /etc/listmonk/config.tomlТеперь сервисная учетная запись может читать файл, а никто другой — нет.
sudo -u listmonk cat /etc/listmonk/config.toml > /dev/null && echo readable
stat -c '%U:%G %a' /etc/listmonk/config.tomlПервая команда выводит readable. Вторая выводит root:listmonk 640. Любая другая непривилегированная учетная запись при попытке выполнить cat получит Permission denied, в этом и заключается смысл: файл содержит пароль от базы данных в открытом виде, а на сервере обычно работает более одного пользователя. Тот же принцип применим к любому запускаемому вами сервису, поэтому изучите принцип наименьших привилегий для сервисных пользователей и применяйте его везде.
Создание схемы с помощью --install
--install создает таблицы и заполняет их настройками по умолчанию. Укажите данные первого администратора через переменные окружения, чтобы учетная запись существовала еще до того, как панель станет доступна.
sudo -u listmonk env LISTMONK_ADMIN_USER=admin \
LISTMONK_ADMIN_PASSWORD='another-long-random-password' \
listmonk --config /etc/listmonk/config.toml --install --yes--yes отвечает на запрос подтверждения. Прочитайте этот запрос перед автоматизацией, так как --install — это установщик для первого запуска, и он удаляет существующую схему Listmonk. Повторный запуск на рабочей базе данных приведет к потере всех подписчиков. В скриптах, которые могут выполняться несколько раз, используйте --install --idempotent --yes: она ничего не делает, если таблицы уже существуют. Изменения схемы, которые поставляются в новых релизах, применяются через --upgrade, а не через --install.
Проверяйте результат со стороны базы данных, а не через браузер.
sudo -u postgres psql -d listmonk -c '\dt'
sudo -u postgres psql -d listmonk -tAc "SELECT username FROM users;"Первая команда выводит список таблиц Listmonk, среди которых subscribers, lists, campaigns, templates и bounces. Вторая команда выводит admin. Пустой результат второй команды означает, что переменные окружения не были переданы процессу, поэтому панель предложит создать первого пользователя через браузер.
Запуск Listmonk через systemd
Создайте /etc/systemd/system/listmonk.service.
[Unit]
Description=Listmonk newsletter and mailing list manager
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
Type=simple
User=listmonk
Group=listmonk
WorkingDirectory=/var/lib/listmonk
ExecStart=/usr/bin/listmonk --config /etc/listmonk/config.toml
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
[Install]
WantedBy=multi-user.targetWorkingDirectory важен, так как Listmonk разрешает относительные пути, включая путь для загрузки медиафайлов, относительно него. After=postgresql.service лишь определяет порядок запуска, но не ожидает готовности Postgres принимать соединения, поэтому Restart=on-failure решает проблему, когда Listmonk запускается слишком рано и не может подключиться.
sudo systemctl daemon-reload
sudo systemctl enable --now listmonk
ss -ltnp | grep 9000
curl -sI http://127.0.0.1:9000/ss должен показывать 127.0.0.1:9000 в состоянии LISTEN. curl, возвращающий любую строку HTTP-статуса, означает, что сервер отвечает. curl, завершающийся с Connection refused, означает, что процесс упал во время запуска, а journalctl -u listmonk -n 50 --no-pager покажет причину. Учтите, что enable --now — это та часть, которая сохраняется после перезагрузки: процесс, запущенный вручную, исчезнет после следующего обновления ядра.
Размещение Nginx и TLS перед приложением
Listmonk работает по обычному HTTP на loopback, поэтому Nginx выполняет TLS (transport layer security) termination и перенаправляет запросы.
server {
listen 443 ssl;
server_name lists.example.com;
client_max_body_size 25m;
location / {
proxy_pass http://127.0.0.1:9000;
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;
}
}Значение client_max_body_size необходимо увеличить, так как импорт подписчиков и загрузка медиафайлов представляют собой POST-запросы с файлами, а Nginx по умолчанию отклоняет всё, что больше 1 MB, с ошибкой 413 Request Entity Too Large. Выпустите сертификат с помощью certbot, который также автоматически добавит строки listen 443 ssl и настроит редирект с 80 порта: шаги описаны в руководстве по Let's Encrypt для Nginx. Откройте порты 80 и 443, а порт 9000 оставьте закрытым, так как прокси обращается к нему через loopback. Если настройки брандмауэра ещё не менялись, начните с основ работы с ufw.
Затем откройте панель администратора и укажите в настройках (Settings) корневой URL (root URL) как https://lists.example.com. При свежей установке там стоит http://localhost:9000, и Listmonk вставляет это значение во все ссылки на отписку и медиафайлы, которые добавляет в письма. Если отправить рассылку до изменения этого параметра, каждый получатель получит ссылки, указывающие на его собственный компьютер. Они не будут работать у читателя, а спам-фильтры воспримут это как признак того, что отправитель не может настроить собственный домен.
Подключение SMTP, которого нет в config.toml
Выполните поиск config.toml для раздела SMTP, и вы его не найдете. Настройки почты хранятся в базе данных, в таблице settings, и редактируются в панели администратора в разделе Settings и SMTP. Именно поэтому сгенерированный файл остается таким коротким, а также поэтому изменение настроек SMTP не требует перезапуска.
Существует два честных варианта для самого SMTP-сервера. Запустите свой собственный, что полностью возлагает ответственность за репутацию на вас и само по себе является серьезным проектом: запуск собственного почтового сервера с помощью Mailcow описывает, что это включает. Либо укажите Listmonk на транзакционный релей и позвольте кому-то другому отвечать за репутацию IP-адреса.
В любом случае используйте порт 587 с STARTTLS или порт 465 с неявным TLS. Не рассчитывайте на исходящий порт 25. Большинство провайдеров VPS блокируют его по умолчанию для новых учетных записей, а заблокированный порт 25 выглядит в точности как зависшее соединение, поскольку пакеты отбрасываются, а не отклоняются, поэтому клиент ожидает тайм-аута вместо быстрого завершения с ошибкой.
Протестируйте систему, прежде чем доверять ей. Создайте список, добавьте свой собственный адрес в качестве подписчика и отправьте кампанию одному получателю. Откройте полученное сообщение и прочитайте полные заголовки. Заголовок Authentication-Results, добавленный принимающей стороной, покажет вам, прошли ли проверку SPF и DKIM.
Доставляемость — это главная задача
Listmonk формирует сообщение, управляет списком рассылки и передает почту для отправки. Решение о том, попадет ли письмо во «Входящие», принимает принимающий провайдер, основываясь на IP-адресе отправителя и домене. У IP-адреса нового VPS нет истории, а отсутствие истории воспринимается всеми крупными почтовыми провайдерами как подозрительный признак.
Четыре компонента обязательны:
- SPF (sender policy framework) TXT-запись, указывающая хост, которому разрешено отправлять почту от имени вашего домена.
- DKIM (domainkeys identified mail) ключ, опубликованный в виде TXT-записи; подпись должна выполняться почтовым сервером, а не Listmonk.
- DMARC (domain based message authentication, reporting and conformance) запись, которая сообщает получателям, как действовать, если первые два метода проверки не пройдены.
- Почтовый ящик для обработки возвратов (bounce mailbox), который считывает Listmonk, чтобы адреса, отклонившие письмо, удалялись из списка, а не подвергались бесконечным попыткам повторной отправки.
Начинайте рассылку постепенно. Домен, который никогда не отправлял почту и внезапно доставляет десять тысяч сообщений за час, выглядит как взломанная учетная запись, поэтому он будет отфильтрован соответствующим образом. Начните с наиболее активных подписчиков и увеличивайте объем рассылки в течение нескольких дней.
В каждом шаблоне должна быть рабочая ссылка для отписки. В шаблоне Listmonk это {{ UnsubscribeURL }}, а тело кампании размещается там, где находится {{ template "content" . }}; этот тег должен присутствовать в шаблоне ровно один раз. Кампания без ссылки для отписки провоцирует жалобы на спам вместо отписок, а жалобы — это самый быстрый способ потерять репутацию отправителя, на создание которой ушли недели.
Резервное копирование и требования к восстановлению
Два компонента должны быть вынесены за пределы сервера: дамп базы данных и config.toml. Добавьте каталог с медиафайлами, если вы загружаете изображения в кампании.
sudo -u postgres pg_dump -Fc listmonk > listmonk-$(date +%F).dumpЭтот дамп содержит подписчиков, кампании, шаблоны и все настройки, включая учетные данные SMTP, поэтому зашифруйте его и храните вне этого сервера. Планирование этой задачи — решенная проблема: см. зашифрованное резервное копирование restic на удаленное хранилище. Файл config.toml состоит из нескольких строк, но содержит пароль от базы данных, поэтому обращайтесь с ним так же.
Обновления выполняются в строго определенном порядке. Остановите сервис, создайте дамп, замените бинарный файл в /usr/bin, выполните listmonk --config /etc/listmonk/config.toml --upgrade, затем запустите сервис. Миграции схемы выполняются только в прямом направлении, поэтому этот дамп — ваш единственный путь для отката.
Почему Listmonk не запускается?
Сначала изучите журнал с помощью journalctl -u listmonk -n 50 --no-pager. Почти каждый сбой при запуске описывается одной строкой в блоке [db].
pq: password authentication failed for user "listmonk" означает, что пароль в [db] не совпадает с ролью в Postgres. Префикс pq — это сообщение драйвера Postgres об отказе со стороны сервера, значит, конфигурация была прочитана корректно, а учетные данные оказались неверными. Сбросьте пароль роли через sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';" и укажите в файле ту же самую строку.
pq: database "listmonk" does not exist означает, что значение database в [db] не соответствует существующей базе данных. sudo -u postgres psql -l выводит список того, что фактически находится на сервере, включая вариант с опечаткой, которую вы допустили.
permission denied во время --install означает, что роль может подключиться, но не является владельцем базы данных, поэтому не может создавать в ней таблицы. Исправьте это с помощью sudo -u postgres psql -c "ALTER DATABASE listmonk OWNER TO listmonk;" и запустите установку снова.
Сервис не запускается, а в журнале указан файл конфигурации. Процесс, запущенный от имени listmonk, не может открыть config.toml, если он оставлен с правами root:root и режимом 600. stat -c '%U:%G %a' /etc/listmonk/config.toml должен выводить root:listmonk 640, а владелец родительского каталога должен быть root:listmonk 750.
Панель работает, но почта не приходит. Это не проблема запуска. Сначала проверьте настройки SMTP, а затем журнал самой рассылки в панели администратора: там фиксируется ошибка, которую почтовый сервер вернул при каждой попытке отправки.
FAQ
Do I need my own mail server to use Listmonk?
No. Listmonk is not a mail server. It needs SMTP credentials for a server that accepts your mail and delivers it, which can be a transactional relay or a mail server you run yourself. Set those credentials under Settings and SMTP in the admin panel, not in config.toml, because the mail settings live in the database. Use port 587 with STARTTLS or port 465 with implicit TLS, since most VPS providers block outbound port 25 on new accounts.
Why do the links in my campaign point at localhost:9000?
The root URL setting is still at its install default of http://localhost:9000. Listmonk writes that value into unsubscribe links and media URLs at the moment a campaign is sent. Open Settings in the admin panel, set the root URL to your real HTTPS address, and save. Messages already delivered cannot be corrected, so send yourself a test campaign and click the unsubscribe link in it before you mail a real list.
Will running --install again wipe my subscribers?
Yes. --install is the first time installer and it drops the existing Listmonk schema, and --yes removes the prompt that would have warned you. In any script that might run twice, use --install --idempotent --yes, which does nothing when the tables already exist. To apply the schema changes in a new release, stop the service, take a pg_dump, then run --upgrade.
Why does Listmonk say password authentication failed for user listmonk?
The password in the [db] block of /etc/listmonk/config.toml does not match the Postgres role of the same name. The journal line is pq: password authentication failed for user "listmonk", and pq is the Postgres driver passing on the server's rejection, which means the config file was found and read. Reset the role's password with sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';", write the identical string into the config file, then run sudo systemctl restart listmonk.