Listmonk на VPS: встановлення розсилки в Ubuntu 24.04
Покроково встановіть Listmonk v6.2.0 на Ubuntu 24.04: PostgreSQL, config.toml, systemd, TLS і SMTP. Дізнайтеся, чому доставка залежить від репутації сервера.
Що потрібно для самостійно розміщеної розсилки на Listmonk
Listmonk — це менеджер інформаційних розсилок і списків розсилки, який розміщується на власному сервері: один двійковий файл Go, одна база даних PostgreSQL, один файл конфігурації та один модуль systemd. Невеликий VPS без проблем запускає його, оскільки Listmonk зберігає підписників і ставить кампанії в чергу, але сам не доставляє пошту. Він передає кожне повідомлення на сервер SMTP (простий протокол передавання пошти), тому показник доставлення визначається репутацією цього сервера, а не цим програмним забезпеченням.
У цьому посібнику описано встановлення Listmonk v6.2.0 — поточного випуску станом на July 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;
SQLOWNER 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 (безпека транспортного рівня) і пересилає запит.
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 потрібно збільшити, оскільки імпорт підписників і завантаження медіафайлів передаються як файли, а nginx за замовчуванням відхиляє все, що перевищує 1 MB, з помилкою 413 Request Entity Too Large. Отримайте сертифікат за допомогою certbot. Він також додасть директиви listen 443 ssl і перенаправлення з порту 80. Інструкції наведено в посібнику зі створення сертифіката Let's Encrypt для nginx. Відкрийте порти 80 і 443, а порт 9000 залиште закритим, оскільки проксі звертається до нього через loopback. Якщо правила брандмауера ще не налаштовано, почніть із основ налаштування брандмауера ufw.
Потім відкрийте панель адміністратора та в розділі Settings задайте кореневу URL-адресу https://lists.example.com. У новій інсталяції встановлено значення http://localhost:9000, і Listmonk записує його в кожне посилання для скасування підписки та URL-адресу медіафайлу, які додає до електронного листа. Якщо надіслати кампанію до зміни цього значення, кожен одержувач отримає посилання, що вказують на його власний комп’ютер. Для одержувача вони не працюватимуть, а для спам-фільтра виглядатимуть так, ніби відправник не вміє налаштувати власний домен.
Підключення SMTP, параметри якого не зберігаються у config.toml
Ви не знайдете розділ SMTP у config.toml. Параметри пошти зберігаються в базі даних, у таблиці settings. Редагуйте їх в адміністративній панелі в розділах Settings і SMTP. Тому згенерований файл залишається таким коротким. Через це для зміни SMTP не потрібно перезапускати службу.
Для самого SMTP-сервера є два практичні варіанти. Запустити власний сервер. У такому разі репутація повністю залежить від вас, а сам проєкт потребує окремої роботи: у матеріалі як запустити власний поштовий сервер за допомогою Mailcow описано необхідні дії. Або налаштувати Listmonk на транзакційний relay і передати керування репутацією IP-адреси іншому постачальнику.
У будь-якому разі використовуйте порт 587 із STARTTLS або порт 465 із неявним TLS. Не плануйте вихідний трафік через порт 25. Більшість постачальників VPS за замовчуванням блокують його для нових облікових записів. Заблокований порт 25 виглядає так само, як з'єднання, що зависло, оскільки пакети відкидаються, а не відхиляються. Тому клієнт очікує завершення тайм-ауту, замість того щоб одразу повідомити про помилку.
Перевірте налаштування, перш ніж покладатися на них. Створіть список, додайте власну адресу як підписника та надішліть кампанію одному одержувачу. Відкрийте отримане повідомлення й перегляньте всі заголовки. Заголовок Authentication-Results, доданий стороною-одержувачем, показує, чи пройшли перевірку SPF і DKIM.
Доставлення — це основне завдання
Listmonk формує повідомлення, відстежує список і передає пошту далі. Рішення про те, чи потрапить ця пошта до вхідних, ухвалює поштовий провайдер-одержувач, використовуючи IP-адресу відправника та домен відправника. Нова IP-адреса VPS не має жодної історії, а всі великі поштові провайдери вважають відсутність історії помірно підозрілою.
Обов’язковими є чотири речі:
- TXT-запис SPF (sender policy framework), у якому вказано хост, якому дозволено надсилати пошту від імені вашого домену.
- Ключ DKIM (domainkeys identified mail), опублікований як TXT-запис. Підписування має виконувати поштовий сервер, а не Listmonk.
- Запис DMARC (domain based message authentication, reporting and conformance), який повідомляє одержувачам, що робити, якщо перші два механізми не пройшли перевірку.
- Поштова скринька для bounce-повідомлень, яку читає 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.
Панель працює, але пошта не надходить. Це не проблема запуску. Спочатку перевірте Settings і SMTP, а потім власний журнал кампанії в адміністративній панелі. У ньому записано помилку, яку поштовий сервер повернув для кожної спроби.
FAQ
Чи потрібен мені власний поштовий сервер для використання Listmonk?
Ні. Listmonk не є поштовим сервером. Йому потрібні облікові дані SMTP для сервера, який приймає та доставляє вашу пошту. Це може бути транзакційний relay або поштовий сервер, яким ви керуєте самостійно. Укажіть ці облікові дані в розділах Settings і SMTP адміністративної панелі, а не в config.toml, оскільки параметри пошти зберігаються в базі даних. Використовуйте порт 587 із STARTTLS або порт 465 з implicit TLS, оскільки більшість провайдерів VPS блокують вихідний порт 25 для нових облікових записів.
Чому посилання в моїй кампанії ведуть на localhost:9000?
Параметр root URL досі має стандартне значення, встановлене під час інсталяції: http://localhost:9000. Listmonk записує це значення в посилання для скасування підписки та URL-адреси медіафайлів у момент надсилання кампанії. Відкрийте Settings в адміністративній панелі, укажіть справжню HTTPS-адресу як root URL і збережіть зміни. Уже доставлені повідомлення виправити неможливо. Тому надішліть собі тестову кампанію та відкрийте в ній посилання для скасування підписки, перш ніж надсилати повідомлення реальному списку.
Чи видалить повторний запуск --install моїх підписників?
Так. --install — це інсталятор для першого запуску, який видаляє наявну схему Listmonk, а --yes вимикає запит із попередженням. У будь-якому скрипті, який може запускатися двічі, використовуйте --install --idempotent --yes. Ця команда нічого не робить, якщо таблиці вже існують. Щоб застосувати зміни схеми в новому випуску, зупиніть службу, створіть pg_dump, а потім запустіть --upgrade.
Чому Listmonk повідомляє, що автентифікація за паролем для користувача listmonk не вдалася?
Пароль у блоці [db] файлу /etc/listmonk/config.toml не збігається з паролем ролі Postgres з такою самою назвою. Рядок журналу має вигляд pq: password authentication failed for user "listmonk", а pq — це драйвер Postgres, який передає відмову сервера. Це означає, що файл конфігурації знайдено та прочитано. Скиньте пароль ролі за допомогою sudo -u postgres psql -c "ALTER USER listmonk WITH PASSWORD 'new-password';", запишіть такий самий рядок у файл конфігурації, а потім запустіть sudo systemctl restart listmonk.