Instalar Rocket.Chat con Docker Compose
Guía para desplegar Rocket.Chat en un VPS usando Docker Compose. Incluye configuración de MongoDB replica set, TLS y gestión de memoria RAM necesaria.
Lo que vas a construir
Un chat privado para tu equipo con control total: Rocket.Chat ejecutándose en tu propio VPS mediante Docker Compose, con cifrado TLS y todos los mensajes almacenados en una base de datos MongoDB que puedes respaldar y mover. Rocket.Chat es una alternativa madura y de código abierto a Slack y Teams; incluye canales, mensajes directos, hilos, intercambio de archivos, voz y video, todo en hardware que tú alquilas y controlas. La aplicación es un único contenedor que se inicia en minutos. Los errores suelen ocurrir en la base de datos, por lo que la mayor parte de esta guía trata sobre MongoDB. En particular, abordaremos un requisito que sorprende a todos la primera vez: Rocket.Chat no funciona con una instancia de MongoDB independiente. Requiere un replica set, incluso si ese "set" es de un solo nodo.
Requisitos previos y el cálculo de RAM que nadie te explica
Dimensiona el servidor con honestidad. El mínimo realista para un equipo pequeño es 2 vCPU y 4 GB de RAM. El proceso Node.js de Rocket.Chat consume aproximadamente entre 1 y 1.5 GB por sí solo, y el cache WiredTiger de MongoDB reserva por defecto cerca de la mitad de la RAM restante. En un VPS de 2 GB, ambos procesos inician correctamente, pero colisionan en cuanto llega tráfico real: el cache de MongoDB crece, el heap de Node crece, el kernel se queda sin páginas y el out-of-memory killer finaliza el proceso más pesado, que suele ser mongod. El contenedor imprime Killed, Docker lo reinicia y obtienes un servidor de chat que se cae cada pocos minutos bajo una carga que debería soportar sin problemas. 2 GB es suficiente para pruebas con dos personas; no es un servidor para un equipo. Comienza con 4 GB, y asigna 8 GB si esperas decenas de usuarios concurrentes, videollamadas o un historial de archivos subidos en crecimiento.
También necesitas tener tres elementos configurados antes de empezar. Un nombre de dominio con un registro A apuntando a la IP pública del VPS; las funciones en tiempo real de Rocket.Chat y los clientes móviles requieren un hostname estable, no una IP directa. Puertos 80 y 443 abiertos tanto en el firewall del servidor como en el firewall de red de tu proveedor, que suele ser un control independiente en la mayoría de los paneles. Y un VPS KVM con Ubuntu 24.04 recién instalado con acceso root o sudo. Si aún estás decidiendo si un servidor de chat es el primer servicio adecuado para ejecutar, la guía sobre qué merece la pena auto-alojar en 2026 analiza las ventajas y desventajas.
Instalar el motor de Docker y el plugin Compose
Use el repositorio apt propio de Docker, no el paquete docker.io que incluye Ubuntu ni el antiguo binario de Python docker-compose independiente. El Compose moderno es un plugin de Docker que se invoca como docker compose — con un espacio, no un guion. La versión antigua docker-compose v1 ha llegado al final de su vida útil y gestiona incorrectamente la sintaxis de healthcheck y dependencias que se muestra a continuación.
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginConfirme que ambos componentes están presentes:
sudo docker version
sudo docker compose versionEl resultado de docker compose version debe ser algo similar a Docker Compose version v2.x para validar la instalación. Si ocurre un error con docker: 'compose' is not a docker command, el plugin no se instaló correctamente y encontrará fallos confusos más adelante; solucione este problema ahora.
El archivo compose: MongoDB como un replica set de un solo nodo
Esta es la parte donde la gente comete errores, así que léala con atención. Rocket.Chat utiliza change streams de MongoDB para enviar mensajes nuevos a los clientes conectados en tiempo real, y los change streams solo están disponibles en un replica set. Si apunta Rocket.Chat a un mongod independiente, se conectará, fallará al abrir un change stream y entrará en un bucle de reinicios infinito. La solución no es compleja: ejecute un contenedor de MongoDB ordinario, pero inícielo con --replSet y luego inicialice un set de un solo miembro.
Cree un directorio de trabajo y un compose.yml:
services:
mongodb:
image: mongo:8.0
restart: always
command: ["mongod", "--replSet", "rs0", "--bind_ip_all", "--oplogSize", "128"]
volumes:
- mongodb_data:/data/db
- mongodb_config:/data/configdb
healthcheck:
test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 10s
retries: 12
rocketchat:
image: registry.rocket.chat/rocketchat/rocket.chat:8.5.1
restart: always
depends_on:
mongodb:
condition: service_healthy
environment:
MONGO_URL: "mongodb://mongodb:27017/rocketchat?replicaSet=rs0"
MONGO_OPLOG_URL: "mongodb://mongodb:27017/local?replicaSet=rs0"
ROOT_URL: "https://chat.example.com"
PORT: "3000"
ports:
- "127.0.0.1:3000:3000"
volumes:
mongodb_data:
mongodb_config:Algunas elecciones aquí son deliberadas. El puerto de Rocket.Chat se publica en 127.0.0.1:3000, no en 0.0.0.0; la aplicación no tiene TLS, por lo que solo el reverse proxy en la misma máquina debe alcanzarla; vincularla a todas las interfaces expondría una página de inicio de sesión en texto plano directamente a internet. MongoDB no se publica en el host; solo es accesible a través de la red interna de Compose bajo el nombre mongodb, que es exactamente el hostname que usa el MONGO_URL. MONGO_URL incluye ?replicaSet=rs0; si omite esto, el driver tratará al servidor como standalone aunque sea un replica set, y los change streams seguirán fallando. MONGO_OPLOG_URL apunta a la base de datos local donde reside el oplog; las versiones modernas de Rocket.Chat prefieren los change streams, pero configurarlo no causa daños y mantiene la compatibilidad con código antiguo. El depends_on usa condition: service_healthy, por lo que Compose espera hasta que MongoDB responda a un ping antes de iniciar Rocket.Chat; esa es la función del healthcheck.
Fije etiquetas de versión reales en ambas imágenes —mongo:8.0 y una versión explícita de Rocket.Chat como 8.5.1 aquí— y nunca use :latest, lo que convierte una docker pull no supervisada en una actualización accidental e imposible de migrar. Verifique la versión estable actual de Rocket.Chat y las versiones de MongoDB que soporta antes de fijar las etiquetas. Rocket.Chat publica un documento de información legible por máquina por cada versión: curl -s https://releases.rocket.chat/8.5.1/info | jq '{compatibleMongoVersions, lts}' devuelve compatibleMongoVersions: ["8.0"] para 8.5.1, por lo que mongo:8.0 es el único motor soportado, además de un flag lts que indica si esa versión es una build de soporte a largo plazo (LTS) para servidores que no desea supervisar manualmente.
Inicializar el replica set
Levante el stack:
sudo docker compose up -dRocket.Chat fallará inmediatamente y Docker reiniciará el contenedor continuamente; esto es normal porque el replica set aún no existe. Créelo manualmente:
sudo docker compose exec mongodb mongosh --eval 'rs.initiate({_id: "rs0", members: [{_id: 0, host: "mongodb:27017"}]})'El resultado correcto es { ok: 1 }. En pocos segundos, el nodo único se elegirá como primary; confírmelo con:
sudo docker compose exec mongodb mongosh --quiet --eval 'rs.status().members[0].stateStr'Debe ver PRIMARY. El detalle más importante de esta página es el argumento host: "mongodb:27017". Si ejecuta un rs.initiate() sin una lista de miembros, MongoDB anunciará el replica set usando el hostname interno del contenedor (un hash aleatorio como a1b2c3d4e5f6). Rocket.Chat, al conectarse desde su propio contenedor, no puede resolver ese nombre; el driver de MongoDB fallará la resolución DNS y entrará en un bucle infinito registrando MongoServerSelectionError: getaddrinfo ENOTFOUND a1b2c3d4e5f6. Inicie siempre usando el nombre de servicio explícito que coincida con su MONGO_URL.
Primer arranque: monitoree el inicio
Una vez que el set es primario, el siguiente reinicio de Rocket.Chat se conectará correctamente e iniciará las migraciones de primera ejecución. Siga los logs:
sudo docker compose logs -f rocketchatLa línea que debe esperar es el banner de inicio:
+--------------------------------------------+
SERVER RUNNING
Rocket.Chat Version: 8.5.1
NodeJS Version: 22.22.3 - x64
+--------------------------------------------+El primer arranque es lento; la aplicación ejecuta migraciones de la base de datos y construye índices, así que espere uno o dos minutos antes de preocuparse. Si el log repite MongoServerSelectionError: Server selection timed out after 30000 ms con una descripción de topología de tipo ReplicaSetNoPrimary, el replica set no se ha iniciado; si repite getaddrinfo ENOTFOUND con un hash aleatorio, se inició con el host incorrecto. En cualquier caso, vuelva al paso anterior. Una vez que vea SERVER RUNNING, Rocket.Chat está escuchando en 127.0.0.1:3000 y es momento de configurar un hostname real y TLS delante de él.
Configúrelo detrás de TLS
Nunca exponga Rocket.Chat mediante HTTP simple. Si inicia sesión por http:// una sola vez, entregará su contraseña de administrador a cualquier persona en la ruta. Termine la conexión TLS en un proxy inverso en el mismo servidor y reenvíe el tráfico a 127.0.0.1:3000. Hay dos puntos importantes: el proxy debe reenviar los encabezados de actualización de WebSocket, ya que Rocket.Chat funciona en tiempo real y fallará sin ellos, y el ROOT_URL del contenedor debe coincidir exactamente con la dirección HTTPS pública que escriben los usuarios.
Comience con un bloque de servidor nginx en HTTP simple que actúe como proxy de la aplicación y reenvíe los encabezados de actualización. Guárdelo como /etc/nginx/sites-available/rocketchat, cree un enlace simbólico en sites-enabled y recargue:
server {
listen 80;
server_name chat.example.com;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Manténgalo en el puerto 80 por ahora; un bloque con listen 443 ssl; y sin certificado no pasará siquiera sudo nginx -t. Recargue nginx (sudo nginx -t && sudo systemctl reload nginx) y luego solicite el certificado. La opción más sencilla en Ubuntu es Certificados TLS de Let's Encrypt con Certbot y nginx: certbot --nginx reescribe el bloque anterior, añade listen 443 ssl;, las líneas ssl_certificate y una redirección automática de 80 a 443, además de programar la renovación automáticamente. Si ya ejecuta varios contenedores detrás de un único proxy, Traefik con TLS automático para múltiples apps Docker es la opción más ordenada: añada etiquetas de router y service al servicio rocketchat y Traefik gestionará las solicitudes y renovará el certificado sin necesidad de un bloque nginx. En cualquier caso, establezca ROOT_URL como https://chat.example.com en compose.yml y ejecute de nuevo sudo docker compose up -d para que el contenedor aplique el cambio. Si desea que el servidor sea accesible solo desde su propia red en lugar de internet, utilice una VPN WireGuard auto-alojada en la VPS y vincule el proxy a la dirección del túnel.
El asistente de configuración inicial
Acceda a https://chat.example.com y Rocket.Chat le guiará a través de un asistente breve. Primero, la cuenta de administrador: nombre real, nombre de usuario, correo electrónico y una contraseña segura; esta es la única cuenta existente, no la pierda. Después, la información de la organización y del servidor: nombre, industria, tamaño, nombre del sitio e idioma predeterminado; es información estética, complétela para continuar. Luego, la opción determinante: registrar este espacio de trabajo en Rocket.Chat Cloud, o mantenerlo de forma independiente (standalone).
El registro permite las notificaciones push móviles mediante la pasarela de Rocket.Chat y el marketplace de complementos, a cambio de mantener una relación de plano de control con la nube de Rocket.Chat. La opción independiente mantiene el servidor totalmente privado y sin dependencias, pero las notificaciones push de iOS y Android dejarán de funcionar, ya que Apple y Google no permiten que una aplicación propia gestione los certificados push; las aplicaciones oficiales utilizan la pasarela de la nube. Elija la opción independiente si la privacidad es la prioridad y sus usuarios utilizan la aplicación web; elija el registro si las notificaciones push móviles son imprescindibles. Puede cambiar esta configuración más tarde en la sección Admin.
Restrinja el acceso antes de invitar a usuarios
Rocket.Chat incluye registro abierto por defecto. El Registration Form está configurado en Public, lo que permite que cualquier persona con la URL cree una cuenta. Esto es un riesgo de seguridad en hostnames públicos. Vaya a Admin → Settings → Accounts → Registration y cambie Registration Form a Disabled para crear cuentas manualmente o mediante enlaces de invitación, o a Secret URL. En esa misma sección, desactive Allow Anonymous Read y Allow Anonymous Write a menos que necesite un canal público de solo lectura.
Configure también el destino de las cargas de archivos. El almacenamiento predeterminado de File Upload es GridFS, que guarda cada imagen y adjunto dentro de MongoDB. Esto es sencillo, pero implica que su base de datos —y cada mongodump que realice— crecerá indefinidamente conforme los usuarios peguen capturas de pantalla. En Admin → Settings → File Upload puede cambiar el almacenamiento al filesystem local o a un bucket compatible con S3, y establecer un tamaño máximo de archivo razonable. Para equipos pequeños, GridFS es adecuado; solo tenga en cuenta que sus backups serán cada vez más pesados.
Backups con mongodump
Todos los datos se encuentran en el volumen mongodb_data. No copie el volumen mientras la base de datos esté en ejecución; realice un volcado consistente con mongodump, redirigido a un archivo en el host:
sudo docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > rocketchat-$(date +%F).archive.gzEse archivo gzipped único contiene todo el espacio de trabajo: usuarios, canales, mensajes, configuraciones y, si dejó los archivos subidos en GridFS, también los archivos. Si movió las subidas al sistema de archivos o a S3, realice una copia de seguridad de ese almacenamiento por separado. Para restaurar en una instalación nueva, inicialice primero el replica set y luego ejecute:
sudo docker compose exec -T mongodb mongorestore --archive --gzip --drop < rocketchat-2026-07-15.archive.gzCopie el archivo fuera del servidor —almacenamiento de objetos, otro servidor o cualquier lugar donde el fallo del VPS no afecte a la copia de seguridad— y ejecute el volcado mediante cron cada noche. Una copia de seguridad que nunca se ha restaurado es una expectativa, no un respaldo; practique la restauración en un VPS de prueba para confirmar que funciona antes de que sea necesario.
Actualizaciones: fije las etiquetas, lea las notas y respete la matriz de Mongo
Dos reglas garantizan que las actualizaciones sean sencillas. Primero, actualice Rocket.Chat una versión mayor a la vez. El sistema ejecuta migraciones de esquema al arrancar y no permite saltos entre versiones mayores; si intenta pasar de 6.x directamente a 8.x, el proceso se detendrá con un error de migración en lugar de corromper sus datos. Cambie la etiqueta de la imagen a la última versión de la siguiente versión mayor, lea las notas de esa versión para identificar cambios disruptivos, ejecute docker compose up -d y verifique que los logs indiquen que la migración ha finalizado antes de continuar. Segundo, respete la matriz de soporte de MongoDB. Cada versión de Rocket.Chat es compatible con un conjunto específico de versiones de MongoDB, y curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions indica cuáles son. Cuando actualice MongoDB —por ejemplo, de 7.0 a 8.0—, hágalo una versión mayor a la vez y configure la versión de compatibilidad de funciones (feature-compatibility version) tras cada salto. En MongoDB 8.0, ese comando requiere un confirm: true explícito, de lo contrario fallará con un mensaje indicando que debe ejecutarlo nuevamente con el flag de confirmación:
sudo docker compose exec mongodb mongosh --eval 'db.adminCommand({setFeatureCompatibilityVersion: "8.0", confirm: true})'Realice un mongodump antes de cada actualización de cualquiera de los dos componentes. Esa es su única medida de seguridad.
Modos de fallo y sus cadenas exactas
Rocket.Chat entra en bucles de reinicio justo después de docker compose up, y docker compose logs rocketchat se llena de un MongoServerSelectionError. MongoDB está en ejecución pero el driver no puede seleccionar un primary; la cadena exacta indica el error cometido. Server selection timed out after 30000 ms con un tipo de topología ReplicaSetNoPrimary significa que no se ejecutó rs.initiate() — el set no tiene configuración aún. getaddrinfo ENOTFOUND seguido de un hash aleatorio significa que se inició sin el host: "mongodb:27017" explícito, por lo que MongoDB anunció un hostname de contenedor irresoluble. Diagnostique con sudo docker compose exec mongodb mongosh --eval 'rs.status()': si da error MongoServerError: no replset config has been received, inicie el set; si muestra un miembro cuyo name es un hash aleatorio, reinicie usando el nombre del servicio.
La interfaz web carga pero el login se queda cargando indefinidamente. Abra la consola del navegador y verá WebSocket connection to 'wss://chat.example.com/websocket' failed. Esto suele ser un desajuste de ROOT_URL o un proxy que no reenvía los headers de upgrade. Confirme que ROOT_URL sea la dirección pública exacta incluyendo https://, y que su bloque nginx location configure Upgrade y Connection "upgrade" con proxy_http_version 1.1. Cambie cualquiera de ellos y ejecute docker compose up -d de nuevo.
Un contenedor se detiene continuamente y docker compose ps muestra que Restarting. docker compose logs se corta a mitad de línea y sudo dmesg | tail muestra Out of memory: Killed process 12345 (mongod) del oom-killer; el código de salida es 137. El servidor no tiene RAM suficiente. La solución definitiva es un VPS más grande — mínimo 4 GB. Como medida temporal, añada swap y limite el cache de MongoDB con --wiredTigerCacheSizeGB 1 en su command, pero el swap solo retrasa el próximo OOM bajo carga real:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfiledocker compose up falla con Error response from daemon: driver failed programming external connectivity ... bind: address already in use. Algo ya está usando el puerto 3000 — a menudo un contenedor de Rocket.Chat previo que no se detuvo correctamente, o una aplicación distinta. Identifíquelo con sudo ss -ltnp | grep :3000, detenga ese proceso o contenedor, o cambie el lado del host en el mapeo a 127.0.0.1:3001:3000 y actualice el proxy_pass de su proxy para que coincida.
FAQ
¿Realmente necesita Rocket.Chat un replica set de MongoDB?
Sí, incluso para un servidor único con un solo nodo de base de datos. Rocket.Chat entrega mensajes en tiempo real usando MongoDB change streams. Los change streams son una función exclusiva de los replica sets; un mongod independiente no puede abrirlos. No necesita varias máquinas; puede ejecutar un contenedor de MongoDB iniciado con --replSet rs0 e inicializar un set de un solo miembro con rs.initiate(). Si omite este paso, el driver no encontrará un primario, por lo que Rocket.Chat entrará en un bucle de reinicio con MongoServerSelectionError: Server selection timed out y no terminará de arrancar.
¿Cuánta RAM necesita Rocket.Chat self-hosted?
Planifique 4 GB como mínimo práctico y 8 GB para un equipo con mucha actividad. El proceso Node de Rocket.Chat utiliza entre 1 y 1.5 GB. MongoDB reserva aproximadamente la mitad de la RAM restante para su cache WiredTiger. En un servidor de 2 GB, ambos procesos colisionan y el out-of-memory killer terminará mongod bajo cualquier carga real, mostrando Killed en los logs y el código de salida 137. 2 GB solo es suficiente para evaluar el software con un par de usuarios de prueba.
¿Cómo pongo Rocket.Chat detrás de HTTPS?
Ejecute un reverse proxy en el mismo VPS que termine la conexión TLS y reenvíe el tráfico a 127.0.0.1:3000, y configure el ROOT_URL del contenedor con su dirección https:// pública. El proxy debe reenviar los headers de upgrade de WebSocket o el inicio de sesión se bloqueará. Certbot con nginx es la configuración más sencilla para una sola aplicación; Traefik es más limpio si ejecuta varios contenedores detrás de un proxy y desea gestión automática de certificados.
¿Cómo hago una copia de seguridad de Rocket.Chat self-hosted?
Realice un dump consistente de la base de datos con mongodump en lugar de copiar el volumen: docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > backup.archive.gz. Ese archivo contiene usuarios, canales, mensajes y configuraciones, además de los archivos subidos si dejó el almacenamiento en GridFS. Copie el archivo fuera del servidor, automatice el proceso cada noche con cron y practique una mongorestore en un servidor de prueba para confirmar que la restauración funciona.
¿Cómo actualizo Rocket.Chat sin romper MongoDB?
Actualice Rocket.Chat una versión mayor a la vez; el software ejecuta migraciones al arrancar y no permite saltar versiones mayores. Lea las notas de cada versión antes de cambiar la etiqueta de la imagen (image tag). Verifique qué versiones de MongoDB soporta la versión de destino con curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions. Al mover MongoDB, avance una versión mayor a la vez y configure setFeatureCompatibilityVersion con confirm: true tras cada salto. Siempre realice una mongodump primero.