Як встановити Discourse на VPS за допомогою Docker
Покрокове встановлення Discourse через офіційний Docker launcher: вимоги до RAM і swap, домен, SMTP, app.yml, rebuild, TLS та reverse proxy.
Встановлення Discourse на VPS: один контейнер, один файл конфігурації
Щоб встановити Discourse на VPS, запустіть власний інсталятор проєкту, дайте відповіді в короткому майстрі та дочекайтеся складання. Discourse постачається як один Docker-контейнер, що містить Rails-застосунок, PostgreSQL, Redis і nginx. Усе, що ви змінюватимете пізніше, зберігається в одному файлі, /var/discourse/containers/app.yml, а кожна зміна застосовується до сайту через повторне складання.
Офіційний спосіб встановлення — discourse_docker: shell-скрипт launcher і набір шаблонів YAML. Discourse не підтримує файл Compose, який ви створюєте самостійно, а контейнер не призначений для ручного розділення на окремі компоненти. Якщо ви звикли до запуску сервісів на VPS за допомогою Docker Compose, очікуйте іншої структури. Тут немає docker compose up -d, а ./launcher rebuild app є розгортанням.
Що потрібно Discourse до початку роботи
Чотири вимоги часто стають несподіваною перешкодою, і кожна з них може виникнути ще до появи сторінки входу.
- Пам’ять. Один контейнер запускає PostgreSQL, Redis, Sidekiq і вебсервер Ruby. На етапі збирання компілюються ресурси, тому потрібно більше пам’яті, ніж для роботи готового сайту.
- Справжнє доменне ім’я. У прикладі конфігурації, що постачається з продуктом, прямо зазначено: "Discourse will not work with a bare IP number."
- Канал для вихідної пошти. Повідомлення про активацію облікових записів, скидання паролів, запрошення адміністраторів і дайджести надсилаються через SMTP (простий протокол передавання пошти).
- Вільні порти 80 і 443 на хості, якщо ви навмисно не розміщуєте Discourse за вже запущеним проксі.
The data behind this chart
[
{
"label": "Documented minimum",
"ram_gb": 1,
"storage_gb": 10
},
{
"label": "Documented recommended",
"ram_gb": 2,
"storage_gb": 20
}
]В офіційній інструкції зі встановлення мінімально потрібними вказано 1 ГБ RAM зі swap і 10 ГБ дискового простору. Рекомендовано використовувати 2 ГБ RAM і 20 ГБ дискового простору. Перший рядок слід сприймати як обсяг ресурсів, за якого інсталятор завершить роботу, а не як обсяг для стабільної роботи спільноти. Ця різниця важлива, оскільки пікове споживання пам’яті припадає на етап збирання, а не на мережевий трафік.
Вкажіть домен на сервер перед установленням
Створіть A-запис для імені хоста, яке використовуватимете, а потім перевірте його безпосередньо із сервера.
dig +short forum.example.com
curl -4 -s https://ifconfig.coОбидві команди мають вивести ту саму адресу. Вони повинні збігатися, оскільки майстер налаштування перевіряє з’єднання з вашим іменем хоста, а запис, який досі вказує на іншу адресу, не проходить цю перевірку. Запис, створений дві хвилини тому, також може залишатися в кеші, тому дочекайтеся завершення старого TTL (time to live), а не намагайтеся обійти помилку майстра.
Заздалегідь визначте, чи оброблятиме запис CDN як проксійований. Проксійований запис приховує адресу сервера, після чого запит сертифіката для контейнера завершується помилкою, оскільки на виклик ACME (automatic certificate management environment) відповідає проксі, а не Discourse. Для першого встановлення залиште запис без проксі.
Запустіть офіційний інсталятор
Одна команда встановлює git, встановлює Docker за допомогою власного інсталяційного скрипту Docker, клонує discourse_docker у /var/discourse і запускає майстер налаштування.
wget -qO- https://raw.githubusercontent.com/discourse/discourse_docker/main/install-discourse | sudo bashЯкщо Docker уже встановлено на сервері й ви хочете бачити кожен крок окремо, виконайте ці дії вручну.
sudo -s
git clone https://github.com/discourse/discourse_docker.git /var/discourse
cd /var/discourse
./discourse-setupЗапускайте команду від імені root. Якщо запустити discourse-setup від імені звичайного користувача, він одразу завершується з помилкою This script must be run as root. Please sudo or log in as root first.. Якщо Docker не встановлено на сервері, команда завершується з помилкою Docker is not installed. Please install Docker first., оскільки ручне клонування нічого не встановлює.
Що запитує майстер налаштування і що він записує
Станом на August 2026 discourse-setup є тонкою оболонкою. Вона запускає discourse/setup-wizard:release як контейнер із host network і змонтованим Docker socket, щоб майстер міг перевірити машину, яку налаштовує. Майстер запитує hostname та email-адреси адміністраторів, а потім параметри SMTP. Він записує containers/app.yml, а потім повторно виконує збірку.
Перед початком варто знати про дві особливості. Якщо на машині мало пам’яті та немає swap, майстер зупиняється і пропонує створити його: оболонка створює 2 GB /swapfile, додає його до /etc/fstab, задає vm.swappiness = 10 у /etc/sysctl.d/30-discourse-swap.conf і повторно запускає майстер. Після завершення майстер виводить Rebuilding app in 5 seconds (Ctrl+C to cancel)... і запускає ./launcher rebuild app на host. Ця збірка на невеликому VPS триває кілька хвилин. Перша збірка найдовша, оскільки кожен asset компілюється з нуля.
./discourse-setup --help містить список важливих flags на випадок проблем. --skip-rebuild записує конфігурацію без збірки, а --skip-connection-test пропускає перевірки DNS і портів. Використовуйте --skip-connection-test лише тоді, коли вже знаєте причину помилки перевірки, наприклад якщо host розташований за мережевим firewall, який ви контролюєте.
Прочитайте app.yml перед першою повторною збіркою
Майстер записує файл, який тепер потрібно підтримувати самостійно. Відкрийте його за допомогою sudo nano /var/discourse/containers/app.yml. Саме ці параметри визначають майже все.
templates:
- "templates/postgres.template.yml"
- "templates/redis.template.yml"
- "templates/web.template.yml"
- "templates/web.ratelimited.template.yml"
## Uncomment these two lines if you wish to add Lets Encrypt (https)
#- "templates/web.ssl.template.yml"
#- "templates/web.letsencrypt.ssl.template.yml"
expose:
- "80:80" # http
- "443:443" # https
env:
DISCOURSE_HOSTNAME: "forum.example.com"
DISCOURSE_DEVELOPER_EMAILS: "you@example.com"
DISCOURSE_SMTP_ADDRESS: smtp.example.com
DISCOURSE_SMTP_PORT: 587
DISCOURSE_SMTP_USER_NAME: user@example.com
DISCOURSE_SMTP_PASSWORD: "your-smtp-password"DISCOURSE_HOSTNAME — це адреса, за якою сайт приймає запити. Discourse формує посилання на її основі. Тому неправильне значення може призвести до того, що сайт спочатку завантажиться, а потім перенаправить вас в інше місце. DISCOURSE_DEVELOPER_EMAILS — це список адрес, розділених комами. Під час першої реєстрації ці адреси автоматично отримують права адміністратора. Укажіть там власну адресу та зареєструйтеся з нею, оскільки саме так створюється перший обліковий запис адміністратора.
Файл зберігає пароль SMTP у відкритому вигляді, тому обмежте доступ до каталогу за допомогою sudo chmod 700 /var/discourse/containers. Це також YAML, а отже, пробільні відступи є частиною конфігурації. Неправильно вирівняний ключ спричинить помилку синтаксичного аналізу під час збірки, і сайт не буде створено. Один із типових ризиків описано безпосередньо в прикладі файлу. # у паролі без лапок позначає початок коментаря, тому пароль, який містить цей символ, потрібно взяти в лапки.
Електронна пошта — етап, на якому зупиняється більшість інсталяцій
Станом на серпень 2026 року майстер налаштування дає змогу пропустити SMTP і використовувати облікові записи Discourse ID, а app.yml має відповідний перемикач DISCOURSE_SKIP_EMAIL_SETUP, описаний там як пропуск перевірки налаштування електронної пошти. Для першого ознайомлення з програмою це прийнятно. Для спільноти це невдалий вибір, оскільки без вихідної пошти ніхто не зможе активувати обліковий запис або скинути пароль.
Практична проблема полягає в тому, що більшість провайдерів VPS блокують вихідний порт 25, тому звичайний поштовий сервер на цьому сервері не надсилатиме повідомлення. Використовуйте автентифікований relay через порт 587 або порт 465 із неявним TLS (transport layer security). Для порту 465 установіть DISCOURSE_SMTP_FORCE_TLS: true — саме це рекомендує приклад конфігурації для такого порту. Перед повторною збіркою перевірте доступність порту з хоста.
nc -vz smtp.example.com 587У разі успішної перевірки результат містить один рядок, що закінчується на succeeded!. Якщо команда зависає, а потім завершується після очікування, це означає, що порт заблокований на шляху вихідного трафіку з вашого VPS. Жодне налаштування Discourse цього не виправить. Використовуйте порт, дозволений вашим провайдером, або попросіть провайдера відкрити цей порт.
Після запуску сайту надішліть тестове повідомлення зі сторінки Email у розділі Admin, а потім перегляньте вкладки Skipped і Bounced на цій самій сторінці. У цих вкладках Discourse записує пошту, яку відмовилася надсилати, і пошту, яку відхилив relay. Там також указано причину, тому перевірка цих вкладок швидша за перегляд журналів.
TLS: дозвольте контейнеру отримати власний сертифікат
Якщо Discourse використовує порти 80 і 443, скористайтеся вбудованим механізмом отримання сертифіката. Розкоментуйте два рядки шаблону SSL, наведені вище, а потім повторно виконайте складання. Шаблон керує acme.sh, зберігає сертифікати у спільному томі в /shared/ssl, регулярно поновлює їх у контейнері та налаштовує Discourse на примусове використання HTTPS.
Порт 80 має залишатися доступним з Інтернету, оскільки саме через нього проходить перевірка HTTP challenge. Якщо firewall дозволяє лише порт 443, складання завершиться успішно, але сертифікат не буде отримано. Одразу після повторного складання перевірте результат за допомогою ./launcher logs app.
Чи варто розміщувати nginx або Caddy перед Discourse?
Якщо Discourse — єдиний вебсервіс на VPS, не робіть цього. Контейнер уже запускає оптимізований nginx, а другий проксі додає ще один проміжний вузол, ще один сертифікат для поновлення та нове джерело помилок у заголовках.
Розміщуйте проксі перед Discourse, якщо той самий VPS обслуговує інші сайти. Додайте templates/web.socketed.template.yml до списку шаблонів, закоментуйте обидва рядки expose і залиште два SSL-шаблони закоментованими. Після цього контейнер прослуховує unix-сокет за адресою /var/discourse/shared/standalone/nginx.http.sock і взагалі не займає портів. Порти 80 і 443 залишаються вільними для вашого проксі.
server {
listen 443 ssl;
server_name forum.example.com;
location / {
proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;
proxy_set_header Host $http_host;
proxy_http_version 1.1;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
}
}Двокрапка після .sock є частиною синтаксису unix-сокета nginx, а sudo nginx -t відмовляється приймати конфігурацію без неї. X-Forwarded-Proto також є обов’язковим. Discourse формує абсолютні посилання, тому без цього заголовка він створює посилання http:// на сторінці HTTPS, а браузери блокують їх як змішаний вміст. Коли контейнер працює через сокет, налаштування TLS стає вашим завданням. Отримайте сертифікат на хості за допомогою Certbot в Ubuntu 24.04 і nginx. Якщо ви ще не вибрали проксі, у матеріалі порівняння nginx, Caddy і Traefik розглянуто компроміси цього вибору.
Перезбирання, оновлення та команди, які ви фактично використовуватимете
cd /var/discourse
./launcher rebuild apprebuild знищує запущений контейнер, створює новий на основі app.yml і запускає його. Сайт буде недоступний протягом усього процесу збирання, тому кожну зміну конфігурації слід розглядати як планове технічне обслуговування тривалістю кілька хвилин.
Зміна лише значень у env: цього не потребує. ./launcher destroy app && ./launcher start app повторно створює контейнер з уже зібраного образу, і це займає кілька секунд. Будь-яка зміна в templates: або hooks: змінює сам образ, тому потребує повного перезбирання.
Оновлення надходять двома способами. Точкові релізи застосовуються через вебінтерфейс у /admin/upgrade за допомогою плагіна docker_manager, який app.yml клонує під час збирання. Зміни базового образу або шаблонів надходять із git.
cd /var/discourse
git pull
./launcher rebuild appСаме під час перезбирання малі сервери найчастіше виходять з ладу, оскільки компіляція ресурсів створює пікове споживання пам’яті в усій системі. Якщо збирання переривається на середині, а dmesg показує рядок на кшталт Out of memory: Killed process із назвою процесу ruby, під час збирання закінчилася оперативна пам’ять, хоча до цього сайт працював нормально. Додайте swap і повторно запустіть перезбирання.
./launcher logs app
./launcher enter app
./launcher cleanuplogs виводить дані контейнера, enter відкриває shell усередині нього, а cleanup видаляє контейнери, зупинені понад 24 години тому. Час від часу запускайте cleanup, оскільки кожне перезбирання залишає старий контейнер, а диск на невеликому VPS непомітно заповнюється.
Резервні копії та файл, якого немає в резервній копії
Створюйте резервні копії на сторінці Backups у розділі Admin. Архів зберігається на хості за шляхом /var/discourse/shared/standalone/backups/default/. Те саме завдання можна виконати з shell.
cd /var/discourse
./launcher enter app
discourse backupdiscourse restore <filename> скасовує цю дію, а відновлення заборонено, доки не виконати discourse enable_restore. Це обмеження потрібне, щоб випадкова команда не перезаписала робочий форум.
Два моменти потрібно перевірити самостійно. Архів містить базу даних і завантажені файли лише тоді, коли ввімкнено налаштування резервного копіювання, яке додає uploads. Тому перевірте це налаштування, перш ніж покладатися на архів. Архів ніколи не містить app.yml. Тому для відновлення на новому VPS все одно потрібні ім’я хоста та блок SMTP. Скопіюйте цей файл за межі сервера.
Архів також зберігається на тому самому диску, що й сайт, який він захищає. Це не є резервною копією. Регулярно копіюйте архів в інше місце.
rsync -avz root@forum.example.com:/var/discourse/shared/standalone/backups/default/ ~/discourse-backups/Скільки RAM потребує активний форум
Bootstrap встановлює UNICORN_WORKERS і db_shared_buffers на основі виявлених параметрів пам’яті та CPU, а приклад конфігурації обмежує shared buffers чвертю загального обсягу пам’яті. Кожен worker unicorn є окремим повноцінним процесом Ruby, а Sidekiq виконує фонові завдання паралельно з ними, тому використання пам’яті залежить від кількості одночасних запитів, а не від кількості зареєстрованих користувачів. Форум із кількома сотнями користувачів і невисокою активністю не створює значного навантаження.
Не розраховуйте параметри сервера за числом із будь-якої статті, зокрема цієї. Вимірюйте навантаження у власній системі.
free -m
docker stats --no-streamПостійне використання swap разом із повільним завантаженням сторінок означає, що RAM недостатньо. Якщо обсяг використаної пам’яті стабільний, а сторінки завантажуються повільно, причина зазвичай інша, тому перед переходом на дорожчий план прочитайте ./launcher logs app. Додайте також перевірку із зовнішньої системи, оскільки форум, у якого закінчується пам’ять о 3am, може відмовити без явних ознак: self-hosted моніторинг стану Uptime Kuma на окремому хості повідомить про проблему раніше, ніж про неї дізнаються користувачі.
Коли Discourse — неправильний вибір
Discourse — це великий застосунок зі складним встановленням і циклом перебудови після кожної зміни параметра в app.yml. Витрати виправдані, якщо вам потрібні повноцінні інструменти модерації та пошук, який продовжує працювати навіть за великого архіву. Для тридцяти людей, яким просто потрібен майданчик для спілкування, це надмірне рішення. Спочатку прочитайте порівняння self-hosted форумного програмного забезпечення і вибирайте Discourse через потрібні вам можливості, а не тому, що ви вже знали цю назву.
FAQ
Чи можна встановити Discourse на VPS без доменного імені?
Ні. У стандартній конфігурації зазначено, що Discourse не працюватиме з однією лише IP-адресою, а потрібен DISCOURSE_HOSTNAME. Discourse формує абсолютні посилання на основі цього імені хоста, тому IP-адреса порушує посилання та унеможливлює випуск сертифіката. Створіть A-запис до початку встановлення та перевірте за допомогою dig +short forum.example.com, що він вказує на адресу вашого сервера.
Чи потрібно налаштовувати SMTP для завершення встановлення?
Станом на August 2026 це можна пропустити. Майстер налаштування пропонує натомість вхід через Discourse ID, а app.yml містить перемикач, який пропускає перевірку налаштування електронної пошти. Для повноцінного використання налаштуйте SMTP, оскільки активація облікових записів і скидання паролів виконуються електронною поштою. Використовуйте автентифікований relay на порту 587 або 465, оскільки більшість VPS-провайдерів блокують вихідний порт 25.
Чому перебудова Discourse перервалася?
Найчастіша причина — нестача пам’яті. Компіляція ресурсів під час збирання потребує більше пам’яті, ніж запущений сайт, тому сервер, який без проблем обслуговує форум, усе одно може не виконати його перебудову. Якщо dmesg показує Out of memory: Killed process із назвою процесу ruby, додайте swap (власний swapfile майстра має розмір 2 GB) і знову виконайте ./launcher rebuild app. Якщо збирання зупиняється через помилку YAML, це вказує на помилку відступів у app.yml.
Чи потрібно розміщувати Discourse за власним nginx або Caddy?
Лише якщо VPS також обслуговує інші сайти. Якщо на сервері працює тільки Discourse, дозвольте контейнеру використовувати порти 80 і 443 та самостійно випускати сертифікат. Так буде менше компонентів, які потрібно налаштовувати. Щоб спільно використовувати сервер, додайте templates/web.socketed.template.yml, закоментуйте рядки expose і налаштуйте проксування до Unix-сокета за адресою /var/discourse/shared/standalone/nginx.http.sock. Передавайте X-Forwarded-Proto, інакше Discourse генеруватиме посилання http:// на сторінці HTTPS.
Як створити резервну копію self-hosted Discourse?
Скористайтеся сторінкою Backups в Admin або виконайте discourse backup після ./launcher enter app. Архіви зберігаються на хості за шляхом /var/discourse/shared/standalone/backups/default/. Переконайтеся, що параметр включення завантажених файлів увімкнено, скопіюйте /var/discourse/containers/app.yml разом з архівом і перенесіть обидва файли на інший сервер, оскільки резервна копія на тому самому диску, що й сайт, не переживе збій, від якого вона має захистити.