SSD Nodes Learn Hosting plans →
Guides Matt ConnorPar Matt Connor · Mis à jour le 2026-08-22

Installer Discourse sur un VPS avec Docker

Découvrez les prérequis Discourse : RAM et swap, domaine réel, SMTP, app.yml, rebuild, TLS et reverse proxy avec le Docker launcher officiel.

Installer Discourse sur un VPS : un conteneur, un fichier de configuration

Pour installer Discourse sur un VPS, exécutez l’installateur fourni par le projet, répondez à un court assistant, puis attendez la compilation. Discourse est distribué sous la forme d’un conteneur Docker unique qui contient l’application Rails, PostgreSQL, Redis et nginx. Toutes les modifications ultérieures se font dans un seul fichier, /var/discourse/containers/app.yml, et chaque modification est appliquée au site lors d’une nouvelle compilation.

L’installation officielle repose sur discourse_docker : un script shell launcher et un ensemble de modèles YAML. Discourse ne prend pas en charge un fichier 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 exécuter des services sur un VPS avec Docker Compose, attendez-vous à une architecture différente. Il n’y a pas de docker compose up -d ici, et ./launcher rebuild app constitue le déploiement.

Ce dont Discourse a besoin avant de commencer

Quatre prérequis posent souvent problème, et chacun peut vous bloquer avant l’affichage de la page de connexion.

  • Mémoire. 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. La configuration d’exemple fournie l’indique clairement : « Discourse ne fonctionnera pas avec une simple adresse IP. »
  • Un chemin d’envoi des e-mails. L’activation des comptes, les réinitialisations de mot de passe, les invitations d’administration et les e-mails de synthèse sortent via SMTP (simple mail transfer protocol).
  • Les ports 80 et 443 doivent être libres sur l’hôte, sauf si vous placez volontairement Discourse derrière un proxy déjà utilisé.
ChartDiscourse published hardware requirements (official install docs, August 2026)
The data behind this chart
[
  {
    "label": "Documented minimum",
    "ram_gb": 1,
    "storage_gb": 10
  },
  {
    "label": "Documented recommended",
    "ram_gb": 2,
    "storage_gb": 20
  }
]

La documentation officielle d’installation fixe le minimum à 1 Go de RAM avec du swap et 10 Go d’espace disque. Elle recommande 2 Go de RAM et 20 Go d’espace disque. Considérez la première ligne comme la configuration qui permet à l’installateur d’aller au bout, et non comme celle avec laquelle vous souhaitez exploiter une communauté. L’écart est important, car le pic de mémoire survient pendant le build, et non en fonction du trafic.

Pointez le domaine vers le serveur avant l’installation

Créez un enregistrement A pour le nom d’hôte que vous utiliserez, puis vérifiez-le depuis le serveur lui-même.

dig +short forum.example.com
curl -4 -s https://ifconfig.co

Les deux commandes doivent afficher la même adresse. Elles doivent être cohérentes, car l’assistant d’installation teste la connexion vers votre nom d’hôte. Un enregistrement qui pointe encore ailleurs échoue à ce test. Un enregistrement créé il y a deux minutes peut aussi être encore mis en cache. Attendez donc l’expiration de l’ancien TTL (time to live) au lieu de tenter de contourner l’assistant.

Décidez maintenant si l’enregistrement sera proxifié par un CDN. Un enregistrement proxifié masque l’adresse de votre serveur. La demande de certificat du conteneur échoue alors, car le proxy répond au challenge ACME (automatic certificate management environment) à la place de Discourse. Laissez l’enregistrement non proxifié pour la première installation.

Exécuter l’installateur officiel

Une seule commande installe git, installe Docker avec le script d’installation de Docker, clone discourse_docker dans /var/discourse et démarre l’assistant de configuration.

wget -qO- https://raw.githubusercontent.com/discourse/discourse_docker/main/install-discourse | sudo bash

Si Docker est déjà installé sur le serveur et que vous préférez voir chaque étape, effectuez la même opération manuellement.

sudo -s
git clone https://github.com/discourse/discourse_docker.git /var/discourse
cd /var/discourse
./discourse-setup

Exé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 le serveur, il s’arrête avec Docker is not installed. Please install Docker first., car le clonage manuel n’installe rien pour vous.

Ce que demande l’assistant de configuration et ce qu’il écrit

En août 2026, discourse-setup est une simple surcouche. Il exécute discourse/setup-wizard:release dans un conteneur avec le réseau de l’hôte et le socket Docker monté, afin que l’assistant puisse inspecter la machine qu’il configure. Il demande le nom d’hôte et les adresses e-mail d’administration, puis les paramètres SMTP. Il écrit containers/app.yml, puis relance la génération.

Deux comportements doivent être connus avant de commencer. Si la machine manque de mémoire et ne dispose d’aucun swap, l’assistant s’arrête et propose d’en créer un : la surcouche crée alors un fichier /swapfile de 2 GB, l’ajoute à /etc/fstab, définit vm.swappiness = 10 dans /etc/sysctl.d/30-discourse-swap.conf, puis relance l’assistant. Lorsque l’assistant termine, il affiche Rebuilding app in 5 seconds (Ctrl+C to cancel)... et exécute ./launcher rebuild app sur l’hôte. Cette génération prend plusieurs minutes sur un petit VPS. La première est la plus lente, car chaque asset est compilé à partir de zéro.

./discourse-setup --help répertorie les flags utiles en cas de problème. --skip-rebuild écrit la configuration sans lancer la génération, et --skip-connection-test ignore les vérifications DNS et des ports. Utilisez --skip-connection-test uniquement si vous savez déjà pourquoi le test échoue, par exemple lorsque l’hôte se trouve derrière un firewall réseau que vous contrôlez.

Lire app.yml avant la première reconstruction

L’assistant écrit un fichier que vous devez désormais maintenir. Ouvrez-le avec sudo nano /var/discourse/containers/app.yml. Voici les paramètres qui déterminent presque tout.

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 est l’adresse à laquelle le site répond. Discourse construit ses liens à partir de cette valeur. Une valeur incorrecte peut donc charger le site une première fois, puis vous rediriger ailleurs. DISCOURSE_DEVELOPER_EMAILS est une liste séparée par des virgules. Ces adresses obtiennent automatiquement les droits d’administration lors de la première inscription. Indiquez votre propre adresse et inscrivez-vous avec celle-ci. C’est ainsi que le premier compte administrateur est créé.

Le fichier stocke votre mot de passe SMTP en clair. Restreignez donc le répertoire avec sudo chmod 700 /var/discourse/containers. Il s’agit également d’un fichier YAML : les espaces sont significatifs pour la configuration. Une clé mal indentée fait échouer la compilation avec une erreur d’analyse et vous laisse sans site. Un piège est documenté dans le fichier d’exemple lui-même. Dans un mot de passe non entouré de guillemets, un # commence un commentaire. Entourez donc de guillemets tout mot de passe qui en contient un.

L’email est l’étape qui bloque la plupart des installations

Depuis août 2026, l’assistant permet d’ignorer la configuration SMTP et d’utiliser les connexions Discourse ID à la place. app.yml propose un commutateur DISCOURSE_SKIP_EMAIL_SETUP correspondant, décrit comme permettant d’ignorer la validation de la configuration de l’email. Cette option convient pour découvrir le logiciel. Elle est déconseillée pour une communauté, car sans email sortant, personne ne peut activer un compte ni réinitialiser un mot de passe.

Le problème pratique est que la plupart des fournisseurs de VPS bloquent le port sortant 25. Un serveur de messagerie installé directement sur la machine ne pourra donc pas remettre les messages. Utilisez un relais authentifié sur le port 587, ou sur le port 465 avec TLS implicite (Transport Layer Security). Pour le port 465, définissez DISCOURSE_SMTP_FORCE_TLS: true, comme le recommande la configuration d’exemple pour ce port. Testez l’accessibilité depuis l’hôte avant de reconstruire l’instance.

nc -vz smtp.example.com 587

Un résultat correct se compose d’une seule ligne qui se termine par succeeded!. Une commande qui reste bloquée puis expire indique que le port est bloqué sur le chemin sortant de votre VPS. Aucun paramètre Discourse ne peut corriger ce problème. Utilisez un port autorisé par votre fournisseur ou demandez-lui de l’ouvrir.

Une fois le site opérationnel, envoyez un message de test depuis la page Email de l’interface d’administration, puis consultez les onglets Skipped et Bounced sur cette même page. C’est dans ces onglets que Discourse enregistre les messages qu’il a refusé d’envoyer et ceux que le relais a rejetés. La raison y est indiquée, ce qui est plus rapide que de consulter les journaux.

TLS : laisser le conteneur obtenir son propre certificat

Si Discourse utilise les ports 80 et 443, utilisez son mécanisme intégré d’émission des certificats. Décommentez les deux lignes du template SSL indiquées plus haut, puis reconstruisez le conteneur. Le template pilote acme.sh, stocke les certificats dans le volume partagé sous /shared/ssl, les renouvelle selon une planification interne au conteneur et configure Discourse pour forcer HTTPS.

Le port 80 doit rester accessible depuis Internet pour que cela fonctionne, car le challenge HTTP reçoit sa réponse sur ce port. Un pare-feu qui autorise uniquement 443 permet d’achever le build, mais le certificat n’est jamais émis. Vérifiez le résultat avec ./launcher logs app juste après la reconstruction.

Faut-il placer nginx ou Caddy devant ?

Si Discourse est le seul service web du VPS, ne le faites pas. Le conteneur exécute déjà une instance nginx optimisée. Un second proxy ajoute un saut, un autre certificat à renouveler et une nouvelle source de problèmes liés aux en-têtes.

Placez un proxy devant Discourse lorsque 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 à l’adresse /var/discourse/shared/standalone/nginx.http.sock et n’utilise 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;
  }
}

Les deux-points à la fin de .sock font partie de la syntaxe du socket Unix de nginx. sudo nginx -t refuse la configuration sans ces deux-points. X-Forwarded-Proto est également obligatoire. Discourse écrit des liens absolus. Sans cet en-tête, il génère des liens http:// sur une page HTTPS, et les navigateurs les bloquent comme contenu mixte. Une fois le conteneur configuré avec un socket, la gestion de TLS vous revient. Émettez 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 présente les compromis de chaque solution.

Reconstruction, mises à niveau et commandes que vous utiliserez réellement

cd /var/discourse
./launcher rebuild app

rebuild détruit le conteneur en cours d’exécution, en crée un nouveau à partir de app.yml, puis le démarre. Le site est indisponible pendant toute la reconstruction. Considérez donc chaque modification de configuration comme une interruption planifiée de quelques minutes.

Modifier uniquement les valeurs sous env: ne nécessite pas de reconstruction complète. ./launcher destroy app && ./launcher start app recrée le conteneur à partir de l’image déjà construite, ce qui prend quelques secondes. Toute modification sous templates: ou hooks: change l’image elle-même et nécessite donc une reconstruction complète.

Les mises à niveau arrivent de deux façons. Les versions intermédiaires s’appliquent depuis l’interface web à /admin/upgrade, grâce au plugin docker_manager que app.yml clone pendant la construction. Les modifications de l’image de base ou des templates proviennent de git.

cd /var/discourse
git pull
./launcher rebuild app

Les reconstructions sont souvent à l’origine des échecs sur les petits serveurs, car la compilation des assets correspond au pic de consommation mémoire de l’ensemble du système. Une construction qui s’arrête en cours de route, avec dmesg affichant une ligne comme Out of memory: Killed process et mentionnant un processus ruby, indique un manque de mémoire pendant la construction, même si le site fonctionnait correctement auparavant. Ajoutez du swap, puis relancez la reconstruction.

./launcher logs app
./launcher enter app
./launcher cleanup

logs affiche la sortie du conteneur, enter ouvre un shell à l’intérieur de celui-ci, et cleanup supprime les conteneurs arrêtés depuis plus de 24 heures. Exécutez cleanup de temps en temps, car chaque reconstruction laisse un ancien conteneur et le disque d’un petit VPS finit par se remplir discrètement.

Sauvegardes et fichier exclu de la sauvegarde

Effectuez les sauvegardes depuis la page Backups de l’interface Admin. L’archive est enregistrée sur l’hôte dans /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 backup

discourse restore <filename> inverse l’opération, et les restaurations sont refusées tant que vous n’avez pas exécuté discourse enable_restore. Cette protection empêche une commande lancée par erreur d’écraser un forum en production.

Vous devez combler vous-même deux lacunes. L’archive contient la base de données et les fichiers importés uniquement lorsque le paramètre de sauvegarde qui inclut les uploads est activé. Vérifiez donc ce paramètre avant de considérer la sauvegarde comme complète. Elle ne contient jamais app.yml. Une restauration sur un VPS vierge nécessite donc encore votre nom d’hôte et votre configuration SMTP. Copiez également ce fichier hors du serveur.

L’archive se trouve aussi sur le même disque que le site qu’elle protège. Ce n’est pas une sauvegarde. Transférez-la régulièrement vers un autre emplacement.

rsync -avz root@forum.example.com:/var/discourse/shared/standalone/backups/default/ ~/discourse-backups/

Ce que coûte un forum actif en RAM

Le bootstrap définit UNICORN_WORKERS et db_shared_buffers à partir de la mémoire et du CPU détectés. La configuration d’exemple limite les buffers partagés au quart de la mémoire totale. Chaque worker Unicorn est un processus Ruby complet. Sidekiq exécute les tâches en arrière-plan en parallèle. L’utilisation mémoire dépend donc du nombre de requêtes simultanées, et non du nombre de membres inscrits. Un forum calme comptant quelques centaines de membres ne représente pas une charge importante. Ce qui partage le serveur compte généralement davantage. S’il s’agit d’une photothèque, les valeurs minimales de RAM mesurées dans la comparaison entre PhotoPrism et Immich vous indiqueront si un rebuild de Discourse dispose encore de suffisamment de mémoire pour se terminer.

Ne dimensionnez pas le serveur à partir d’un chiffre indiqué dans un article, y compris celui-ci. Mesurez votre propre charge.

free -m
docker stats --no-stream

Un swap utilisé en permanence avec des pages lentes signifie que la RAM est insuffisante. Une mémoire stable avec des pages lentes indique généralement un autre problème. Consultez donc ./launcher logs app avant de souscrire à une offre plus grande. Ajoutez également un contrôle depuis l’extérieur du serveur. Un forum qui manque de mémoire à 3 h du matin tombe en panne sans forcément générer d’alerte visible. Un monitor Uptime Kuma auto-hébergé sur un hôte séparé vous préviendra avant vos membres.

Quand Discourse n’est pas le bon choix

Discourse est une application volumineuse, dont l’installation est lourde et qui doit être reconstruite après chaque modification d’un paramètre stocké dans app.yml. Ce coût apporte de véritables outils de modération et un moteur de recherche qui reste fonctionnel lorsque les archives deviennent importantes. Pour trente personnes qui veulent simplement disposer d’un espace de discussion, c’est une solution plus lourde que nécessaire. Consultez d’abord la comparaison des logiciels de forum auto-hébergés, puis choisissez Discourse parce que vous avez besoin de ses fonctionnalités, et non parce que c’est le nom que vous connaissiez déjà.

FAQ

Puis-je installer Discourse sur un VPS sans nom de domaine ?

Non. La configuration fournie indique que Discourse ne fonctionnera pas avec une simple adresse IP, et DISCOURSE_HOSTNAME est requis. Discourse construit des liens absolus à partir de ce nom d’hôte. Une adresse IP à cet emplacement casse les liens et empêche l’émission du certificat. Créez un enregistrement A avant de commencer, puis vérifiez avec dig +short forum.example.com qu’il pointe vers l’adresse de votre serveur.

Dois-je configurer SMTP pour terminer l’installation ?

Depuis août 2026, vous pouvez passer cette étape. L’assistant d’installation propose à la place les connexions Discourse ID, et app.yml accepte une option qui désactive la validation de la configuration de l’e-mail. Pour toute utilisation allant au-delà d’un premier test, configurez SMTP, car l’activation des comptes et les réinitialisations de mot de passe sont envoyées par e-mail. Utilisez un relais authentifié sur le port 587 ou 465, car la plupart des fournisseurs de VPS bloquent les connexions sortantes sur le port 25.

Pourquoi la reconstruction de Discourse a-t-elle échoué en cours d’exécution ?

La mémoire est la cause la plus fréquente. La compilation des ressources pendant la reconstruction nécessite plus de mémoire que le site en fonctionnement. Une machine qui sert correctement le forum peut donc quand même échouer à le reconstruire. Si dmesg affiche Out of memory: Killed process avec un processus Ruby, ajoutez du swap — le fichier de swap créé par l’assistant fait 2 GB — puis relancez ./launcher rebuild app. Un build qui s’arrête sur une erreur YAML indique plutôt une erreur d’indentation dans app.yml.

Discourse doit-il être placé derrière votre propre nginx ou Caddy ?

Uniquement si le VPS héberge également d’autres sites. Si Discourse est seul sur le serveur, laissez le conteneur utiliser les ports 80 et 443 et émettre lui-même son certificat. Vous aurez ainsi moins d’éléments à gérer. Pour partager la machine, ajoutez templates/web.socketed.template.yml, commentez les lignes expose et faites suivre les requêtes vers le socket Unix situé à /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 Admin, ou exécutez discourse backup après ./launcher enter app. Les archives sont placées sur l’hôte, dans /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, puis transférez les deux éléments vers une autre machine. Une sauvegarde stockée sur le même disque que le site ne survivra pas à la panne contre laquelle elle est censée vous protéger.