Discourse в Docker Compose: готовый файл
Если вы выбираете движок форума для open-source проекта или комьюнити продукта, рано или поздно упрётесь в Discourse — с его репутацией, тегами, доверительными уровнями пользователей и вменяемой модерацией это фактически стандарт. Проблема в другом: официальный установщик Discourse исторически завязан на собственный launcher-скрипт и app.yml, а не на обычный docker-compose.yml, и это отпугивает тех, кто привык держать все свои сервисы в одном знакомом формате. Ниже — рабочий compose-файл на базе образов Bitnami, разбор каждой переменной окружения и список мест, где новички теряют время.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что нужно подготовить перед стартом
Discourse — не самое лёгкое приложение. Он тянет за собой PostgreSQL, Redis, фоновый обработчик задач (Sidekiq) и рендеринг Ember-приложения на клиенте, поэтому экономить на ресурсах сервера не стоит.
Минимальные и комфортные требования (ориентир, у вас может отличаться в зависимости от посещаемости и числа плагинов):
| Параметр | Минимум | Комфортно |
|---|---|---|
| RAM | 2 ГБ | 4 ГБ и больше |
| CPU | 1 vCPU | 2 vCPU |
| Диск | 20 ГБ SSD | 40+ ГБ SSD |
| Домен | обязателен | обязателен |
Домен обязателен не как формальность: Discourse жёстко привязывает URL сайта к hostname на этапе инициализации, и поменять его потом — отдельная головная боль с миграцией ссылок в базе. Также заранее понадобится рабочий SMTP: без исходящей почты не работает регистрация, восстановление пароля и уведомления, а Discourse при старте прямо ругается, если почта не настроена. Если своего SMTP нет, можно поднять Mailcow на VPS или взять внешний сервис (Mailgun, Postmark, SendGrid — у всех есть бесплатные лимиты для старта).
Docker и Docker Compose plugin должны быть уже установлены — если ещё нет, это отдельный шаг на чистой машине.
Структура проекта и docker-compose.yml
Создаём рабочую директорию и файл окружения отдельно от самого compose-файла — так секреты не улетают в git по ошибке:
mkdir -p /opt/discourse && cd /opt/discourse
touch .env docker-compose.yml
chmod 600 .env
Сам docker-compose.yml — три сервиса: PostgreSQL, Redis и сам Discourse на образах Bitnami (они держат конфигурацию через переменные окружения, что удобнее для compose, чем собирать связку вручную):
services:
postgresql:
image: bitnami/postgresql:15
restart: unless-stopped
environment:
POSTGRESQL_USERNAME: discourse
POSTGRESQL_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRESQL_DATABASE: discourse
volumes:
- postgresql_data:/bitnami/postgresql
redis:
image: bitnami/redis:7.2
restart: unless-stopped
environment:
REDIS_PASSWORD: ${REDIS_PASSWORD}
volumes:
- redis_data:/bitnami/redis/data
discourse:
image: bitnami/discourse:3
restart: unless-stopped
depends_on:
- postgresql
- redis
ports:
- "127.0.0.1:8080:3000"
environment:
DISCOURSE_HOST: ${DISCOURSE_HOST}
DISCOURSE_DATABASE_HOST: postgresql
DISCOURSE_DATABASE_PORT_NUMBER: 5432
DISCOURSE_DATABASE_USER: discourse
DISCOURSE_DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
DISCOURSE_DATABASE_NAME: discourse
DISCOURSE_REDIS_HOST: redis
DISCOURSE_REDIS_PASSWORD: ${REDIS_PASSWORD}
DISCOURSE_USERNAME: admin
DISCOURSE_PASSWORD: ${DISCOURSE_ADMIN_PASSWORD}
DISCOURSE_EMAIL: ${DISCOURSE_ADMIN_EMAIL}
DISCOURSE_SMTP_HOST: ${SMTP_HOST}
DISCOURSE_SMTP_PORT: ${SMTP_PORT}
DISCOURSE_SMTP_USER: ${SMTP_USER}
DISCOURSE_SMTP_PASSWORD: ${SMTP_PASSWORD}
DISCOURSE_SMTP_PROTOCOL: tls
volumes:
- discourse_data:/bitnami/discourse
volumes:
postgresql_data:
redis_data:
discourse_data:
Порт публикуем только на 127.0.0.1 — наружу приложение отдаёт reverse proxy, о нём ниже. Это стандартная практика для любого веб-сервиса в контейнере: сам контейнер никогда не должен слушать 0.0.0.0 напрямую в проде.
Обратите внимание: конкретные названия переменных окружения у образов Bitnami привязаны к версии тега и иногда меняются между релизами. Перед боевым запуском стоит свериться со страницей образа bitnami/discourse на Docker Hub или в реестре Bitnami — там всегда актуальный список переменных для конкретного тега, который вы используете.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверПеременные окружения и секреты
Файл .env рядом с compose-файлом:
DISCOURSE_HOST=forum.example.com
DISCOURSE_ADMIN_EMAIL=admin@example.com
DISCOURSE_ADMIN_PASSWORD=сложный-пароль-минимум-12-символов
POSTGRES_PASSWORD=другой-сложный-пароль
REDIS_PASSWORD=ещё-один-пароль
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_USER=discourse@example.com
SMTP_PASSWORD=пароль-приложения-smtp
Пароли генерируйте отдельно для каждого сервиса, не переиспользуйте один и тот же:
openssl rand -base64 24
DISCOURSE_HOST — это именно то доменное имя, по которому форум будет доступен снаружи, без протокола и без слэша на конце. Если позже вы захотите сменить домен, готовьтесь редактировать записи в таблице site_settings через rails console внутри контейнера — на лету через переменную окружения смена hostname не подхватывается автоматически.
Для SMTP работает связка «логин/пароль + TLS на 587 порту» у большинства провайдеров. Если используете Gmail или Yandex как relay, там потребуется пароль приложения, а не основной пароль аккаунта — обычный пароль почтовые сервисы для SMTP-авторизации давно не принимают.
Первый запуск и инициализация
Поднимаем стек и следим за логами — инициализация базы и первичные миграции Discourse занимают несколько минут:
docker compose --env-file .env up -d
docker compose logs -f discourse
В логах должно появиться сообщение о том, что миграции применены и Puma (веб-сервер Rails) слушает 3000 порт. Если контейнер discourse перезапускается в цикле — почти всегда причина в недоступности PostgreSQL или Redis на момент старта: depends_on в Compose гарантирует только порядок запуска контейнеров, а не готовность самой базы принимать соединения. Если проблема повторяется, добавьте restart: on-failure вместо unless-stopped на время отладки — так вы увидите финальную ошибку в логах, а не бесконечный рестарт.
Первый вход выполняется под учёткой, заданной в DISCOURSE_ADMIN_EMAIL / DISCOURSE_ADMIN_PASSWORD. После входа сразу стоит зайти в /admin/site_settings и пройтись по разделу «Onboarding» — там мастер настройки, который проверяет почту, базовые категории и разрешения регистрации.
Reverse proxy и SSL
Сам Discourse SSL не терминирует — это задача внешнего прокси. Самый предсказуемый вариант — nginx как reverse proxy перед контейнером плюс сертификат от Let's Encrypt:
server {
listen 443 ssl http2;
server_name forum.example.com;
ssl_certificate /etc/letsencrypt/live/forum.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/forum.example.com/privkey.pem;
client_max_body_size 20m;
location / {
proxy_pass http://127.0.0.1:8080;
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;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
server {
listen 80;
server_name forum.example.com;
return 301 https://$host$request_uri;
}
Заголовки X-Forwarded-Proto и апгрейд соединения для WebSocket обязательны: Discourse использует WebSocket-соединение (через MessageBus) для realtime-уведомлений в браузере, без него живые обновления просто не работают, а пользователи будут видеть новые ответы только после ручного обновления страницы. client_max_body_size увеличен, потому что дефолтный лимит nginx в 1 МБ слишком мал для загрузки изображений и аватаров.
Сам сертификат получаем стандартно через certbot — этот шаг подробно расписан в статье про настройку Let's Encrypt SSL на VPS.
Бэкапы и обслуживание
Discourse хранит всё состояние в трёх местах: база PostgreSQL, файлы (аватары, загруженные изображения — если не подключено внешнее S3-хранилище) в volume discourse_data, и настройки в переменных окружения самого compose. Бэкапить нужно все три.
Дамп базы отдельно от volume-снапшота — самый надёжный вариант для восстановления на другом сервере:
docker compose exec postgresql pg_dump -U discourse discourse | gzip > discourse_$(date +%F).sql.gz
Volume с загруженными файлами имеет смысл бэкапить регулярно тем же способом, что и остальные Docker volume на сервере — общий подход и грабли разобраны в статье про бэкап Docker volume.
Обновление до новой версии образа — стандартный для Compose путь:
docker compose pull discourse
docker compose up -d discourse
Перед обновлением на проде стоит сначала снять дамп базы: миграции схемы в новых версиях Discourse иногда необратимы, откатиться проще из бэкапа, чем распутывать частично применённую миграцию руками. Если у вас уже есть общий стандарт для продакшен-стека на Compose (healthcheck, лимиты ресурсов, логирование), пройдитесь по нему — общие практики собраны в статье про Docker Compose для продакшена.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли обойтись без Redis?
Нет, Redis обязателен — на нём построены очередь фоновых задач Sidekiq, кэш сессий и часть realtime-механизма. Отключить его нельзя, только вынести на отдельный сервер при росте нагрузки.
Почему форум не отправляет письма даже при правильном SMTP?
Чаще всего дело в том, что провайдер SMTP блокирует отправку с нового домена или требует SPF/DKIM-записи в DNS. Проверьте логи docker compose logs discourse | grep -i mail — там обычно точная причина отказа от SMTP-сервера.
Хватит ли 2 ГБ RAM для маленького сообщества?
Формально Discourse запустится и на 2 ГБ, но с минимальным запасом: Sidekiq и рендеринг под нагрузкой быстро выедают память, особенно при импорте контента или массовой индексации поиска. Для стабильной работы закладывайте 4 ГБ, если сообщество активнее пары десятков онлайн-пользователей одновременно.
Нужен ли отдельный сервер под PostgreSQL?
Для одного форума — нет, PostgreSQL в соседнем контейнере на том же хосте справляется без проблем. Вынос в отдельный настроенный PostgreSQL на VPS имеет смысл, когда у вас на сервере крутится ещё несколько тяжёлых баз и они начинают конкурировать за IO.
Можно ли хранить загруженные файлы не в volume, а в S3?
Да, и на проде это рекомендуемая практика — так volume контейнера остаётся лёгким, а файлы переживают пересоздание контейнера без отдельного бэкапа. Настраивается через переменные DISCOURSE_S3_*, но это отдельная тема, требующая заранее заведённого S3-совместимого бакета.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →