Cómo instalar Discourse en un VPS con Docker
Instala Discourse con el launcher oficial de Docker: revisa RAM y swap, configura dominio y SMTP, edita app.yml y aplica cambios con rebuild y TLS.
Instalar Discourse en un VPS: un contenedor, un archivo de configuración
Para instalar Discourse en un VPS, ejecute el instalador propio del proyecto, responda a un asistente breve y espere a que termine la compilación. Discourse se distribuye como un único contenedor Docker que contiene la aplicación Rails, PostgreSQL, Redis y nginx. Todo lo que cambie después se encuentra en un solo archivo, /var/discourse/containers/app.yml, y cada cambio se aplica al sitio mediante una nueva compilación.
La instalación oficial es discourse_docker: un script de shell launcher y un conjunto de plantillas YAML. Discourse no admite un archivo Compose escrito por usted, y el contenedor no está diseñado para dividirse manualmente. Si está acostumbrado a ejecutar servicios en un VPS con Docker Compose, encontrará una estructura diferente. Aquí no existe docker compose up -d, y ./launcher rebuild app es el despliegue.
Qué necesita Discourse antes de empezar
Cuatro requisitos suelen causar problemas, y cada uno puede bloquear el proceso 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 recursos estáticos y necesita más memoria que el sitio en ejecución.
- Un nombre de dominio real. La configuración de ejemplo incluida lo indica claramente: "Discourse no funcionará con una dirección IP sin nombre de dominio."
- Una ruta de correo saliente. La activación de cuentas, los restablecimientos de contraseña, las invitaciones de administrador y los mensajes de resumen se envían mediante SMTP (protocolo simple de transferencia de correo).
- Los puertos 80 y 443 libres en el host, salvo que coloque Discourse deliberadamente detrás de un proxy que ya esté ejecutando.
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 como mínimo 1 GB de RAM con swap y 10 GB de disco, y recomienda 2 GB de RAM con 20 GB de disco. Interprete la primera fila como la cantidad que permite completar el instalador, no como la cantidad que necesita para ejecutar una comunidad. La diferencia es importante porque el pico de memoria se produce durante la compilación, no por el tráfico.
Apunte el dominio al servidor antes de instalar
Cree un registro A para el nombre de host que utilizará y, después, confírmelo desde el propio servidor.
dig +short forum.example.com
curl -4 -s https://ifconfig.coAmbos comandos deben mostrar la misma dirección. Deben coincidir porque el asistente de configuración prueba la conexión con el nombre de host. Si el registro todavía apunta a otro lugar, esa prueba falla. Es posible que un registro creado hace dos minutos siga almacenado en caché. Espere a que transcurra el TTL (tiempo de vida) anterior en lugar de intentar forzar el asistente.
Decida ahora si un CDN hará de proxy para el registro. Un registro con proxy oculta la dirección del servidor. En ese caso, la solicitud de certificado del contenedor falla porque el proxy responde al desafío de ACME (entorno de gestión automática de certificados) en lugar de Discourse. Mantenga el registro sin proxy durante la primera instalación.
Ejecute el instalador oficial
Un comando instala git, instala Docker con el script de instalación del propio 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 bashSi Docker ya está instalado en el servidor y prefiere ver cada paso, haga el mismo trabajo manualmente.
sudo -s
git clone https://github.com/discourse/discourse_docker.git /var/discourse
cd /var/discourse
./discourse-setupEjecútelo como root. Si se inicia como un usuario normal, discourse-setup se detiene de inmediato con This script must be run as root. Please sudo or log in as root first.. Si Docker no está instalado en el servidor, se detiene con Docker is not installed. Please install Docker first. porque la clonación manual no instala nada por usted.
Qué solicita el asistente de configuración y qué archivos escribe
A fecha de agosto de 2026, discourse-setup es un wrapper ligero. Ejecuta discourse/setup-wizard:release como contenedor, con la red del host y el socket de Docker montados, para que el asistente pueda inspeccionar la máquina que está configurando. Solicita el nombre de host y las direcciones de correo electrónico de administración, y después los datos SMTP. Escribe containers/app.yml y vuelve a compilar.
Conviene conocer dos comportamientos antes de empezar. Si la máquina tiene poca memoria y no tiene swap, el asistente se detiene y ofrece crearla: el wrapper crea un /swapfile de 2 GB, lo añade a /etc/fstab, establece vm.swappiness = 10 en /etc/sysctl.d/30-discourse-swap.conf y vuelve a iniciar el asistente. Cuando el asistente termina, muestra 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. La primera es la más lenta porque todos los recursos se compilan desde cero.
./discourse-setup --help muestra las opciones relevantes cuando algo falla. --skip-rebuild escribe la configuración sin compilar y --skip-connection-test omite las comprobaciones de DNS y de puertos. Use --skip-connection-test sólo cuando ya sepa por qué falla la prueba, por ejemplo, cuando el host está detrás de un firewall de red que usted controla.
Leer app.yml antes de la primera reconstrucción
El asistente escribe un archivo que ahora debe mantener usted. Ábralo con sudo nano /var/discourse/containers/app.yml. Estas son las partes que determinan casi todo.
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 genera sus enlaces a partir de ella. Un valor incorrecto hace que el sitio cargue una vez y después le redirija a otro lugar. DISCOURSE_DEVELOPER_EMAILS es una lista separada por comas, y esas direcciones se convierten automáticamente en administradores durante el primer registro. Introduzca ahí su propia dirección y regístrese con ella, porque así se crea la primera cuenta de administrador.
El archivo almacena la contraseña de SMTP en texto plano, por lo que debe restringir el directorio con sudo chmod 700 /var/discourse/containers. También es YAML, lo que significa que los espacios en blanco forman parte de la configuración: una clave mal alineada hace que la compilación falle con un error de análisis y le deja sin sitio. Hay una trampa documentada en el propio archivo de ejemplo. Un # dentro de una contraseña sin comillas inicia un comentario, por lo que debe poner entre comillas cualquier contraseña que lo contenga.
El correo electrónico es el paso que detiene la mayoría de las instalaciones
Desde agosto de 2026, el asistente permite omitir SMTP y usar inicios de sesión de Discourse ID. app.yml incluye un interruptor DISCOURSE_SKIP_EMAIL_SETUP equivalente, descrito allí como la omisión de la validación de la configuración del correo. Omitir este paso es razonable para probar el software por primera vez. Es una mala opción para una comunidad, porque 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 saliente 25, por lo que un servidor de correo básico en el propio equipo no podrá entregar mensajes. Use un relay autenticado en el puerto 587 o en el puerto 465 con TLS implícito (seguridad de la capa de transporte). Para el puerto 465, establezca DISCOURSE_SMTP_FORCE_TLS: true; la configuración de ejemplo recomienda esta opción para ese puerto. Compruebe la conectividad desde el host antes de volver a crear la instalación.
nc -vz smtp.example.com 587Un resultado correcto es una sola línea que termina en succeeded!. Si un comando se queda bloqueado y después agota el tiempo de espera, significa que el puerto está bloqueado en la ruta de salida de su VPS. Ningún ajuste de Discourse puede solucionar ese problema. Use un puerto permitido por el proveedor o pídale que lo abra.
Cuando el sitio esté disponible, envíe un mensaje de prueba desde la página Email de Admin y revise las pestañas Skipped y Bounced de esa misma página. En esas pestañas, Discourse registra los mensajes que se negó a enviar y los mensajes que el relay rechazó. También indican el motivo, lo que resulta más rápido que revisar los registros.
TLS: permita que el contenedor obtenga su propio certificado
Si Discourse controla los puertos 80 y 443, use su mecanismo integrado de emisión. Quite las marcas de comentario de las dos líneas de plantilla SSL mostradas arriba y vuelva a compilar. La plantilla controla acme.sh, almacena los certificados en el volumen compartido en /shared/ssl, los renueva según un calendario dentro del contenedor y configura Discourse para forzar HTTPS.
El puerto 80 debe seguir siendo accesible desde Internet para que esto funcione, porque allí se responde al desafío HTTP. Un firewall que permita sólo 443 produce una compilación que termina correctamente, pero el certificado nunca se emite. Compruebe el resultado con ./launcher logs app justo después de volver a compilar.
¿Debe colocar nginx o Caddy delante?
Si Discourse es el único servicio web del VPS, no lo haga. El contenedor ya ejecuta un nginx optimizado. 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 aloje otros sitios. Añada templates/web.socketed.template.yml a la lista de plantillas, comente las dos líneas expose y deje comentadas las dos plantillas SSL. El contenedor escuchará entonces en un socket Unix en /var/discourse/shared/standalone/nginx.http.sock y no ocupará ningún puerto. Así, los puertos 80 y 443 quedan disponibles 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 forman parte de la sintaxis de socket Unix de nginx. sudo nginx -t rechaza la configuración si faltan. X-Forwarded-Proto tampoco es opcional. Discourse escribe enlaces absolutos. Sin esa cabecera, genera enlaces http:// en una página HTTPS y los navegadores los bloquean como contenido mixto. Cuando el contenedor usa un socket, TLS pasa a ser responsabilidad suya. Emita el certificado en el host con Certbot en Ubuntu 24.04 y nginx. Si todavía no ha elegido un proxy, la comparación entre nginx, Caddy y Traefik explica las ventajas y desventajas de cada opción.
Reconstrucciones, actualizaciones y los comandos que realmente usará
cd /var/discourse
./launcher rebuild apprebuild destruye el contenedor en ejecución, crea uno nuevo desde app.yml y lo inicia. El sitio permanece fuera de servicio durante toda la compilación, por lo que cada cambio de configuración debe tratarse como una ventana de mantenimiento programada de unos minutos.
Los cambios realizados sólo en los valores de env: no requieren ese proceso. ./launcher destroy app && ./launcher start app vuelve a crear el contenedor a partir de la imagen que ya compiló, lo que tarda unos segundos. Cualquier cambio en templates: o hooks: modifica la imagen, por lo que requiere la reconstrucción completa.
Las actualizaciones llegan de dos formas. Las versiones menores se aplican desde la interfaz web en /admin/upgrade, mediante el plugin docker_manager que app.yml clona durante la compilación. Los cambios en la imagen base o en las plantillas proceden de git.
cd /var/discourse
git pull
./launcher rebuild appLas reconstrucciones son el punto en el que fallan los servidores pequeños, porque la compilación de recursos es el pico de consumo de memoria de todo el sistema. Si una compilación se detiene a mitad de proceso y dmesg muestra una línea como Out of memory: Killed process que identifica un proceso ruby, el sistema se quedó sin memoria durante la compilación, aunque el sitio funcionara correctamente hasta ese momento. Añada swap y vuelva a ejecutar la reconstrucción.
./launcher logs app
./launcher enter app
./launcher cleanuplogs muestra la salida del contenedor, enter abre un shell dentro de él y cleanup elimina los contenedores que llevan más de 24 horas detenidos. Ejecute cleanup de vez en cuando, porque cada reconstrucción deja un contenedor antiguo y el disco de un VPS pequeño se llena sin que resulte evidente.
Copias de seguridad y el archivo que la copia no contiene
Cree copias de seguridad desde la página Backups de Admin. El archivo se guarda en el host, en /var/discourse/shared/standalone/backups/default/. El mismo trabajo se ejecuta desde un shell.
cd /var/discourse
./launcher enter app
discourse backupdiscourse restore <filename> revierte el proceso y las restauraciones se rechazan hasta que ejecute discourse enable_restore. Esta protección evita que un comando accidental sobrescriba un foro en producción.
Debe resolver dos carencias por su cuenta. El archivo contiene la base de datos y contiene los archivos cargados sólo cuando está activada la opción de copia que incluye las cargas. Compruebe esa opción antes de confiar en la copia. Nunca contiene app.yml. Por tanto, una restauración en un VPS nuevo todavía necesita el nombre de host y el bloque SMTP. También debe copiar ese archivo fuera del servidor.
El archivo también se encuentra en el mismo disco que el sitio que protege. Eso no es una copia de seguridad. Transfiéralo a otra ubicación según una programación.
rsync -avz root@forum.example.com:/var/discourse/shared/standalone/backups/default/ ~/discourse-backups/Cuánta RAM consume un foro activo
El arranque establece UNICORN_WORKERS y db_shared_buffers a partir de la memoria y la CPU detectadas, y la configuración de ejemplo limita los búferes compartidos a una cuarta parte de la memoria total. Cada worker de unicorn es un proceso Ruby completo, y Sidekiq ejecuta trabajos en segundo plano junto a ellos. Por eso, el consumo de memoria depende de las peticiones simultáneas, no del número de miembros registrados. Un foro tranquilo con unos cientos de miembros no supone una carga elevada. Normalmente importa más qué otros servicios comparten el servidor. Si se trata de una biblioteca de fotos, los valores mínimos de RAM medidos en la comparación entre PhotoPrism e Immich indican si una reconstrucción de Discourse todavía dispone de memoria suficiente para terminar.
No dimensione el servidor a partir de una cifra de un artículo, incluido este. Mida su propio entorno.
free -m
docker stats --no-streamEl uso constante de swap junto con páginas lentas indica que falta RAM. Si la memoria se mantiene estable y las páginas siguen lentas, la causa suele ser otra. Lea ./launcher logs app antes de contratar un plan más grande. Añada también una comprobación desde fuera del servidor, porque un foro que se queda sin memoria a las 3am falla sin mostrar avisos: un monitor de estado Uptime Kuma autohospedado en otro host le avisará antes que sus miembros.
Cuando 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 configuración que se almacena en app.yml. Ese coste proporciona herramientas de moderación completas y una búsqueda que sigue funcionando cuando el archivo es grande. Para treinta personas que sólo necesitan un lugar donde conversar, requiere más recursos de los que necesita la conversación. Lea primero la comparación de software de foros autohospedado y elija Discourse porque necesita sus funciones, 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 incluida indica que Discourse no funciona con una dirección IP sin nombre de dominio, y se requiere DISCOURSE_HOSTNAME. Discourse genera enlaces absolutos a partir de ese nombre de host, por lo que una dirección IP rompe los enlaces e impide emitir el certificado. Cree un registro A antes de empezar y confirme con dig +short forum.example.com que resuelve a la dirección de su servidor.
¿Tengo que configurar SMTP para terminar la instalación?
Desde agosto de 2026 puede omitirlo. El asistente de configuración ofrece iniciar sesión mediante Discourse ID y app.yml incluye una opción para omitir la validación de la configuración de correo electrónico. Para cualquier uso posterior a una primera prueba, configúrelo, porque la activación de cuentas y los restablecimientos de contraseñas se envían por correo. Use un relay autenticado en el puerto 587 o 465, ya que la mayoría de los proveedores de VPS bloquean el puerto saliente 25.
¿Por qué falló la reconstrucción de Discourse a mitad del proceso?
La memoria suele ser la causa. La compilación de recursos durante la compilación necesita más memoria que el sitio en ejecución, por lo que un servidor que sirve el foro correctamente puede seguir fallando al reconstruirlo. Si dmesg muestra Out of memory: Killed process y menciona un proceso de ruby, añada swap (el archivo de swap propio del asistente tiene 2 GB) y vuelva a ejecutar ./launcher rebuild app. Si la compilación se detiene por un error de YAML, el problema apunta a una sangría incorrecta en app.yml.
¿Debo poner Discourse detrás de mi propio nginx o Caddy?
Sólo cuando el VPS aloja también otros sitios. Si Discourse es el único servicio del servidor, deje que el contenedor conserve los puertos 80 y 443 y emita su propio certificado. Así habrá menos componentes que administrar. Para compartir el servidor, añada templates/web.socketed.template.yml, comente las líneas expose y configure el proxy hacia el socket Unix en /var/discourse/shared/standalone/nginx.http.sock. Transmita X-Forwarded-Proto, o Discourse generará enlaces http:// en una página HTTPS.
¿Cómo hago una copia de seguridad de un Discourse autohospedado?
Use la página Backups del área Admin o ejecute discourse backup después de ./launcher enter app. Los archivos comprimidos se guardan en el host, en /var/discourse/shared/standalone/backups/default/. Confirme que esté activada la opción que incluye las cargas de archivos, copie /var/discourse/containers/app.yml junto con el archivo comprimido y mueva ambos a otra máquina, porque una copia de seguridad en el mismo disco que el sitio no sobrevive al fallo para el que se creó.