Bien que le déploiement de Discourse selon le guide d’installation officiel soit plus pratique et plus sûr, je souhaite explorer plus en profondeur le conteneur et voir comment il peut être déployé sur Linux sans Docker. Je souhaite partager les étapes à titre d’information. Vous les adaptez et les utilisez à vos propres risques.
Plonger dans le fonctionnement de Discourse dans un conteneur
J’examine la sortie de ./launcher start-cmd webonly :
true run --shm-size=512m --link data:data -d --restart=always -e LANG=en_US.UTF-8 -e RAILS_ENV=production … --name webonly -t -v /var/discourse/shared/webonly:/shared … local_discourse/webonly /sbin/boot
Ensuite, j’examine /sbin/boot et /etc/service/unicorn/run, et j’obtiens la commande principale pour démarrer Discourse :
LD_PRELOAD=$RUBY_ALLOCATOR HOME=/home/discourse USER=discourse exec thpoff chpst -u discourse:www-data -U discourse:www-data bundle exec config/unicorn_launcher -E production -c config/unicorn.conf.rb
Préparer le système
Pour information, j’utilise Ubuntu 24.04 et zsh.
Suivez le guide d’installation officiel de PG pour installer PostgreSQL depuis le dépôt Apt de PostgreSQL. J’ai installé la version 18, qui fonctionne très bien, bien que l’installation officielle utilise la version 15 au moment de la rédaction.
Installez redis (8.2 au moment de la rédaction, l’installation officielle utilise 7.0), nginx et créez l’utilisateur dédié discourse :
apt install nginx libnginx-mod-http-brotli-static redis zsh zsh-autosuggestions zsh-syntax-highlighting
systemctl enable --now postgresql redis nginx
useradd -m -s /bin/zsh discourse
Installez ImageMagick 7 (j’utilise IMEI) et vérifiez la version. La mienne est :
magick --version
Version: ImageMagick 7.1.2-3 Q16-HDRI
Ensuite, changez d’utilisateur (su - discourse) et installez pnpm et rvm.
curl -fsSL https://get.pnpm.io/install.sh | zsh -
curl -sSL https://get.rvm.io | bash
Ensuite, adaptez et ajoutez la configuration suivante à votre .zshrc :
/home/discourse/.zshrc
# pnpm
export PNPM_HOME="/home/discourse/.local/share/pnpm"
case ":$PATH:" in
*":$PNPM_HOME:"*) ;;
*) export PATH="$PNPM_HOME:$PATH" ;;
esac
# pnpm fin
alias npm='pnpm'
alias npx='pnpx'
# Ajouter RVM au PATH pour le scripting. Assurez-vous que ceci est le dernier changement de variable PATH.
export PATH="$PATH:$HOME/.rvm/bin"
export ALLOW_EMBER_CLI_PROXY_BYPASS=1
export RAILS_ENV=production
export UNICORN_SIDEKIQ_MAX_RSS=1000
export UNICORN_WORKERS=4
export UNICORN_SIDEKIQS=1
export PUMA_SIDEKIQ_MAX_RSS=1000
export PUMA_WORKERS=4
export PUMA_SIDEKIQS=1
#export RUBY_YJIT_ENABLE=1
#export RUBY_CONFIGURE_OPTS="--enable-yjit"
export DISCOURSE_HOSTNAME=example.com
export DISCOURSE_DEVELOPER_EMAILS=discourse-admin@example.com
export DISCOURSE_MAXMIND_ACCOUNT_ID=<id>
export DISCOURSE_MAXMIND_LICENSE_KEY=<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
export DISCOURSE_MAX_REQS_PER_IP_EXCEPTIONS="127.0.0.1 ::1"
cd /var/www/discourse
Déconnectez-vous et reconnectez-vous en tant que discourse pour que .zshrc prenne effet.
Installez Node et Ruby :
pnpm env use --global latest # installera Node 24.9 au moment de la rédaction. L'installation officielle utilise 22
rvm get master
rvm install 3.4 # installera Ruby 3.4.6 au moment de la rédaction. L'installation officielle utilise 3.3
rvm use 3.4 --default
Préparer la base de données (et restaurer une sauvegarde)
sudo -u postgres createuser -s discourse
sudo -u postgres createdb discourse
$sudo -u postgres psql discourse
psql>
ALTER USER discourse WITH PASSWORD 'xxx';
CREATE EXTENSION hstore;CREATE EXTENSION pg_trgm;
CREATE EXTENSION plpgsql;
CREATE EXTENSION unaccent;
CREATE EXTENSION vector;
# Pour restaurer une base de données extraite d'une sauvegarde :
$ gunzip < dump.sql.gz | psql discourse
Pour restaurer une sauvegarde, vous devez également copier les dossiers public et plugins.
Installer Discourse
Je me suis référé à discourse_docker/templates/web.template.yml at 20e33fbfd98d3b8d9c57f7a111beff8aa51a5b98 · discourse/discourse_docker · GitHub
En tant qu’utilisateur root :
cd /var/www/
git clone https://github.com/discourse/discourse
mkdir -p /var/www/discourse/public
chown -R discourse:discourse /var/www/discourse/
chown -R discourse:www-data /var/www/discourse/public
Configurez config/discourse.conf :
config/discourse.conf
max_data_explorer_api_req_mode = 'none'
max_user_api_reqs_per_day = '28800'
hostname = '127.0.0.1'
hostname = 'example.com'
redis_host = '127.0.0.1'
db_password = '<password>'
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-admin@example.com'
max_user_api_reqs_per_minute = '200'
maxmind_license_key = '<key>'
maxmind_account_id = '<id>'
max_reqs_per_ip_per_minute = '20000'
db_host = '127.0.0.1'
enable_cors = 'true'
db_port = ''
max_reqs_per_ip_mode = 'none'
max_admin_api_reqs_per_minute = '600'
smtp_user_name = '<name>'
smtp_address = 'postal.example.com'
smtp_port = '25'
smtp_password = '<password>'
smtp_domain = 'postalsend.example.com'
notification_email = 'noreply@postalsend.example.com'
Effectuez les opérations d’installation de bundle/pnpm, de migration de base de données et de précompilation des assets. C’est aussi ainsi que vous mettez à jour Discourse et les plugins.
En tant qu’utilisateur discourse :
cd /var/www/discourse
git stash
git pull
git checkout tests-passed
cd plugins
for plugin in *
do
echo $plugin; cd ${plugin}; git pull; cd ..
done
cd ../
sed -i '/gem "rails_multisite"/i gem "rails"' Gemfile
bundle install --jobs $(($(nproc) - 1))
pnpm i
bundle exec rake db:migrate
bundle exec rake themes:update
bundle exec rake assets:precompile
Je ne souhaite pas utiliser unicorn. Heroku recommande d’utiliser le serveur web Puma plutôt qu’Unicorn. Voici mon fichier config/puma.rb écrit après consultation de config/unicorn.conf.rb :
config/puma.rb
# frozen_string_literal: true
require "fileutils"
#require 'puma/acme'
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"
# Charger le logger logstash si activé
if enable_logstash_logger
require_relative "../lib/discourse_logstash_logger"
FileUtils.touch(puma_stderr_path) if !File.exist?(puma_stderr_path)
# Remarque : Vous devrez peut-être adapter l'initialisation du logger pour 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
# Nombre de workers (processus)
workers ENV.fetch("PUMA_WORKERS", 6).to_i
# Définir le répertoire
directory discourse_path
# Se lier à l'adresse et au port spécifiés
bind ENV.fetch(
"PUMA_BIND",
"tcp://#{ENV["PUMA_BIND_ALL"] ? "" : "127.0.0.1:"}#{ENV.fetch("PUMA_PORT", 3000)}",
)
#bind 'tcp://0.0.0.0:80'
#plugin :acme
#acme_server_name 'example.com'
#acme_tos_agreed true
#bind 'acme://0.0.0.0:443'
# Emplacement du fichier PID
FileUtils.mkdir_p("#{discourse_path}/tmp/pids")
pidfile ENV.fetch("PUMA_PID_PATH", "#{discourse_path}/tmp/pids/puma.pid")
# Fichier d'état - utilisé par pumactl
state_path "#{discourse_path}/tmp/pids/puma.state"
# Configuration spécifique à l'environnement
if ENV["RAILS_ENV"] == "production"
# Timeout en production
worker_timeout 30
else
# Timeout en développement
worker_timeout ENV.fetch("PUMA_TIMEOUT", 60).to_i
end
# Précharger l'application
preload_app!
# Gérer le démarrage et l'arrêt des workers
before_fork do
Discourse.preload_rails!
Discourse.before_fork
# Vérification du superviseur
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
# Workers 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
# Délai avant la réouverture du journal Sidekiq
sleep 1
Demon::Sidekiq.kill("USR2")
end
end
end
# Démon de synchronisation des e-mails
if ENV["DISCOURSE_ENABLE_EMAIL_SYNC_DEMON"] == "true"
puts "starting up EmailSync demon"
Demon::EmailSync.start(1)
end
# Démon des plugins
DiscoursePluginRegistry.demon_processes.each do |demon_class|
puts "starting #{demon_class.prefix} demon"
demon_class.start(1)
end
# Thread de surveillance du démon
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
# Fermer la connexion Redis
Discourse.redis.close
end
on_worker_boot do
DiscourseEvent.trigger(:web_fork_started)
Discourse.after_fork
end
# Gestion du timeout des workers
worker_timeout 30
# Options de bas niveau pour les workers
threads 8, 32
Pour exécuter Discourse, lancez puma -C config/puma.rb.
En utilisant systemd, vous pouvez le démarrer au démarrage et le redémarrer en cas d’échec. Voici le fichier de service :
/etc/systemd/system/discourse.service
[Unit]
Description=Discourse avec le serveur Puma
After=network.target postgresql.service
Requires=postgresql.service
[Service]
Type=simple
User=discourse
Group=discourse
WorkingDirectory=/var/www/discourse
# Nécessite d'exécuter `rvm 3.4.6 --default` avant le lancement de ce service
ExecStart=/usr/bin/zsh -lc 'source /home/discourse/.zshrc && /home/discourse/.rvm/gems/ruby-3.4.6/bin/puma -C config/puma.rb'
ExecReload=/usr/bin/zsh -lc 'source /home/discourse/.zshrc && /home/discourse/.rvm/gems/ruby-3.4.6/bin/pumactl restart'
# Configuration du redémarrage
Restart=always
RestartSec=5s
# Mesures de sécurité de base
NoNewPrivileges=true
ProtectSystem=full
ProtectHome=read-only
[Install]
WantedBy=multi-user.target
Maintenant, le serveur Puma écoute sur 127.0.0.1:3000. Adaptez le fichier de configuration nginx depuis Docker :
/etc/nginx/sites-enabled/discourse.conf
# Types MIME supplémentaires que vous souhaitez que nginx gère, ajoutez-les ici
types {
text/csv csv;
#application/wasm wasm;
}
upstream discourse { server 127.0.0.1:3000; }
# inactive signifie que nous conservons les éléments pendant 1440 minutes indépendamment du dernier accès (1 semaine)
# levels signifie qu'il s'agit d'une hiérarchie de 2 niveaux car nous pouvons avoir beaucoup de fichiers
# max_size limite la taille du cache
proxy_cache_path /var/nginx/cache inactive=1440m levels=1:2 keys_zone=one:10m max_size=600m;
# Augmenté par rapport à la valeur par défaut pour accommoder les gros cookies lors des flux oAuth2
# comme dans https://meta.discourse.org/t/x/74060 et les gros en-têtes CSP et Link (preload)
proxy_buffer_size 32k;
proxy_buffers 4 32k;
# Augmenté par rapport à la valeur par défaut pour permettre un grand volume de cookies dans les en-têtes de requête
# Discourse lui-même tente de minimiser la taille des cookies, mais nous ne pouvons pas contrôler les autres cookies définis par d'autres outils sur le même domaine.
large_client_header_buffers 4 32k;
# Tenter de préserver le protocole, doit être dans le contexte http
map $http_x_forwarded_proto $thescheme {
default $scheme;
"~https$" https;
}
log_format log_discourse '[$time_local] "$http_host" $remote_addr "$request" "$http_user_agent" "$sent_http_x_discourse_route" $status $bytes_sent "$http_referer" $upstream_response_time $request_time "$upstream_http_x_discourse_username" "$upstream_http_x_discourse_trackview" "$upstream_http_x_queue_time" "$upstream_http_x_redis_calls" "$upstream_http_x_redis_time" "$upstream_http_x_sql_calls" "$upstream_http_x_sql_time"';
# Autoriser la bypass du cache depuis localhost
#geo $bypass_cache {
# default 0;
# 127.0.0.1 1;
# ::1 1;
#}
limit_req_zone $binary_remote_addr zone=flood:10m rate=12r/s;
limit_req_zone $binary_remote_addr zone=bot:10m rate=200r/m;
limit_req_status 429;
limit_conn_zone $binary_remote_addr zone=connperip:10m;
limit_conn_status 429;
server {
access_log /var/log/nginx/access.log log_discourse;
#listen unix:/var/nginx/nginx.http.sock;
listen 443 ssl;
listen [::]:443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.cer;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
set_real_ip_from unix:;
set_real_ip_from 127.0.0.1/32;
set_real_ip_from ::1/128;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
gzip on;
gzip_vary on;
gzip_min_length 1000;
gzip_comp_level 5;
gzip_types application/json text/css text/javascript application/x-javascript application/javascript image/svg+xml application/wasm;
gzip_proxied any;
# Décommenter et configurer cette section pour la prise en charge HTTPS
# NOTE : Placez votre certificat SSL dans votre répertoire de configuration nginx principal (/etc/nginx)
#
# rewrite ^/(.*) https://enter.your.web.hostname.here/$1 permanent;
#
# listen 443 ssl;
# ssl_certificate your-hostname-cert.pem;
# ssl_certificate_key your-hostname-cert.key;
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# ssl_ciphers HIGH:!aNULL:!MD5;
#
server_tokens off;
sendfile on;
keepalive_timeout 65;
# taille maximale de téléchargement de fichier (maintenez à jour lors de la modification du paramètre de site correspondant)
client_max_body_size 128m ;
# chemin vers le répertoire public de Discourse
set $public /var/www/discourse/public;
# sans les weak etags, nous ne tirons aucun bénéfice des etags sur le contenu dynamiquement compressé
# de plus, les etags sont basés sur le fichier dans nginx et non sur le hachage des données
# utilisez les dates, cela résout le problème correctement même entre serveurs
etag off;
# empêcher le téléchargement direct des sauvegardes
location ^~ /backups/ {
internal;
}
# bypass de la pile Rails avec un 204 peu coûteux pour les requêtes favicon.ico
location /favicon.ico {
return 204;
access_log off;
log_not_found off;
}
location / {
root $public;
add_header ETag "";
# auth_basic on;
# auth_basic_user_file /etc/nginx/htpasswd;
location ~ ^/uploads/short-url/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Request-Start "t=${msec}";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $thescheme;
proxy_pass http://discourse;
break;
}
location ~ ^/(secure-media-uploads/|secure-uploads)/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Request-Start "t=${msec}";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $thescheme;
proxy_pass http://discourse;
break;
}
location ~* (fonts|assets|plugins|uploads)/.*\.(eot|ttf|woff|woff2|ico|otf)$ {
expires 1y;
add_header Cache-Control public,immutable;
add_header Access-Control-Allow-Origin *;
}
location = /srv/status {
access_log off;
log_not_found off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Request-Start "t=${msec}";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $thescheme;
proxy_pass http://discourse;
break;
}
# un minimum de mise en cache ici pour éviter de continuer à demander
# à long terme, nous devrions probablement augmenter à 1 an
location ~ ^/javascripts/ {
expires 1d;
add_header Cache-Control public,immutable;
add_header Access-Control-Allow-Origin *;
}
location ~ ^/assets/(?<asset_path>.+)$ {
expires 1y;
# le pipeline d'assets active ceci
brotli_static on;
gzip_static on;
add_header Cache-Control public,immutable;
# HOOK dans l'emplacement des assets (utilisé pour l'extensibilité)
# TODO Je ne pense pas que ce break soit nécessaire, il sort simplement de rewrite
break;
}
location ~ ^/plugins/ {
expires 1y;
add_header Cache-Control public,immutable;
add_header Access-Control-Allow-Origin *;
}
# mettre en cache les emojis
location ~ /images/emoji/ {
expires 1y;
add_header Cache-Control public,immutable;
add_header Access-Control-Allow-Origin *;
}
location ~ ^/uploads/ {
# NOTE : il est vraiment ennuyeux que nous ne puissions pas simplement définir les en-têtes
# au niveau supérieur et les hériter.
#
# proxy_set_header n'hérite PAS, par conception, nous devons le répéter,
# sinon les en-têtes ne sont pas définis correctement
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Request-Start "t=${msec}";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $thescheme;
proxy_set_header X-Sendfile-Type X-Accel-Redirect;
proxy_set_header X-Accel-Mapping $public/=/downloads/;
expires 1y;
add_header Cache-Control public,immutable;
## règles optionnelles de protection contre le hotlinking des téléchargements
#valid_referers none blocked mysite.com *.mysite.com;
#if ($invalid_referer) { return 403; }
# CSS personnalisé
location ~ /stylesheet-cache/ {
add_header Access-Control-Allow-Origin *;
try_files $uri =404;
}
# cela nous permet de bypass Rails
location ~* \.(gif|png|jpg|jpeg|bmp|tif|tiff|ico|webp|avif)$ {
add_header Access-Control-Allow-Origin *;
try_files $uri =404;
}
# SVG nécessite un en-tête supplémentaire attaché
location ~* \.(svg)$ {
}
# miniatures et images optimisées
location ~ /_?optimized/ {
add_header Access-Control-Allow-Origin *;
try_files $uri =404;
}
proxy_pass http://discourse;
break;
}
location ~ ^/admin/backups/ {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Request-Start "t=${msec}";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $thescheme;
proxy_set_header X-Sendfile-Type X-Accel-Redirect;
proxy_set_header X-Accel-Mapping $public/=/downloads/;
proxy_pass http://discourse;
break;
}
# Ce gros bloc est nécessaire pour que nous puissions activer sélectivement
# l'accélération pour les sauvegardes, les avatars, les sprites, etc.
# voir la note sur la répétition ci-dessus
location ~ ^/(svg-sprite/|letter_avatar/|letter_avatar_proxy/|user_avatar|highlight-js|stylesheets|theme-javascripts|favicon/proxied|service-worker) {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Request-Start "t=${msec}";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $thescheme;
# si Set-Cookie est dans la réponse, rien n'est mis en cache
# c'est doublement mauvais car nous ne transmettons pas last modified
proxy_ignore_headers "Set-Cookie";
proxy_hide_header "Set-Cookie";
proxy_hide_header "X-Discourse-Username";
proxy_hide_header "X-Runtime";
# note : x-accel-redirect ne peut pas être utilisé avec proxy_cache
proxy_cache one;
proxy_cache_key "$scheme,$host,$request_uri";
proxy_cache_valid 200 301 302 7d;
#proxy_cache_bypass $bypass_cache;
proxy_pass http://discourse;
break;
}
# nous devons désactiver le buffering pour le message bus
location /message-bus/ {
proxy_set_header X-Request-Start "t=${msec}";
proxy_set_header Host $http_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 $thescheme;
proxy_http_version 1.1;
proxy_buffering off;
proxy_pass http://discourse;
break;
}
# cela signifie que chaque fichier dans public est essayé en premier
try_files $uri @discourse;
}
location /downloads/ {
internal;
alias $public/;
}
location @discourse {
limit_conn connperip 20;
limit_req zone=flood burst=12 nodelay;
limit_req zone=bot burst=100 nodelay;
proxy_set_header Host $http_host;
proxy_set_header X-Request-Start "t=${msec}";
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 $thescheme;
proxy_pass http://discourse;
}
}
Maintenant, votre Discourse est accessible depuis example.com:443.
Maintenance
Pour accéder à la console Rails, exécutez simplement rails c en tant qu’utilisateur discourse dans /var/www/discourse. La commande discourse que l’on trouve dans la documentation officielle est essentiellement bundle exec script/discourse.
Pour mettre à jour Discourse, consultez #upgrade-cmd, puis redémarrez Puma en utilisant soit puma restart soit puma phased-restart. Pour la différence, consultez puma/docs/restart.md at main · puma/puma · GitHub.