Instalación de Discourse detrás de un Reverse Proxy usando la instalación recomendada (sostenida)

En esta publicación, te mostraré exactamente cómo ejecutar Discourse detrás de un proxy inverso de Nginx. Aún utilizando los métodos de instalación admitidos.

Requisitos:

  1. Nombre de dominio válido.
  2. Certificado SSL válido.
  3. Entradas DNS válidas que apunten al nombre de dominio.
  4. Servidor de correo electrónico o proveedor SMTP válido y funcional.
  5. Proxy inverso Nginx funcional y capacidad para acceder a la instancia y crear el certificado SSL para su uso posterior.
  6. VM o contenedor LxC que pueda ejecutar Docker.

Para esta guía, usaré mi propia instancia como ejemplo, con todas las funciones en funcionamiento, copias de seguridad, actualizaciones, etc. Probado durante aproximadamente 2 semanas.

Especificaciones de mi VM de Discourse:

  • CPU: 4 núcleos
  • RAM: 6 GB
  • Swap: 8 GB (archivo de intercambio SSD)
  • Almacenamiento: 50 GB (SSD)
  • SO: Ubuntu 22.04.3

Puedes usar el mínimo, pero al probar. Discourse básicamente abusa de 2 GB fácilmente. El uso inactivo es de aproximadamente 1.48 GB de 6 GB.

NOTA: Esta instalación utiliza el proxy inverso para crear los certificados SSL. Se puede usar Certbot si se prefiere.

PASO 1:

Descarga la última versión de Ubuntu Server desde Get Ubuntu Server | Download | Ubuntu

PASO 2:

  1. Instala Ubuntu Server en la plantilla VM/LxC
    1.1 Asegúrate de que Ubuntu esté actualizado con todos los paquetes del servidor.
  2. Usa los siguientes comandos para instalar todos los paquetes requeridos:
apt update -y && apt upgrade -y && apt wget curl zip git docker.io nginx -y && reboot

PASO 3:
Instalando Discourse. Siguiendo la Guía para principiantes discourse/docs/INSTALL-cloud.md at main · discourse/discourse · GitHub

Conéctate a tu servidor por SSH y simplemente ingresa lo siguiente:

sudo -s
git clone https://github.com/discourse/discourse_docker.git /var/discourse
cd /var/discourse
chmod 700 containers

Después de completar lo anterior. Puedes ejecutar ./discourse-setup

Sigue todos los pasos e ingrésalos correctamente, ya que esto será vital para una instalación exitosa.
Los pasos te pedirán lo siguiente; ejemplo de la guía de configuración:

Deja que la instalación se ejecute, puede llevar un tiempo dependiendo de tu conexión a Internet y las especificaciones del servidor. Tardó entre 5 y 8 minutos en completar la instalación utilizando la configuración de mi VM.

PASO 4:

Una vez que la instalación se haya completado, verás el comando de inicio que está utilizando el contenedor de Docker y el ID del contenedor generado (hash).

NOTA: Dado que estás detrás de un proxy a través de la WAN, mostrará un error 502 y, dado que el contenedor utiliza la red de Docker. No será accesible desde la WAN o LAN, a menos que te conectes a la red 172.17.0.1/16, que no necesitaremos.

Verifica que la instalación se haya completado y que el contenedor de Docker se esté ejecutando usando:

docker ps

Deberías obtener la siguiente salida.

CONTAINER ID   IMAGE                 COMMAND        CREATED      STATUS       PORTS     NAMES
XXXXXX   local_discourse/app   “/sbin/boot”   6 days ago   Up 7 hours             app

PASO 5:
Actualizando el archivo app.yml para que se ajuste a la configuración del proxy inverso.

Usando tu editor de texto preferido, abre /var/discourse/container/app.yml
Reemplaza/comenta las siguientes líneas: (esto será manejado por el Nginx local)

  #- "templates/web.ssl.template.yml"
  #- "templates/web.letsencrypt.ssl.template.yml"

Inmediatamente después de #- "templates/web.letsencrypt.ssl.template.yml" agrega lo siguiente:

- "templates/web.socketed.template.yml"

Luego comenta la sección expose, ya que Discourse ahora usará un websocket, finalmente Nginx podrá iniciarse ya que liberará los puertos 80 y 443 requeridos localmente.

#expose:
# - "80:80" # http
# - "443:443" # https

Ahora, para forzar a Discourse a suministrar solo enlaces con HTTPS, agrega lo siguiente en la sección env:

# FORCE SSL
DISCOURSE_FORCE_HTTPS: true

Ahora, para que las cosas finalmente funcionen, necesitarás reconstruir la aplicación. Usando lo siguiente:

cd /var/discourse
./launcher rebuild app

Deja que eso se ejecute y se complete. Para verificar si fue exitoso, asegúrate de que el comando de ejecución de Docker se muestre o simplemente ejecuta docker ps y verás el contenedor en ejecución.

PASO 6:

Habilitando Nginx y configurando el sitio predeterminado para que apunte al contenedor de Docker. Permitiendo que el proxy inverso acceda al contenedor utilizando el Nginx local.

Ejecuta lo siguiente:

systemctl enable nginx && systemctl start nginx

Para verificar si se está ejecutando. Intenta acceder a la página de inicio predeterminada de Nginx usando tu navegador y la IP local.
Ej:

http://10.10.0.4

Deberías obtener una página de felicitaciones de Nginx.

PASO 7:
Actualizando la configuración predeterminada a lo siguiente:
Primero vacía el archivo de configuración:

echo "" > /etc/nginx/sites-available/default

Usando tu editor de texto, abre el archivo de configuración y agrega lo siguiente:
NOTA: Actualiza server_name con tu dominio y ssl_certificate, ssl_certificate_key con la ubicación de tu certificado SSL y clave.
Se puede usar Certbot, pero yo simplemente hago rsync de mi certificado y clave desde mi proxy inverso.

server {
    listen 80; listen [::]:80;
    server_name add.yourdomain.com;
    server_tokens off;
    return 301 https://$host$request_uri;
}
# Default server configuration
#
server {
	# SSL configuration
	#
	server_tokens off;
	listen 443 ssl default_server;
	listen [::]:443 ssl default_server;

    server_name add.yourdomain.com;
	ssl_certificate /etc/ssl/certs/your_ssl_cert.bundle;
	ssl_certificate_key /etc/ssl/private/your_ssl_cert.key;
	ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
	ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';
	ssl_prefer_server_ciphers on;
	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 $proxy_add_x_forwarded_for;
		proxy_set_header X-Forwarded-Proto $scheme;
		proxy_set_header X-Real-IP $remote_addr;
	}
}

Guarda el nuevo archivo predeterminado y ejecuta systemctl restart nginx.

PASO 8:

Abre tu navegador y ve al dominio de tu foro https://midominio.com y serás recibido con la siguiente página.

Eso es todo, todo listo. Luego puedes seguir los pasos para registrarte y poner en marcha tu foro/comunidad :).

PASO ADICIONAL:

Mantenimiento Post-Instalación
Sugerimos encarecidamente que actives las actualizaciones de seguridad automáticas para tu sistema operativo. En Ubuntu, usa dpkg-reconfigure -plow unattended-upgrades

Para obtener soporte, puedes usar esta comunidad, para el Nginx local, no dudes en pedir ayuda en esta publicación.

10 Me gusta

Esto es genial. Gracias.

Algo que me gustaría ver como mejora de esta guía es poder servir activos estáticos a través de nginx.

Actualmente, todas las solicitudes estáticas van al worker en la configuración anterior. Todavía estoy tratando de encontrar una solución para esto, publicaré una actualización si la encuentro.

¿Puedes especificar a qué archivos estáticos te refieres?

¿Quizás estás hablando de las páginas estáticas 404, 302, 500 de nginx o de las páginas estáticas de Discourse?

Lo investigué un poco y descubrí que la redirección para las páginas 404 funciona. La única página que parece que no puedo configurar es la página de error 500.

¿Puedes ser un poco más específico para que sepa exactamente en qué estoy investigando? :slight_smile:

Esta guía es excelente. Pude seguirla entera hasta que llegué a la carga del sitio web. Me aparece un error de “demasiados redireccionamientos”. Cuando uso la IP, Discourse carga bien, pero no puedo superar el problema de los redireccionamientos. Tan cerca.

Cualquier ayuda sería apreciada.

Gracias.

¿Alguien sabe cómo puedo resolver este problema? El sitio funciona, pero noto estos errores en el archivo nginx error.log del contenedor. He estado usando un proxy inverso configurado según estas instrucciones.

Mirando /etc/nginx/conf.d/discourse.conf en el contenedor, veo

upstream discourse { server 127.0.0.1:3000; }

¿Debería ser algo como?:

upstream discourse { http://unix:/var/discourse/shared/standalone/nginx.http.sock; }

Mirando en /var/log/nginx en el contenedor, veo errores como:

2025/02/09 21:00:21 [error] 69#69: *1 connect() failed (111: Connection refused) while connecting to upstream, client: xx.xx.xxx.xxx, server: _, request: "POST /message-bus/b39980c2387e4750bc1e320cb6195424/poll?dlp=t HTTP/1.1", upstream: "http://127.0.0.1:3000/message-bus/b39980c2387e4750bc1e320cb6195424/poll?dlp=t", host: "discourse.xxxx.com"

2025/02/09 21:00:23 [error] 67#67: *3 connect() failed (111: Connection refused) while connecting to upstream, client: xx.xx.xx.xxx, server: _, request: "GET /chat/api/me/channels HTTP/1.1", upstream: "http://127.0.0.1:3000/chat/api/me/channels", host: "discourse.xxxx.com"

2025/02/09 21:00:23 [error] 70#70: *5 connect() failed (111: Connection refused) while connecting to upstream, client: xx.xx.xx.xxx, server: _, request: "POST /message-bus/d95b6999d26242f28f4875732b195440/poll HTTP/1.1", upstream: "http://127.0.0.1:3000/message-bus/d95b6999d26242f28f4875732b195440/poll", host: "discourse.xxxx.com"

¡Gracias!

¿Seguiste correctamente el paso 5? Parece que no estás usando un socket.

Sí, estoy usando un socket en el proxy inverso local fuera del contenedor y, compruebo, seguí el Paso 5 en app.yml.

/var/discourse/containers# grep web.socketed.template.yml app.yml
  - "templates/web.socketed.template.yml"

/var/discourse/containers# grep templates/web.ssl.template.yml app.yml
  #  - "templates/web.ssl.template.yml"

/var/discourse/containers# grep templates/web.letsencrypt.ssl.template.yml app.yml
  #  - "templates/web.letsencrypt.ssl.template.yml"

/var/discourse/containers# grep http app.yml

#  - "80:80"   # http
#  - "443:443" # https

/var/discourse/containers# grep DISCOURSE_FORCE_HTTPS app.yml
DISCOURSE_FORCE_HTTPS: true
templates:
  - "templates/postgres.template.yml"
  - "templates/redis.template.yml"
  - "templates/web.template.yml"
  ## Uncomment the next line to enable the IPv6 listener
  #- "templates/web.ipv6.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"
  - "templates/web.socketed.template.yml"
## which TCP/IP ports should this container expose?
## If you want Discourse to share a port with another webserver like Apache or nginx,
## see https://meta.discourse.org/t/17247 for details
#expose:
#  - "80:80"   # http
#  - "443:443" # https

Revisé esto: Discourse working with jwilder /nginx proxy & acme-companion

pero realmente no puedo entenderlo en términos de mi problema…