Развертывание Discourse без Docker

Некоторые детали моих решений:

  1. Конфигурация Nginx:
    За обратным прокси-сервером (haproxy) необходимо скопировать файл /var/discourse/config/nginx-config-sample.conf в /etc/nginx/sites-enabled/discourse.conf или аналогичный целевой путь, затем изменить listen <ваш_порт_прокси> (если требуется) и заменить _ в server_name на ваше доменное имя (subdomain.example.com). Ничего больше менять не нужно.
  2. Страницы не отображаются (сообщение «Oops») и регистрация администратора по электронной почте не работает:
    Причиной некорректного отображения страниц после регистрации администратора при первой загрузке стал пакет Magick. Я следовал файлу, указанному в логе: /var/www/discourse/lib/letter_avatar.rb:112. На строке 112 находились две команды magick, которые действительно не отвечали. Команда convert работала, поэтому я заменил magick на этой строке на convert. После этих исправлений в логе появилась другая ошибка. Попытка выполнить ту же процедуру с командой из файла показала, что команда magick не работает, а convert также завершается с ошибкой. Примечание: версия magick --version была 7.x, а convert --version показывала 6.x. Причина заключалась в том, что я сначала установил imagemagick через apt, а затем скомпилировал Magick 7 из исходного кода. Возникли конфликты, и Magick сообщал, что команда convert устарела. Поэтому я перезапустил свой скрипт, используя только Magick 7. Это немедленно решило проблему: я увидел новые страницы, которые ждал дни, и электронная почта также заработала! Magick действительно волшебство.
  3. Осталась проблема смешанного контента, но сайт работает нормально.
    Примечание: в файле puma.rb строка bind ENV.fetch("PUMA_BIND", "tcp://#{ENV['PUMA_BIND_ALL'] ? '' : '127.0.0.1:'}3000") должна выглядеть именно так (исправление из первого сообщения).
  4. РЕДАКТИРОВАНИЕ пункта «3.» о принудительном использовании HTTPS для Discourse: Я точно не знаю почему, но мои браузеры больше не показывают предупреждения о «смешанном контенте» (возможно, из-за обновления кэша), поэтому сейчас всё в порядке. Мне осталось только завершить свой скрипт.
3 лайка

Подтверждено, работает!!!
ImageMagick v7 решает проблему «Ooops»!
Насколько я проверил, всё работает полностью.
Я протестирую остальные функции и сообщу как можно скорее.

1 лайк

Для тестирования я установил переменную окружения PUMA_BIND при запуске puma.

Что касается ImageMagick, по какой-то причине Discourse не может выполнить конвертацию изображения в веб-интерфейсе: при загрузке большого изображения он вежливо отказывается его конвертировать… Я всё ещё отлаживаю проблему.

Есть какие-то сдвиги в отладке? С моей стороны я работаю над magick. На некоторых изображениях размером около 50 КБ в браузере появляется всплывающее окно:
timeout -k 40.0 20 magick gif:/tmp/RackMultipart20250927-23598-xrrp6e.gif -auto-orient -background white -interlace none -flatten -debug all -quality 90 jpg:/tmp/image20250927-23598-9ujq3d.jpg, но изображение не загружается.
Если размер больше, всплывающее окно не появляется, но индикатор загрузки бесконечно крутится без результата. В /var/www/discourse/log ошибки не записываются.

У меня та же проблема :joy::sob:

Единственный способ, которым мне удалось запустить это — использовать imagemagick из репозитория brew.

НО это падает, когда изображение больше 3 МБ… возможно, я настроил слишком строгую конфигурацию политик.

Попробуйте!!!

Я тестирую установку через Docker, но мне кажется, что развертывание nginx, unicorn, redis, postgresql и всего остального в ОДНОМ контейнере — это довольно глупо. Это вообще не имеет смысла. И нет документации по инфраструктуре для крупных развертываний… Я работаю в IT уже более 20 лет и вижу только проблемы, которые скоро возникнут при использовании такой инфраструктуры.

Не говоря уже о «Docker, поедателе пространства» (как Дормамму) :rofl::rofl:

Эта проблема исчезла с использованием следующего решения, описанного в конце:

Отладка 1: bundle db:create выводит, также в логах при первой загрузке:


неизвестный OID 21096: не удалось распознать тип ‘embeddings’. Он будет обработан как String.
рабочий pngquant: pngquant не найден; пожалуйста, предоставьте правильный бинарный файл или отключите этого рабочего (аргумент --no-pngquant или :pngquant => false через параметры)
рабочий oxipng: oxipng не найден; пожалуйста, предоставьте правильный бинарный файл или отключите этого рабочего (аргумент --no-oxipng или :oxipng => false через параметры)
рабочий jhead: jhead не найден, jpegtran не найден; пожалуйста, предоставьте правильный бинарный файл или отключите этого рабочего (аргумент --no-jhead или :jhead => false через параметры)
рабочий jpegoptim: jpegoptim не найден; пожалуйста, предоставьте правильный бинарный файл или отключите этого рабочего (аргумент --no-jpegoptim или :jpegoptim => false через параметры)

Отладка 2: Некоторые команды magick с файлами выводят:

нет делегата декодирования для этого формата изображения

Эта последняя проблема упоминается здесь.

Решение находится здесь (устанавливает magick с плагинами форматов):

t=$(mktemp) && \
wget 'https://dist.1-2.dev/imei.sh' -qO "$t" && \
bash "$t" && \
rm "$t"

После этого мой размер загрузки может достигать 518 КБ. Выше — нет. Это касается только изображений. Все остальные документы, аудио, видео загружаются без проблем.

Временное решение для оставшейся проблемы:
Я проверил настройки администратора, discourse.conf, nginx/sites-enabled/discourse.conf, папку git. Наконец, в AdminPanel/Parameters/Files я отключил “Composer media optimization image enabled”, и всё заработало корректно. Теперь я могу загружать любые изображения.

Да, IMEI — это почти то же решение, что и Brew, но я уверен, что оно не займёт около 2 ГБ на диске :rofl::rofl::sob::sob::sob::face_with_symbols_on_mouth::face_with_symbols_on_mouth::face_with_symbols_on_mouth:

Я проверю ваше решение по поводу максимального размера файла при загрузке.

Также я ищу способ отправки писем «бесплатно» (я на ранней стадии и пока не хочу подключать Mailtrap, Mailgun и т.п.)

Почему бы вам не установить собственный почтовый сервер? У меня есть свой, и я написал скрипт для его установки. Дайте знать, если вам это интересно. Вам нужно будет лишь ежегодно оплачивать доменное имя.

Конечно! Напиши мне в личные сообщения инструкции или ссылку на репозиторий Git! :grinning_face_with_smiling_eyes:

Я перестал использовать собственные почтовые серверы много лет назад…

При выполнении команды rake assets:precompile появилась ошибка No such file or directory - brotli. Просто установите её через менеджер пакетов.

2 лайка

Я обнаружил, что возникает следующая ошибка:

bundler: failed to load command: puma (/home/mry/.rbenv/versions/3.4.6/bin/puma)
/home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/puma-7.0.4/lib/puma/cluster.rb:472:in 'Puma::Cluster#run': undefined method 'wait_readable' for nil (NoMethodError)

            if read.wait_readable([0, @next_check - Time.now].max)
                   ^^^^^^^^^^^^^^
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/puma-7.0.4/lib/puma/launcher.rb:202:in 'Puma::Launcher#run'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/puma-7.0.4/lib/puma/cli.rb:73:in 'Puma::CLI#run'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/puma-7.0.4/bin/puma:10:in '<top (required)>'
        from /home/mry/.rbenv/versions/3.4.6/bin/puma:25:in 'Kernel#load'
        from /home/mry/.rbenv/versions/3.4.6/bin/puma:25:in '<top (required)>'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/cli/exec.rb:59:in 'Kernel.load'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/cli/exec.rb:59:in 'Bundler::CLI::Exec#kernel_load'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/cli/exec.rb:23:in 'Bundler::CLI::Exec#run'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/cli.rb:452:in 'Bundler::CLI#exec'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/vendor/thor/lib/thor/command.rb:28:in 'Bundler::Thor::Command#run'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/vendor/thor/lib/thor/invocation.rb:127:in 'Bundler::Thor::Invocation#invoke_command'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/vendor/thor/lib/thor.rb:538:in 'Bundler::Thor.dispatch'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/cli.rb:35:in 'Bundler::CLI.dispatch'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/vendor/thor/lib/thor/base.rb:584:in 'Bundler::Thor::Base::ClassMethods#start'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/cli.rb:29:in 'Bundler::CLI.start'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/exe/bundle:28:in 'block in <top (required)>'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/lib/bundler/friendly_errors.rb:117:in 'Bundler.with_friendly_errors'
        from /home/mry/.rbenv/versions/3.4.6/lib/ruby/gems/3.4.0/gems/bundler-2.6.4/exe/bundle:20:in '<top (required)>'
        from /home/mry/.rbenv/versions/3.4.6/bin/bundle:25:in 'Kernel#load'
        from /home/mry/.rbenv/versions/3.4.6/bin/bundle:25:in '<main>'

Затем Puma перезапускается сама. Что мне нужно сделать, чтобы предотвратить это?

Редактировано: Похоже, это проблема, связанная с Puma, возникшая с Ruby 3.4.0. Решается отключением YJIT.

Тогда используйте версию 3.3.7. Это версия, которую я использую.

Я слишком ленив, чтобы перескачивать гемы, поэтому просто отключаю YJIT, продолжая использовать 3.4.6.

Всем привет,
Вот мой скрипт для полной установки в три этапа, без Docker, в контейнере Debian 12 (для меня это LXC).
Используйте блоки if, чтобы запускать только нужные части.

  1. Установите свои параметры и запустите скрипт от root с необходимыми аргументами. Будьте осторожны: скрипт завершится в точке выхода перед частью “### MANUAL”.
  2. Эти команды вам нужно будет выполнить вручную в терминале контейнера. Мне не удалось заставить это работать в моем непривилегированном LXC-контейнере. (пользователь по умолчанию — root)
  3. Затем запустите остальное.
    Я многократно тестировал этот скрипт, и он работает, но после адаптации для публикации могут остаться некоторые ошибочные части.
install_script
#!/bin/bash
########## СКРИПТ УСТАНОВКИ DISCOURSE НА DEBIAN 12 БЕЗ DOCKER ##################
#- последний тест в декабре 2025 года
############################## ЗАПУСКАТЬ ОТ ROOT, лучше всего в LXC-контейнере!!!!!!!
#- источник обсуждения : https://meta.discourse.org/t/deploy-discourse-without-docker/351194
##############################################################################
# РУЧНЫЕ ПАРАМЕТРЫ
certbot_contact_email=hello@domain.tld
certbot_renew_port=9785
nginx_discourse_port=8080
DISCOURSE_DEVELOPER_EMAILS='emailaddress'
DISCOURSE_SMTP_ADDRESS=smtp_server_address
DISCOURSE_SMTP_PORT=465
DISCOURSE_SMTP_USER_NAME=xxx
DISCOURSE_SMTP_PASSWORD=xxx
DISCOURSE_SMTP_DOMAIN=xxx
DISCOURSE_NOTIFICATION_EMAIL=no-reply-xxxx
DISCOURSE_MAXMIND_ACCOUNT_ID=50
# КОНЕЦ РУЧНЫХ ПАРАМЕТРОВ

shopt -s expand_aliases
source /etc/bash.bashrc
container=$1
cont_haproxy=$2
pool=$3
lxc_image=$4
hostname=$5
db_password=$6
maxmind_license_key=$7
DISCOURSE_HOSTNAME=$hostname
DISCOURSE_MAXMIND_LICENSE_KEY=$maxmind_license_key

### УСТАНОВКА MAGICK 7 и его плагинов
# https://github.com/SoftCreatR/imei/?tab=readme-ov-file
if [ 1 == 1 ]; then
t=$(mktemp) && \
wget 'https://dist.1-2.dev/imei.sh' -qO "$t" && \
bash "$t" && \
rm "$t"
fi

### БАЗОВАЯ УСТАНОВКА ###
if [ 1 == 1 ]; then
apt install -y apt-utils postgresql nginx libnginx-mod-http-brotli-filter libnginx-mod-http-brotli-static redis
systemctl enable --now postgresql redis-server nginx
useradd -m -s /bin/bash discourse
usermod -aG sudo discourse
passwd -d discourse
# RVM (RUBY)
apt install -y ruby-full # необходимо
su - discourse -c 'curl -sSL https://get.rvm.io | bash'
su - discourse -c 'source /home/discourse/.bashrc'
su - discourse -c 'source /home/discourse/.rvm/scripts/rvm' # необходимо после установки rvm
su - discourse -c 'rvm install 3.3.7' # на сегодня установит node 3.3.7
source /home/discourse/.rvm/scripts/rvm
# fi
# настройка POSTGRESQL
apt update && apt upgrade -y
apt autoremove -y

# ВАМ НУЖНО ПРИНЯТЬ ОБНОВЛЕНИЕ
/usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y
apt install -y postgresql-18-pgvector
fi
alias mydb="sudo -u postgres psql discourse -c "
if [ 1 == 1 ]; then
su - discourse -c 'sudo -u postgres createuser -s discourse'
# fi
su - discourse -c 'sudo -u postgres createdb discourse'
su - discourse -c 'sudo -u postgres psql -c "ALTER USER discourse WITH ENCRYPTED PASSWORD '\''password'\'';"'
alias mydb="sudo -Hiu discourse psql -c "
su - discourse -c 'psql -c "GRANT CONNECT ON DATABASE discourse TO root;"'
su - discourse -c 'psql -c "CREATE EXTENSION pg_trgm;CREATE EXTENSION hstore;"'
su - discourse -c 'psql -c "ALTER DATABASE discourse OWNER TO discourse;"'
su - discourse -c 'psql -c "CREATE EXTENSION unaccent;CREATE EXTENSION plpgsql;"'
su - discourse -c 'psql -c "CREATE EXTENSION vector;"'
echo "OK"

### УСТАНОВКА DISCOURSE ИЗ GITHUB
cd /var/www/
git clone https://github.com/discourse/discourse
git config --global --add safe.directory /var/www/discourse
# exit
### НЕОБХОДИМО ДЛЯ bundle, ruby и т.д., но не для самого discourse
tee -a /home/discourse/.bashrc > /dev/null <<EOT
export RAILS_ENV=production
export UNICORN_SIDEKIQ_MAX_RSS=800
export UNICORN_WORKERS=8
export UNICORN_SIDEKIQS=1
export PUMA_SIDEKIQ_MAX_RSS=800
export PUMA_WORKERS=8
export PUMA_SIDEKIQS=1
export RUBY_GC_HEAP_GROWTH_MAX_SLOTS=40000
export RUBY_GC_HEAP_INIT_SLOTS=400000
export RUBY_GC_HEAP_OLDOBJECT_LIMIT_FACTOR=1.5

export DISCOURSE_HOSTNAME=$hostname
export DISCOURSE_DEVELOPER_EMAILS=$DISCOURSE_DEVELOPER_EMAILS
export DISCOURSE_SMTP_ADDRESS=$DISCOURSE_SMTP_ADDRESS
export DISCOURSE_SMTP_PORT=$DISCOURSE_SMTP_PORT
export DISCOURSE_SMTP_USER_NAME=$DISCOURSE_SMTP_USER_NAME
export DISCOURSE_SMTP_PASSWORD=$DISCOURSE_SMTP_PASSWORD
export DISCOURSE_SMTP_DOMAIN=$DISCOURSE_SMTP_DOMAIN
export DISCOURSE_NOTIFICATION_EMAIL=$DISCOURSE_NOTIFICATION_EMAIL
export DISCOURSE_MAXMIND_ACCOUNT_ID=$DISCOURSE_MAXMIND_ACCOUNT_ID
export DISCOURSE_MAXMIND_LICENSE_KEY=$maxmind_license_key

export DISCOURSE_ENABLE_CORS=true
export DISCOURSE_MAX_REQS_PER_IP_MODE=none
export DISCOURSE_MAX_REQS_PER_IP_PER_MINUTE=20000
export DISCOURSE_MAX_REQS_PER_IP_PER_10_SECONDS=5000
export DISCOURSE_MAX_ASSET_REQS_PER_IP_PER_10_SECONDS=20000
export DISCOURSE_MAX_REQS_RATE_LIMIT_ON_PRIVATE=false
export DISCOURSE_MAX_USER_API_REQS_PER_MINUTE=200
export DISCOURSE_MAX_USER_API_REQS_PER_DAY=28800
export DISCOURSE_MAX_ADMIN_API_REQS_PER_MINUTE=600
export DISCOURSE_MAX_DATA_EXPLORER_API_REQ_MODE=none
EOT
cd /var/www/discourse
git stash
git pull
git checkout tests-passed 
cd plugins
for plugin in *
do
echo "ok"
    echo $plugin; cd ${plugin}; git pull; cd ..
done
# fi
chown -R discourse:discourse /var/www/discourse/     
mkdir -p /var/www/discourse/public
chown -R discourse:www-data /var/www/discourse/public
tee -a /var/www/discourse/config/discourse.conf > /dev/null <<EOT
max_data_explorer_api_req_mode = 'none'
max_user_api_reqs_per_day = '28800'
hostname = '$hostname'
redis_host = 'localhost'
smtp_user_name = $$DISCOURSE_SMTP_USER_NAME
db_password = '$db_password'
smtp_address = '$DISCOURSE_SMTP_ADDRESS'
db_socket = ''
max_reqs_per_ip_per_10_seconds = '5000'
max_asset_reqs_per_ip_per_10_seconds = '20000'
max_reqs_rate_limit_on_private = 'false'
developer_emails = '$DISCOURSE_DEVELOPER_EMAILS'
max_user_api_reqs_per_minute = '200'
maxmind_license_key = '$maxmind_license_key'
smtp_port = '$DISCOURSE_SMTP_PORT'
maxmind_account_id = '$DISCOURSE_MAXMIND_ACCOUNT_ID'
smtp_password = '$DISCOURSE_SMTP_PASSWORD'
max_reqs_per_ip_per_minute = '20000'
notification_email = '$DISCOURSE_NOTIFICATION_EMAIL'
db_host = 'localhost'
enable_cors = 'true'
db_port = ''
max_reqs_per_ip_mode = 'none'
smtp_domain = '$DISCOURSE_SMTP_DOMAIN'
#max_admin_api_reqs_per_minute = '600'
smtp_force_tls='true'
EOT

cd /var/www/discourse && sed -i '/gem "rails_multisite"/i gem "rails"' Gemfile
cd /var/www/discourse && sed -i '/gem "image_optim"/i gem "image_optim_pack"' Gemfile
su - discourse -c 'cd /var/www/discourse && bundle install'
# fi

### PNPM (NODEJS)
su - discourse -c 'curl -fsSL https://get.pnpm.io/install.sh' | su - discourse -c 'sh -'
su - discourse -c 'source /home/discourse/.bashrc'
source /home/discourse/.rvm/scripts/rvm
su - discourse -c 'sed -i "s/~> 3.3/3.3.7/" /var/www/discourse/Gemfile'
chown -R discourse:discourse /var/www/discourse/Gemfile Gemfile.lock
apt-get -yqq install brotli # нужно ли это????????????????
# fi
su - discourse -c 'cd /var/www/discourse && bin/rails db:create'
# fi
su - discourse -c 'source /home/discourse/.rvm/scripts/rvm'
tee -a .bashrc > /dev/null <<EOT
# pnpm
export PNPM_HOME="/home/discourse/.local/share/pnpm"
case ":\$PATH:" in
  *":\$PNPM_HOME:"*) ;;
  *) export PATH="\$PNPM_HOME:\$PATH" ;;
esac
# pnpm end
EOT
which pnpm
source .bashrc
pnpm env use --global lts # на сегодня установит node 22
cd /var/www/discourse && pnpm i
chown -R discourse:discourse .
chown -R discourse:www-data public
exit

#################################
######### РУЧНАЯ НАСТРОЙКА ################
export PATH="$PATH:/home/discourse/.rvm/bin"
rvm install "ruby-3.3.7" # установка в root
cd /var/www/discourse
bundle install
bundle exec rake db:migrate
bundle exec rake themes:update
bundle exec rake assets:precompile
######### РУЧНАЯ НАСТРОЙКА #################
##################################

fi
### puma.rb
#cd /var/www/discourse
cp /var/www/discourse/config/puma.rb /var/www/discourse/config/puma.rb_original

tee /var/www/discourse/config/puma.rb > /dev/null <<EOT
# frozen_string_literal: true

require "fileutils"

discourse_path = File.expand_path(File.expand_path(File.dirname(__FILE__)) + "/../")

enable_logstash_logger = ENV["ENABLE_LOGSTASH_LOGGER"] == "1"
puma_stderr_path = "#{discourse_path}/log/puma.stderr.log"
puma_stdout_path = "#{discourse_path}/log/puma.stdout.log"

# Загрузить логгер logstash, если включен
if enable_logstash_logger
  require_relative "../lib/discourse_logstash_logger"
  FileUtils.touch(puma_stderr_path) if !File.exist?(puma_stderr_path)
  # Примечание: возможно, потребуется адаптировать инициализацию логгера для Puma
  log_formatter = proc do |severity, time, progname, msg|
    event = {
      "@timestamp" => Time.now.utc,
      "message" => msg,
      "severity" => severity,
      "type" => "puma"
    }
    "#{event.to_json}\n"
  end
else
  stdout_redirect puma_stdout_path, puma_stderr_path, true
end

# Количество воркеров (процессов)
workers ENV.fetch("PUMA_WORKERS", 8).to_i

# Установить директорию
directory discourse_path

# Привязка к указанному адресу и порту
bind ENV.fetch("PUMA_BIND", "tcp://#{ENV['PUMA_BIND_ALL'] ? '' : '127.0.0.1:'}3000")

# Расположение PID-файла
FileUtils.mkdir_p("#{discourse_path}/tmp/pids")
pidfile ENV.fetch("PUMA_PID_PATH", "#{discourse_path}/tmp/pids/puma.pid")

# Файл состояния — используется pumactl
state_path "#{discourse_path}/tmp/pids/puma.state"

# Конфигурация, зависящая от окружения
if ENV["RAILS_ENV"] == "production"
  # Тайм-аут для продакшена
  worker_timeout 30
else
  # Тайм-аут для разработки
  worker_timeout ENV.fetch("PUMA_TIMEOUT", 60).to_i
end

# Предзагрузка приложения
preload_app!

# Обработка запуска и остановки воркеров
before_fork do
  Discourse.preload_rails!
  Discourse.before_fork

  # Проверка супервизора
  supervisor_pid = ENV["PUMA_SUPERVISOR_PID"].to_i
  if supervisor_pid > 0
    Thread.new do
      loop do
        unless File.exist?("/proc/#{supervisor_pid}")
          puts "Kill self supervisor is gone"
          Process.kill "TERM", Process.pid
        end
        sleep 2
      end
    end
  end

  # Воркеры Sidekiq
  sidekiqs = ENV["PUMA_SIDEKIQS"].to_i
  if sidekiqs > 0
    puts "starting #{sidekiqs} supervised sidekiqs"

    require "demon/sidekiq"
    Demon::Sidekiq.after_fork { DiscourseEvent.trigger(:sidekiq_fork_started) }
    Demon::Sidekiq.start(sidekiqs)

    if Discourse.enable_sidekiq_logging?
      Signal.trap("USR1") do
        # Задержка повторного открытия логов Sidekiq
        sleep 1
        Demon::Sidekiq.kill("USR2")
      end
    end
  end

  # Демон синхронизации электронной почты
  if ENV["DISCOURSE_ENABLE_EMAIL_SYNC_DEMON"] == "true"
    puts "starting up EmailSync demon"
    Demon::EmailSync.start(1)
  end

  # Демоны плагинов
  DiscoursePluginRegistry.demon_processes.each do |demon_class|
    puts "starting #{demon_class.prefix} demon"
    demon_class.start(1)
  end

  # Поток мониторинга демонов
  Thread.new do
    loop do
      begin
        sleep 60

        if sidekiqs > 0
          Demon::Sidekiq.ensure_running
          Demon::Sidekiq.heartbeat_check
          Demon::Sidekiq.rss_memory_check
        end

        if ENV["DISCOURSE_ENABLE_EMAIL_SYNC_DEMON"] == "true"
          Demon::EmailSync.ensure_running
          Demon::EmailSync.check_email_sync_heartbeat
        end

        DiscoursePluginRegistry.demon_processes.each(&:ensure_running)
      rescue => e
        Rails.logger.warn("Error in demon processes heartbeat check: #{e}\n#{e.backtrace.join("\n")}")
      end
    end
  end

  # Закрыть соединение с Redis
  Discourse.redis.close
end

on_worker_boot do
  DiscourseEvent.trigger(:web_fork_started)
  Discourse.after_fork
end

# Обработка тайм-аута воркера
worker_timeout 30

# Низкоуровневые опции воркера
threads 8, 32
EOT

### CERTBOT
apt install -y certbot
certbot certonly --non-interactive --agree-tos --renew-with-new-domains  --standalone --email $certbot_contact_email --rsa-key-size 4096 --http-01-port $certbot_renew_port -d $hostname
# fi

### NGINX
cp /var/www/discourse/config/nginx.sample.conf /etc/nginx/sites-enabled/discourse.conf
sed -i "s/80/$nginx_discourse_port/" /etc/nginx/sites-enabled/discourse.conf
sed -i "s/server_name _/server_name $hostname/" /etc/nginx/sites-enabled/discourse.conf
mkdir -p /var/nginx/cache
systemctl enable nginx
systemctl restart nginx
systemctl status nginx

### АВТОЗАПУСК СЕРВИСА SYSTEMD
sudo tee /etc/systemd/system/discourse.service > /dev/null <<EOT
[Unit]
Description=Discourse с сервером Puma
After=network.target postgresql.service
Requires=postgresql.service

[Service]
Type=simple
User=discourse
Group=discourse
WorkingDirectory=/var/www/discourse
# требуется запуск `rvm 3.3.7 --default` перед запуском этого сервиса
ExecStart=/usr/bin/bash -lc '/home/discourse/.rvm/gems/ruby-3.3.7/bin/puma -C config/puma.rb'
ExecReload=/usr/bin/bash -lc '/home/discourse/.rvm/gems/ruby-3.3.7/bin/pumactl restart'
# необходима эта строка ниже
Environment=RAILS_ENV=production
# Конфигурация перезапуска
Restart=always
RestartSec=5s

# Базовые меры безопасности
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=read-only

[Install]
WantedBy=multi-user.target
EOT

systemctl enable discourse
systemctl start discourse

# ИЛИ РУЧНОЙ ЗАПУСК
# puma -C config/puma.rb

### НАСТРОЙКИ
echo "ПОСЛЕДНИЕ РУЧНЫЕ НАСТРОЙКИ:"
echo ""
echo "Перейдите в Админ > Файлы и добавьте нужные расширения файлов - видео, аудио и документы
echo "ДЛЯ ПРИНУДИТЕЛЬНОГО HTTPS: https://domain.com/admin/config/security"