SSD Nodes Learn 🎉 VPS desde $5.50/mes
Guías Matt ConnorPor Matt Connor

Cómo instalar Discourse en un VPS con Docker

Guía técnica para desplegar Discourse usando el script oficial. Aprende a configurar app.yml, gestionar swap, configurar SMTP y aplicar cambios mediante el comando rebuild.

Instalar Discourse en un VPS: un contenedor, un archivo de configuración

Para instalar Discourse en un VPS, ejecute el instalador del propio proyecto, responda a un breve asistente y espere a que finalice la compilación. Discourse se distribuye como un único contenedor Docker que contiene la aplicación Rails, PostgreSQL, Redis y nginx. Todo lo que modifique posteriormente reside en un único archivo, /var/discourse/containers/app.yml, y cada cambio se aplica al sitio mediante una recompilación.

La instalación oficial es discourse_docker: un script de shell launcher junto con un conjunto de plantillas YAML. Discourse no admite un archivo Compose escrito por usted mismo, y el contenedor no está diseñado para ser dividido manualmente. Si está acostumbrado a ejecutar servicios en un VPS con Docker Compose, espere una estructura diferente. Aquí no existe docker compose up -d, y ./launcher rebuild app es el método de despliegue.

Requisitos previos para Discourse

Cuatro requisitos suelen causar problemas y cada uno de ellos impide el acceso antes de llegar a la página de inicio de sesión.

  • Memoria. Un contenedor ejecuta PostgreSQL, Redis, Sidekiq y un servidor web Ruby. El paso de compilación genera los assets y requiere más memoria que el sitio en ejecución.
  • Un nombre de dominio real. La configuración de ejemplo que se distribuye lo indica claramente: "Discourse no funcionará con una dirección IP directa".
  • Una ruta de correo saliente. La activación de cuentas, el restablecimiento de contraseñas, las invitaciones de administrador y los resúmenes de correo se envían mediante SMTP (simple mail transfer protocol).
  • Puertos 80 y 443 libres en el host, a menos que coloque deliberadamente Discourse detrás de un proxy que ya esté ejecutando.
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
  }
]

El documento oficial de instalación establece un mínimo de 1 GB de RAM con swap y 10 GB de disco, y recomienda 2 GB de RAM con 20 GB de disco. Considere la primera cifra como el valor necesario para que el instalador finalice, no como el valor recomendado para mantener una comunidad. La diferencia es importante porque el pico de memoria ocurre durante la compilación, no por el tráfico.

Apunte el dominio al servidor antes de realizar la instalación

Cree un registro A para el nombre de host que utilizará y, a continuación, confírmelo desde el propio servidor.

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

Ambos comandos deben mostrar la misma dirección. Deben coincidir porque el asistente de configuración ejecuta una prueba de conexión contra su nombre de host, y un registro que todavía apunte a otro lugar hará que dicha prueba falle. Un registro creado hace dos minutos también podría estar almacenado en caché; por lo tanto, espere a que expire el TTL (tiempo de vida) anterior en lugar de intentar forzar al asistente.

Decida ahora si el registro será gestionado a través de un CDN. Un registro proxy oculta la dirección de su servidor, lo que provoca que la solicitud de certificado del contenedor falle, ya que el desafío ACME (entorno de gestión automática de certificados) es respondido por el proxy en lugar de por Discourse. Mantenga el registro sin proxy durante la primera instalación.

Ejecutar el instalador oficial

Un solo comando instala git, instala Docker mediante el script de instalación propio de Docker, clona discourse_docker en /var/discourse e inicia el asistente de configuración.

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

Si Docker ya está presente en el equipo y prefiere ver cada paso, realice el mismo trabajo manualmente.

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

Ejecútelo como root. Si se inicia como un usuario normal, discourse-setup se detiene inmediatamente con This script must be run as root. Please sudo or log in as root first.. Si no hay Docker en el equipo, se detiene con Docker is not installed. Please install Docker first., ya que la clonación manual no instala nada por usted.

Lo que solicita el asistente de configuración y lo que escribe

Desde agosto de 2026, discourse-setup es un envoltorio ligero. Ejecuta discourse/setup-wizard:release como un contenedor con la red del host y el socket de Docker montados, de modo que el asistente pueda inspeccionar la máquina que está configurando. Solicita el nombre de host y las direcciones de correo electrónico del administrador, y luego los datos de su bloque SMTP. Escribe containers/app.yml y, a continuación, reconstruye.

Vale la pena conocer dos comportamientos antes de empezar. Si la máquina tiene poca memoria y no dispone de swap, el asistente se detiene y ofrece crearla: el envoltorio genera entonces un archivo /swapfile de 2 GB, lo añade a /etc/fstab, establece vm.swappiness = 10 en /etc/sysctl.d/30-discourse-swap.conf y reinicia el asistente. Cuando el asistente termina, imprime Rebuilding app in 5 seconds (Ctrl+C to cancel)... y ejecuta ./launcher rebuild app en el host. Esa compilación tarda varios minutos en un VPS pequeño, y la primera es la más lenta porque todos los recursos se compilan desde cero.

./discourse-setup --help enumera los flags importantes cuando algo falla. --skip-rebuild escribe la configuración sin compilar, y --skip-connection-test omite las comprobaciones de DNS y puertos. Utilice --skip-connection-test solo cuando ya sepa por qué falla la prueba, por ejemplo, cuando el host se encuentra detrás de un firewall de red que usted controla.

Lea app.yml antes de la primera reconstrucción

El asistente genera un archivo que ahora es su responsabilidad mantener. Ábralo con sudo nano /var/discourse/containers/app.yml. Estas son las secciones que determinan casi todo el funcionamiento.

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 es la dirección en la que responde el sitio y Discourse construye sus enlaces a partir de ella; un valor incorrecto hará que el sitio cargue una vez y luego lo redirija a otro lugar. DISCOURSE_DEVELOPER_EMAILS es una lista separada por comas; esas direcciones se convierten automáticamente en administradores al registrarse por primera vez. Introduzca su propia dirección ahí y regístrese con ella, ya que así es como se crea la primera cuenta de administrador.

El archivo almacena su contraseña SMTP en texto plano, por lo que debe restringir el directorio con sudo chmod 700 /var/discourse/containers. Además, al ser YAML, los espacios en blanco son parte de la configuración: una clave mal alineada provoca un error de análisis durante la compilación y deja el sitio inoperativo. Una trampa común está documentada en el propio archivo de ejemplo. Un # dentro de una contraseña sin comillas inicia un comentario, por lo que debe encerrar entre comillas cualquier contraseña que contenga ese carácter.

El correo electrónico es el paso que detiene la mayoría de las instalaciones

A partir de agosto de 2026, el asistente permite omitir SMTP y utilizar inicios de sesión mediante Discourse ID, y app.yml incluye un interruptor DISCOURSE_SKIP_EMAIL_SETUP equivalente, descrito allí como una forma de omitir la validación de la configuración de correo. Omitir este paso es razonable para un primer vistazo al software. Es una mala elección para una comunidad, ya que sin correo saliente nadie puede activar una cuenta ni restablecer una contraseña.

El problema práctico es que la mayoría de los proveedores de VPS bloquean el puerto de salida 25, por lo que un servidor de correo básico en el equipo no realizará entregas. Utilice un relay autenticado en el puerto 587, o en el 465 con TLS (transport layer security) implícito. Para el puerto 465, configure DISCOURSE_SMTP_FORCE_TLS: true, que la configuración de ejemplo recomienda para ese puerto. Pruebe la conectividad desde el host antes de reconstruir.

nc -vz smtp.example.com 587

Un resultado correcto es una sola línea que termina en succeeded!. Un comando que se queda bloqueado y luego agota el tiempo de espera significa que el puerto está bloqueado en la ruta de salida de su VPS, y ninguna configuración de Discourse soluciona eso. Cambie a un puerto que su proveedor permita o solicite al proveedor que lo abra.

Una vez que el sitio esté activo, envíe un mensaje de prueba desde la página de Email en Admin, luego lea las pestañas Skipped y Bounced en esa misma página. Esas pestañas son donde Discourse registra el correo que rechazó enviar y el correo que el relay rechazó, y allí se indica el motivo, lo cual es más rápido que leer los registros.

TLS: permita que el contenedor obtenga su propio certificado

Si Discourse gestiona los puertos 80 y 443, utilice su sistema de emisión integrado. Quite los comentarios de las dos líneas de plantillas SSL mostradas anteriormente y, a continuación, recompile. La plantilla controla acme.sh, almacena los certificados en el volumen compartido bajo /shared/ssl, los renueva según una programación dentro del contenedor y configura Discourse para forzar HTTPS.

El puerto 80 debe permanecer accesible desde Internet para que esto funcione, ya que el desafío HTTP se responde allí. Un firewall que solo permita el puerto 443 resultará en una compilación que finaliza, pero un certificado que nunca se emite. Compruebe el resultado con ./launcher logs app inmediatamente después de la recompilación.

¿Debería colocar nginx o Caddy delante?

Si Discourse es el único servicio web en el VPS, no lo haga. El contenedor ya ejecuta un nginx optimizado, y un segundo proxy añade un salto, otro certificado que renovar y una nueva fuente de errores en las cabeceras.

Colóquelo delante cuando el mismo VPS sirva otros sitios. Añada templates/web.socketed.template.yml a la lista de plantillas, comente ambas líneas expose y deje las dos plantillas SSL comentadas. El contenedor escuchará entonces en un socket unix en /var/discourse/shared/standalone/nginx.http.sock y no ocupará ningún puerto, lo que libera los puertos 80 y 443 para su propio 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;
  }
}

Los dos puntos finales después de .sock son parte de la sintaxis de sockets unix de nginx, y sudo nginx -t rechaza la configuración si no están presentes. X-Forwarded-Proto tampoco es opcional. Discourse escribe enlaces absolutos, por lo que sin esa cabecera emite enlaces http:// en una página HTTPS, y los navegadores los bloquean como contenido mixto. Con el contenedor conectado mediante socket, el TLS pasa a ser su responsabilidad, así que emita el certificado en el host con Certbot en Ubuntu 24.04 y nginx. Si aún no se ha decidido por un proxy, la comparativa entre nginx, Caddy y Traefik cubre el compromiso que está asumiendo.

Reconstrucciones, actualizaciones y los comandos que realmente utilizará

cd /var/discourse
./launcher rebuild app

rebuild destruye el contenedor en ejecución, arranca uno nuevo a partir de app.yml y lo inicia. El sitio permanece fuera de línea durante toda la compilación, así que considere cada cambio de configuración como un tiempo de inactividad programado de unos pocos minutos.

Cambiar solo los valores bajo env: no requiere esto. ./launcher destroy app && ./launcher start app recrea el contenedor a partir de la imagen que ya compiló, lo cual toma segundos. Cualquier cambio bajo templates: o hooks: modifica la imagen en sí, por lo que requiere una reconstrucción completa.

Las actualizaciones llegan de dos formas. Las versiones menores se aplican desde la interfaz web en /admin/upgrade, proporcionadas por el plugin docker_manager que app.yml clona durante la compilación. Los cambios en la imagen base o en las plantillas provienen de git.

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

Las reconstrucciones son el punto donde fallan los servidores pequeños, ya que la compilación de activos es el pico de memoria de todo el sistema. Una compilación que se detiene a mitad de camino, con dmesg mostrando una línea como Out of memory: Killed process que nombra un proceso ruby, se quedó sin memoria durante la compilación, aunque el sitio funcionaba correctamente de antemano. Añada espacio de intercambio (swap) y ejecute la reconstrucción de nuevo.

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

logs imprime la salida del contenedor, enter abre una shell dentro de él y cleanup elimina los contenedores que han estado detenidos durante más de 24 horas. Ejecute cleanup de vez en cuando, ya que cada reconstrucción deja un contenedor antiguo atrás y el disco en un VPS pequeño se agota silenciosamente.

Copias de seguridad y el archivo que no contienen

Realice copias de seguridad desde la página Backups en Admin. El archivo se guarda en el host en /var/discourse/shared/standalone/backups/default/. La misma tarea se ejecuta desde una shell.

cd /var/discourse
./launcher enter app
discourse backup

discourse restore <filename> revierte el proceso y las restauraciones se rechazan hasta que ejecute discourse enable_restore. Esta protección existe para que un comando erróneo no sobrescriba un foro en producción.

Existen dos brechas que debe cerrar usted mismo. El archivo contiene la base de datos y solo incluye los archivos subidos si la configuración de copia de seguridad que incluye subidas está activada; verifique esa configuración antes de confiar en ella. Nunca contiene app.yml, por lo que una restauración en un VPS nuevo aún requiere su nombre de host y el bloque SMTP, lo que significa que también debe copiar ese archivo fuera del servidor.

El archivo también reside en el mismo disco que el sitio que protege, lo cual no constituye una copia de seguridad. Transfiéralo a otra ubicación de forma programada.

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

El coste en RAM de un foro con mucha actividad

El proceso de arranque establece UNICORN_WORKERS y db_shared_buffers a partir de la memoria y CPU que detecta, y la configuración de ejemplo limita los shared buffers a una cuarta parte de la memoria total. Cada worker de unicorn es un proceso completo de Ruby, y Sidekiq ejecuta tareas en segundo plano junto a ellos, por lo que el uso de memoria depende de las peticiones concurrentes y no del número de miembros registrados. Un foro poco activo con unos pocos cientos de miembros no supone una carga de trabajo elevada.

No dimensione el servidor basándose en una cifra de un artículo, incluido este. Mida la suya propia.

free -m
docker stats --no-stream

Si el swap está en uso constante y las páginas cargan lento, significa que le falta RAM. Si la memoria se mantiene estable pero las páginas siguen lentas, el problema suele ser otro, así que lea ./launcher logs app antes de contratar un plan superior. Añada también una comprobación desde fuera del servidor, ya que un foro que se queda sin memoria a las 3 de la mañana falla silenciosamente: un monitor de estado Uptime Kuma autohospedado en un host independiente le avisará antes que sus miembros.

Cuándo Discourse no es la opción adecuada

Discourse es una aplicación grande con una instalación pesada y un ciclo de reconstrucción para cada ajuste que resida en app.yml. Ese coste compensa con herramientas de moderación reales y un buscador que sigue funcionando cuando el archivo es extenso. Para treinta personas que buscan un lugar donde hablar, es más máquina de lo que la conversación requiere. Lea primero la comparativa de software de foros autohospedados y elija Discourse porque desea lo que ofrece, no porque sea el nombre que ya conocía.

FAQ

¿Puedo instalar Discourse en un VPS sin un nombre de dominio?

No. La configuración distribuida establece que Discourse no funcionará con una dirección IP desnuda, y se requiere DISCOURSE_HOSTNAME. Discourse construye enlaces absolutos a partir de ese nombre de host, por lo que una dirección IP ahí rompe los enlaces y bloquea la emisión de certificados. Cree un registro A antes de comenzar y confirme con dig +short forum.example.com que resuelve a la dirección de su servidor.

¿Debo configurar SMTP para terminar la instalación?

A partir de agosto de 2026 puede omitirlo. El asistente de configuración ofrece inicios de sesión mediante Discourse ID en su lugar, y app.yml incluye un parámetro que omite la validación de la configuración de correo. Para cualquier uso más allá de una primera revisión, configúrelo, ya que la activación de cuentas y el restablecimiento de contraseñas se realizan por correo. Utilice un relay autenticado en el puerto 587 o 465, dado que la mayoría de los proveedores de VPS bloquean el puerto 25 de salida.

¿Por qué falló mi reconstrucción de Discourse a mitad del proceso?

La memoria es la causa habitual. La compilación de activos durante la construcción requiere más memoria que el sitio en ejecución, por lo que un servidor que aloja el foro correctamente puede fallar al reconstruirlo. Si dmesg muestra Out of memory: Killed process haciendo referencia a un proceso ruby, añada swap (el archivo de intercambio del asistente es de 2 GB) y ejecute ./launcher rebuild app de nuevo. Una construcción que se detiene por un error de YAML apunta a un error de sangría en app.yml.

¿Debería Discourse estar detrás de mi propio nginx o Caddy?

Solo cuando el VPS aloja otros sitios también. Si está solo en un servidor, deje que el contenedor mantenga los puertos 80 y 443 y emita su propio certificado, lo que reduce la complejidad. Para compartir la máquina, añada templates/web.socketed.template.yml, comente las líneas expose y realice el proxy hacia el socket unix en /var/discourse/shared/standalone/nginx.http.sock. Pase X-Forwarded-Proto, o Discourse emitirá enlaces http:// en una página HTTPS.

¿Cómo realizo una copia de seguridad de un Discourse autohospedado?

Utilice la página de Backups en el panel de administración, o ejecute discourse backup después de ./launcher enter app. Los archivos se guardan en el host en /var/discourse/shared/standalone/backups/default/. Confirme que la configuración que incluye las subidas esté activada, copie /var/discourse/containers/app.yml junto al archivo, y mueva ambos a otra máquina, ya que una copia de seguridad en el mismo disco que el sitio no sobrevive al fallo para el cual existe.