Installer Discourse sur un VPS avec Docker
Apprenez à déployer Discourse sur un VPS via le launcher officiel. Ce guide détaille la configuration du fichier app.yml, la gestion du swap, le SMTP et le rebuild du conteneur.
Installer Discourse sur un VPS : un conteneur, un fichier de configuration
Pour installer Discourse sur un VPS, exécutez l'installateur du projet, répondez à un court assistant et attendez la construction de l'image. Discourse est distribué sous la forme d'un conteneur Docker unique contenant l'application Rails, PostgreSQL, Redis et nginx. Tout ce que vous modifierez ultérieurement se trouve dans un seul fichier, /var/discourse/containers/app.yml, et chaque modification est appliquée au site via une reconstruction.
L'installation officielle est discourse_docker : un script shell launcher ainsi qu'un ensemble de modèles YAML. Discourse ne prend pas en charge les fichiers Compose que vous rédigeriez vous-même, et le conteneur n'est pas conçu pour être séparé manuellement. Si vous avez l'habitude de gérer des services sur un VPS avec Docker Compose, attendez-vous à une structure différente. Il n'y a pas de docker compose up -d ici, et ./launcher rebuild app constitue le déploiement.
Prérequis avant de commencer avec Discourse
Quatre exigences piègent souvent les utilisateurs, et chacune d'elles bloque la progression avant même d'atteindre la page de connexion.
- Mémoire vive. Un conteneur exécute PostgreSQL, Redis, Sidekiq et un serveur web Ruby. L'étape de build compile les assets et nécessite plus de mémoire que le site en fonctionnement.
- Un nom de domaine réel. L'exemple de configuration fourni est explicite : "Discourse ne fonctionnera pas avec une adresse IP brute."
- Un accès mail sortant. L'activation des comptes, la réinitialisation des mots de passe, les invitations administrateur et les résumés par mail transitent tous via SMTP (Simple Mail Transfer Protocol).
- Les ports 80 et 443 libres sur l'hôte, sauf si vous placez délibérément Discourse derrière un proxy déjà en place.
The data behind this chart
[
{
"label": "Documented minimum",
"ram_gb": 1,
"storage_gb": 10
},
{
"label": "Documented recommended",
"ram_gb": 2,
"storage_gb": 20
}
]Le document d'installation officiel fixe le seuil minimal à 1 Go de RAM avec swap et 10 Go d'espace disque, et recommande 2 Go de RAM avec 20 Go d'espace disque. Considérez la première valeur comme le strict nécessaire pour terminer l'installation, et non comme la configuration idéale pour héberger une communauté. Cet écart est important car le pic de consommation mémoire survient lors du build, et non lors du trafic utilisateur.
Faites pointer le domaine vers le serveur avant l'installation
Créez un enregistrement A pour le nom d'hôte que vous allez utiliser, puis confirmez-le depuis le serveur lui-même.
dig +short forum.example.com
curl -4 -s https://ifconfig.coLes deux commandes doivent afficher la même adresse. Elles doivent concorder car l'assistant d'installation effectue un test de connexion vers votre nom d'hôte, et un enregistrement qui pointe encore ailleurs fera échouer ce test. Un enregistrement créé il y a deux minutes peut également être encore en cache ; attendez donc que l'ancien TTL (time to live) expire au lieu de lutter contre l'assistant.
Décidez dès maintenant si l'enregistrement sera proxifié par un CDN. Un enregistrement proxifié masque l'adresse de votre serveur, et la requête de certificat du conteneur échouera alors, car le challenge ACME (automatic certificate management environment) est traité par le proxy au lieu de Discourse. Gardez l'enregistrement sans proxy pour la première installation.
Exécuter l'installateur officiel
Une seule commande installe git, installe Docker via le script d'installation officiel, clone discourse_docker dans /var/discourse et lance l'assistant de configuration.
wget -qO- https://raw.githubusercontent.com/discourse/discourse_docker/main/install-discourse | sudo bashSi Docker est déjà présent sur la machine et que vous préférez effectuer chaque étape manuellement, réalisez les opérations vous-même.
sudo -s
git clone https://github.com/discourse/discourse_docker.git /var/discourse
cd /var/discourse
./discourse-setupExécutez la commande en tant que root. Lancé par un utilisateur standard, discourse-setup s'arrête immédiatement avec This script must be run as root. Please sudo or log in as root first.. Si Docker n'est pas installé sur la machine, le processus s'arrête avec Docker is not installed. Please install Docker first., car le clonage manuel n'installe aucune dépendance pour vous.
Ce que demande l'assistant de configuration et ce qu'il écrit
En août 2026, discourse-setup n'est qu'une fine surcouche. Il exécute discourse/setup-wizard:release en tant que conteneur avec le réseau hôte et le socket Docker montés, afin que l'assistant puisse inspecter la machine qu'il configure. Il demande le nom d'hôte et les adresses e-mail de l'administrateur, puis vos paramètres SMTP. Il écrit containers/app.yml, puis lance une reconstruction.
Deux comportements méritent d'être connus avant de commencer. Si la machine manque de mémoire et n'a pas de swap, l'assistant s'arrête et propose d'en créer un : la surcouche crée alors un fichier /swapfile de 2 Go, l'ajoute à /etc/fstab, définit vm.swappiness = 10 dans /etc/sysctl.d/30-discourse-swap.conf, puis relance l'assistant. Lorsque l'assistant se termine, il affiche Rebuilding app in 5 seconds (Ctrl+C to cancel)... et exécute ./launcher rebuild app sur l'hôte. Cette compilation prend plusieurs minutes sur un petit VPS, et la première est la plus longue car chaque ressource est compilée à partir de zéro.
./discourse-setup --help liste les flags importants en cas de problème. --skip-rebuild écrit la configuration sans compiler, et --skip-connection-test ignore les vérifications DNS et de port. N'utilisez --skip-connection-test que si vous savez déjà pourquoi le test échoue, par exemple lorsque l'hôte se trouve derrière un pare-feu réseau que vous contrôlez.
Lisez app.yml avant la première reconstruction
L'assistant génère un fichier dont vous avez désormais la charge. Ouvrez-le avec sudo nano /var/discourse/containers/app.yml. Les sections suivantes déterminent la quasi-totalité du comportement.
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 correspond à l'adresse sur laquelle le site répond ; Discourse s'en sert pour construire ses liens. Une valeur erronée empêchera le site de fonctionner correctement après le chargement initial. DISCOURSE_DEVELOPER_EMAILS est une liste séparée par des virgules ; les adresses indiquées deviennent automatiquement administrateurs lors de la première inscription. Renseignez votre propre adresse et enregistrez-vous avec, car c'est ainsi que le premier compte administrateur est créé.
Le fichier stocke votre mot de passe SMTP en texte clair ; restreignez donc l'accès au répertoire avec sudo chmod 700 /var/discourse/containers. Le format YAML est sensible aux espaces : une indentation incorrecte provoque une erreur d'analyse lors de la reconstruction et rend le site indisponible. Un piège est documenté dans le fichier d'exemple lui-même. Un # situé à l'intérieur d'un mot de passe non entouré de guillemets est interprété comme le début d'un commentaire. Placez donc entre guillemets tout mot de passe en contenant un.
L'e-mail est l'étape qui bloque la plupart des installations
Depuis août 2026, l'assistant vous permet d'ignorer la configuration SMTP et d'utiliser à la place les connexions Discourse ID, et app.yml comporte un commutateur DISCOURSE_SKIP_EMAIL_SETUP correspondant, décrit comme permettant de passer la validation de la configuration e-mail. Ignorer cette étape est raisonnable pour une première approche du logiciel. C'est un mauvais choix pour une communauté, car sans courrier sortant, personne ne peut activer de compte ni réinitialiser de mot de passe.
Le problème pratique est que la plupart des fournisseurs de VPS bloquent le port sortant 25, donc un serveur mail standard sur la machine ne pourra pas distribuer les messages. Utilisez un relais authentifié sur le port 587, ou sur le port 465 avec TLS (transport layer security) implicite. Pour le port 465, configurez DISCOURSE_SMTP_FORCE_TLS: true, ce que la configuration d'exemple recommande pour ce port. Testez l'accessibilité depuis l'hôte avant de reconstruire.
nc -vz smtp.example.com 587Un résultat sain est une ligne unique se terminant par succeeded!. Une commande qui reste bloquée puis expire signifie que le port est bloqué sur le chemin sortant de votre VPS, et aucun paramètre Discourse ne peut corriger cela. Passez sur un port autorisé par votre fournisseur, ou demandez au fournisseur de l'ouvrir.
Une fois le site en ligne, envoyez un message de test depuis la page Email dans l'interface d'administration, puis consultez les onglets Skipped et Bounced sur cette même page. Ces onglets sont l'endroit où Discourse enregistre les e-mails qu'il a refusé d'envoyer et ceux que le relais a rejetés, et ils indiquent la raison, ce qui est plus rapide que de lire les journaux.
TLS : laisser le conteneur gérer son propre certificat
Si Discourse utilise les ports 80 et 443, utilisez son mécanisme de délivrance intégré. Décommentez les deux lignes de template SSL indiquées ci-dessus, puis relancez la compilation. Le template pilote acme.sh, stocke les certificats dans le volume partagé sous /shared/ssl, les renouvelle selon un calendrier interne au conteneur et configure Discourse pour forcer le HTTPS.
Le port 80 doit rester accessible depuis Internet pour que cela fonctionne, car le défi HTTP y est validé. Un pare-feu autorisant uniquement le port 443 aboutira à une compilation terminée, mais à un certificat qui ne sera jamais émis. Vérifiez le résultat avec ./launcher logs app immédiatement après la compilation.
Faut-il placer nginx ou Caddy en frontal ?
Si Discourse est le seul service web sur le VPS, ne le faites pas. Le conteneur exécute déjà un nginx optimisé. Un second proxy ajoute un saut réseau, un certificat supplémentaire à renouveler et une nouvelle source de bugs liés aux en-têtes.
Utilisez un frontal si le même VPS héberge d'autres sites. Ajoutez templates/web.socketed.template.yml à la liste des templates, commentez les deux lignes expose et laissez les deux templates SSL commentés. Le conteneur écoute alors sur un socket unix à /var/discourse/shared/standalone/nginx.http.sock et n'occupe aucun port, ce qui libère les ports 80 et 443 pour votre propre proxy.
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;
}
}Le deux-points final après .sock fait partie de la syntaxe des sockets unix de nginx, et sudo nginx -t refuse la configuration sans lui. X-Forwarded-Proto n'est pas optionnel non plus. Discourse génère des liens absolus ; sans cet en-tête, il émet des liens http:// sur une page HTTPS, ce qui provoque leur blocage par les navigateurs pour contenu mixte. Une fois le conteneur configuré sur un socket, la gestion du TLS vous incombe. Générez donc le certificat sur l'hôte avec Certbot sur Ubuntu 24.04 et nginx. Si vous n'avez pas encore choisi de proxy, la comparaison entre nginx, Caddy et Traefik détaille les compromis à prendre en compte.
Reconstructions, mises à jour et commandes indispensables
cd /var/discourse
./launcher rebuild apprebuild détruit le conteneur en cours d'exécution, en initialise un nouveau à partir de app.yml, puis le démarre. Le site est hors ligne pendant toute la durée de la reconstruction ; considérez donc chaque modification de configuration comme une interruption de service planifiée de quelques minutes.
La modification des seules valeurs sous env: ne nécessite pas cette opération. ./launcher destroy app && ./launcher start app recrée le conteneur à partir de l'image déjà construite, ce qui ne prend que quelques secondes. Toute modification sous templates: ou hooks: altère l'image elle-même et impose donc une reconstruction complète.
Les mises à jour s'effectuent de deux manières. Les versions mineures s'appliquent depuis l'interface web sur /admin/upgrade, via le plugin docker_manager que app.yml clone lors de la construction. Les changements apportés à l'image de base ou aux modèles proviennent de git.
cd /var/discourse
git pull
./launcher rebuild appLes reconstructions sont le point de défaillance des petits serveurs, car la compilation des assets constitue le pic de consommation mémoire du système. Une construction qui s'interrompt en cours de route, avec dmesg affichant une ligne telle que Out of memory: Killed process mentionnant un processus ruby, indique une saturation de la mémoire vive pendant la construction, même si le site fonctionnait correctement auparavant. Ajoutez du swap et relancez la reconstruction.
./launcher logs app
./launcher enter app
./launcher cleanuplogs affiche la sortie du conteneur, enter ouvre un shell à l'intérieur, et cleanup supprime les conteneurs arrêtés depuis plus de 24 heures. Exécutez cleanup régulièrement, car chaque reconstruction laisse derrière elle un ancien conteneur, ce qui peut saturer silencieusement l'espace disque sur un petit VPS.
Sauvegardes et fichiers exclus de l'archive
Effectuez vos sauvegardes depuis la page Backups de l'interface d'administration. L'archive est générée sur l'hôte à l'emplacement /var/discourse/shared/standalone/backups/default/. La même tâche peut être exécutée depuis un shell.
cd /var/discourse
./launcher enter app
discourse backupLa commande discourse restore <filename> permet d'inverser le processus ; les restaurations sont refusées tant que vous n'exécutez pas discourse enable_restore. Cette sécurité empêche qu'une commande erronée n'écrase un forum en production.
Il existe deux lacunes que vous devez combler vous-même. L'archive contient la base de données, mais elle n'inclut les fichiers téléversés que si l'option de sauvegarde correspondante est activée ; vérifiez ce paramètre avant de vous fier à l'archive. Elle ne contient jamais le fichier app.yml. Par conséquent, une restauration sur un VPS vierge nécessite toujours de reconfigurer votre nom d'hôte et vos paramètres SMTP. Vous devez donc copier ce fichier en dehors du serveur.
L'archive est stockée sur le même disque que le site qu'elle protège, ce qui ne constitue pas une sauvegarde robuste. Transférez-la régulièrement vers un emplacement distant.
rsync -avz root@forum.example.com:/var/discourse/shared/standalone/backups/default/ ~/discourse-backups/Le coût en RAM d'un forum actif
Le bootstrap définit UNICORN_WORKERS et db_shared_buffers en fonction de la mémoire et du CPU détectés, et la configuration par défaut limite les shared buffers à un quart de la mémoire totale. Chaque worker unicorn est un processus Ruby complet, et Sidekiq exécute les tâches de fond à leurs côtés ; la consommation de mémoire dépend donc des requêtes simultanées plutôt que du nombre de membres inscrits. Un forum peu fréquenté avec quelques centaines de membres ne constitue pas une charge de travail importante.
Ne dimensionnez pas le serveur en vous basant sur un chiffre lu dans un article, y compris celui-ci. Mesurez vos propres besoins.
free -m
docker stats --no-streamUn swap constamment sollicité associé à des pages lentes signifie que vous manquez de RAM. Une mémoire stable avec des pages lentes indique généralement un autre problème ; lisez donc ./launcher logs app avant de souscrire à une offre supérieure. Ajoutez également une vérification depuis l'extérieur du serveur, car un forum qui sature sa mémoire à 3h du matin s'arrête sans prévenir : un moniteur de statut Uptime Kuma auto-hébergé sur un hôte distinct vous préviendra avant vos membres.
Quand Discourse n'est pas le bon choix
Discourse est une application volumineuse avec une installation lourde et un cycle de reconstruction pour chaque paramètre situé dans app.yml. Ce coût permet d'obtenir de véritables outils de modération et une recherche qui reste efficace même avec une archive importante. Pour trente personnes souhaitant un espace de discussion, c'est une machine disproportionnée par rapport aux besoins. Consultez d'abord le comparatif des logiciels de forum auto-hébergés et choisissez Discourse parce que vous avez besoin de ses fonctionnalités, et non parce que c'est le nom que vous connaissez déjà.
FAQ
Puis-je installer Discourse sur un VPS sans nom de domaine ?
Non. La configuration fournie stipule que Discourse ne fonctionnera pas avec une simple adresse IP, et un DISCOURSE_HOSTNAME est requis. Discourse génère des liens absolus à partir de ce nom d'hôte ; une adresse IP brise donc les liens et empêche l'émission de certificats. Créez un enregistrement A avant de commencer et vérifiez avec dig +short forum.example.com qu'il pointe bien vers l'adresse de votre serveur.
Dois-je configurer SMTP pour terminer l'installation ?
Depuis août 2026, vous pouvez ignorer cette étape. L'assistant d'installation propose des connexions via Discourse ID à la place, et app.yml inclut une option pour passer la validation de la configuration e-mail. Pour toute utilisation au-delà d'un premier test, configurez-le, car l'activation des comptes et la réinitialisation des mots de passe passent par l'e-mail. Utilisez un relais authentifié sur le port 587 ou 465, car la plupart des fournisseurs VPS bloquent le port 25 sortant.
Pourquoi mon rebuild de Discourse a-t-il échoué en cours de route ?
La mémoire est la cause habituelle. La compilation des assets pendant le build nécessite plus de mémoire que le site en fonctionnement ; un serveur qui héberge correctement le forum peut donc échouer lors d'un rebuild. Si dmesg affiche Out of memory: Killed process en désignant un processus ruby, ajoutez du swap (le fichier de swap de l'assistant fait 2 Go) et relancez ./launcher rebuild app. Un build qui s'arrête sur une erreur YAML indique une erreur d'indentation dans app.yml.
Discourse doit-il être placé derrière mon propre nginx ou Caddy ?
Uniquement si le VPS héberge également d'autres sites. S'il est seul sur le serveur, laissez le conteneur gérer les ports 80 et 443 et émettre son propre certificat, ce qui réduit le nombre de composants mobiles. Pour partager la machine, ajoutez templates/web.socketed.template.yml, commentez les lignes expose et faites un proxy vers le socket unix à /var/discourse/shared/standalone/nginx.http.sock. Transmettez X-Forwarded-Proto, sinon Discourse générera des liens http:// sur une page HTTPS.
Comment sauvegarder un Discourse auto-hébergé ?
Utilisez la page Backups dans l'interface d'administration, ou exécutez discourse backup après ./launcher enter app. Les archives sont stockées sur l'hôte à /var/discourse/shared/standalone/backups/default/. Vérifiez que le paramètre incluant les uploads est activé, copiez /var/discourse/containers/app.yml avec l'archive, et déplacez le tout sur une autre machine, car une sauvegarde située sur le même disque que le site ne survit pas à la panne pour laquelle elle existe.