SSD Nodes Learn
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-07-24

Installare Rocket.Chat con Docker Compose

Guida completa per installare Rocket.Chat su VPS con Docker Compose. Configura MongoDB replica set, TLS e backup evitando errori di connessione al database.

Cosa stai costruendo

Una chat privata per il tuo team di tua proprietà: Rocket.Chat eseguito sul tuo VPS tramite Docker Compose, protetto da TLS, con ogni messaggio salvato in un database MongoDB che puoi backuppare e spostare. Rocket.Chat è l'alternativa open-source matura a Slack e Teams: canali, messaggi diretti, thread, condivisione di file e audio/video, tutto su hardware che affitti e controlli. L'applicazione è un singolo container che si avvia in pochi minuti. Eventuali problemi di funzionamento dipendono dal database adiacente; pertanto, gran parte di questa guida riguarda MongoDB, e in particolare un requisito che sorprende tutti la prima volta: Rocket.Chat non funziona con un'istanza MongoDB standalone. Richiede un replica set, anche se tale "set" è composto da un singolo nodo.

Prerequisiti e il calcolo della RAM che nessuno ti dice

Dimensiona il server in modo onesto. Il limite minimo realistico per un piccolo team è 2 vCPU e 4 GB di RAM. Il processo Node.js di Rocket.Chat richiede circa 1 - 1.5 GB da solo, e la cache WiredTiger di MongoDB occupa circa la metà della RAM rimanente per impostazione predefinita. Su un VPS da 2 GB, entrambi i processi avviano correttamente, ma entrano in conflitto non appena arriva traffico reale: la cache di MongoDB cresce, l'heap di Node cresce, il kernel esaurisce le pagine e l'out-of-memory killer termina il processo più grande — solitamente mongod. Il container restituisce l'errore Killed, Docker lo riavvia e si ottiene un server di chat che si interrompe ogni pochi minuti sotto un carico che dovrebbe gestire senza problemi. 2 GB sono sufficienti per test iniziali con due utenti; non sono adatti a un server per team. Inizia con 4 GB e passa a 8 GB se prevedi decine di utenti simultanei, videochiamate o una cronologia di upload in crescita.

Prima di iniziare, sono necessari tre elementi. Un nome di dominio con un record A che punti all'IP pubblico del VPS — le funzionalità real-time di Rocket.Chat e i client mobile richiedono un hostname stabile, non un semplice IP. Le porte 80 e 443 devono essere aperte sia sul firewall del server che sul firewall di rete del provider, che è un controllo separato nella maggior parte dei pannelli. E un VPS KVM con Ubuntu 24.04 appena installato con privilegi root o sudo. Se stai ancora decidendo se un server di chat sia il servizio ideale da gestire per primo, la guida a cosa vale la pena auto-ospitare nel 2026 illustra i pro e i contro.

Install the Docker engine and the Compose plugin

Usa il repository apt ufficiale di Docker. Non usare il pacchetto docker.io incluso in Ubuntu e non il vecchio binario Python docker-compose. Il moderno Compose è un plugin di Docker che si invoca come docker compose — con uno spazio e non un trattino. La vecchia versione docker-compose v1 è obsoleta e gestisce in modo errato la sintassi di healthcheck e dependency riportata sotto.

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-plugin

Verifica la presenza di entrambi i componenti:

sudo docker version
sudo docker compose version

Il comando docker compose version che restituisce un output simile a Docker Compose version v2.x è il controllo necessario. Se ricevi l'errore docker: 'compose' is not a docker command, il plugin non è stato installato. Questo causerà errori complessi in seguito; risolvi il problema in questa fase.

Il file compose: MongoDB come replica set a nodo singolo

Questa è la sezione in cui si commettono errori, quindi leggerla con attenzione. Rocket.Chat utilizza i change streams di MongoDB per inviare i nuovi messaggi ai client connessi in tempo reale; i change streams sono disponibili solo su un replica set. Se si punta Rocket.Chat verso un semplice mongod standalone, la connessione avverrà, ma l'apertura del change stream fallirà, causando un ciclo continuo di riavvii. La soluzione non è complessa: si esegue un normale container MongoDB, ma lo si avvia con --replSet e si inizializza successivamente un set a un solo membro.

Creare una directory di lavoro e 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:

Alcune scelte sono deliberate. La porta di Rocket.Chat è pubblicata su 127.0.0.1:3000 e non su 0.0.0.0: l'app non utilizza TLS, quindi solo il reverse proxy sulla stessa macchina deve raggiungerla; il binding su tutte le interfacce esporrebbe una pagina di login in chiaro su internet. MongoDB non è esposto sull'host; è raggiungibile solo tramite la rete interna di Compose con il nome mongodb, che è l'hostname utilizzato da MONGO_URL. MONGO_URL include ?replicaSet=rs0: senza questo parametro il driver tratterà il server come standalone anche se è un replica set, e i change streams falliranno ancora. MONGO_OPLOG_URL punta al database local dove risiede l'oplog; le versioni recenti di Rocket.Chat preferiscono i change streams, ma impostarlo è innocuo e garantisce la compatibilità con il codice legacy. Il depends_on utilizza condition: service_healthy, quindi Compose attende che MongoDB risponda a un ping prima di avviare Rocket.Chat; questa è la funzione del healthcheck.

Utilizzare tag di versione specifici per entrambe le immagini — mongo:8.0 e una release esplicita di Rocket.Chat come 8.5.1 in questo esempio — e non usare mai :latest, che trasforma un aggiornamento docker pull non supervisionato in un upgrade accidentale e non migratabile. Verificare la versione stabile attuale di Rocket.Chat e le versioni di MongoDB supportate prima di fissare i tag. Rocket.Chat pubblica un documento informativo leggibile dalle macchine per ogni release: curl -s https://releases.rocket.chat/8.5.1/info | jq '{compatibleMongoVersions, lts}' restituisce compatibleMongoVersions: ["8.0"] per la versione 8.5.1, quindi mongo:8.0 è l'unico motore supportato, insieme a un flag lts che indica se la release è una versione long-term-support adatta per server che non richiedono manutenzione costante.

Inizializza il replica set

Avvia lo stack:

sudo docker compose up -d

Rocket.Chat andrà in crash immediatamente e Docker proverà a riavviarlo continuamente — è un comportamento previsto, poiché il replica set non è ancora stato creato. Crealo manualmente:

sudo docker compose exec mongodb mongosh --eval 'rs.initiate({_id: "rs0", members: [{_id: 0, host: "mongodb:27017"}]})'

Il risultato corretto è { ok: 1 }. Entro pochi secondi il nodo singolo si elegerà primary; conferma con:

sudo docker compose exec mongodb mongosh --quiet --eval 'rs.status().members[0].stateStr'

Il risultato atteso è PRIMARY. Il dettaglio più importante di questa pagina è l'argomento host: "mongodb:27017". Se esegui un comando rs.initiate() senza l'elenco dei membri, MongoDB annuncerà il replica set utilizzando l'hostname interno del container — un hash casuale come a1b2c3d4e5f6. Rocket.Chat, connettendosi dal proprio container, non può risolvere tale nome; il driver di MongoDB fallirà la risoluzione DNS e entrerà in un loop infinito loggando MongoServerSelectionError: getaddrinfo ENOTFOUND a1b2c3d4e5f6. Inizia sempre con il nome del servizio esplicito che corrisponde al tuo MONGO_URL.

Primo avvio: monitorare l'avvio

Una volta impostato il set come primary, il successivo riavvio di Rocket.Chat avverrà correttamente e inizierà le migrazioni della prima esecuzione. Monitorare i log:

sudo docker compose logs -f rocketchat

La riga da attendere è il banner di startup:

+--------------------------------------------+
        SERVER RUNNING
   Rocket.Chat Version: 8.5.1
        NodeJS Version: 22.22.3 - x64
+--------------------------------------------+

Il primo avvio è lento: l'app esegue le migrazioni del database e crea gli indici; attendere uno o due minuti prima di intervenire. Se il log ripete MongoServerSelectionError: Server selection timed out after 30000 ms con una descrizione della topologia di tipo ReplicaSetNoPrimary, il replica set non è stato avviato; se ripete getaddrinfo ENOTFOUND su un hash casuale, è stato avviato con l'host errato. In entrambi i casi, tornare al passaggio precedente. Quando compare SERVER RUNNING, Rocket.Chat è in ascolto su 127.0.0.1:3000 ed è necessario configurare un hostname reale e il protocollo TLS.

Mettilo dietro TLS

Non esporre mai Rocket.Chat su HTTP semplice. Effettuare il login su http:// una sola volta per consegnare la password di amministrazione a chiunque sia presente sul percorso di rete. Termina la connessione TLS in un reverse proxy sulla stessa macchina e inoltra il traffico a 127.0.0.1:3000. Due aspetti sono fondamentali: il proxy deve inoltrare gli header di WebSocket upgrade, poiché Rocket.Chat opera in tempo reale e smette di funzionare senza di essi, e il ROOT_URL del container deve corrispondere esattamente all'indirizzo HTTPS pubblico digitato dagli utenti.

Inizia con un server block nginx in HTTP semplice che faccia da proxy verso l'app e inoltri gli header di upgrade. Salva il file come /etc/nginx/sites-available/rocketchat, crea un symlink in sites-enabled e ricarica:

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;
    }
}

Mantieni la porta 80 per ora — un blocco con listen 443 ssl; e senza certificato non supererà nemmeno sudo nginx -t. Ricarica nginx (sudo nginx -t && sudo systemctl reload nginx), quindi richiedi il certificato. La soluzione più semplice su Ubuntu è Certificati TLS Let's Encrypt con Certbot e nginx: certbot --nginx riscrive il blocco precedente, aggiungendo listen 443 ssl;, le righe ssl_certificate e un reindirizzamento automatico da 80 a 443, e pianifica il rinnovo. Se utilizzi già diversi container dietro un unico proxy, Traefik con TLS automatico per molte app Docker è l'opzione più ordinata — aggiungi le label router e service al servizio rocketchat e Traefik gestirà la richiesta e il rinnovo del certificato senza l'uso di nginx. In entrambi i casi, imposta ROOT_URL su https://chat.example.com in compose.yml ed esegui nuovamente sudo docker compose up -d affinché il container applichi la modifica. Se desideri che il server sia raggiungibile solo dalla tua rete interna invece che da internet, utilizza un VPN WireGuard self-hosted sul VPS e vincola il proxy all'indirizzo del tunnel.

La procedura guidata di configurazione iniziale

Accedi a https://chat.example.com e Rocket.Chat avvierà una breve procedura guidata. Per prima cosa, l'account admin — nome reale, username, email e una password sicura; questo è l'unico account esistente, quindi non perderlo. Successivamente, le informazioni su organizzazione e server — nome, settore, dimensione, nome del sito e lingua predefinita; sono dati estetici, compiliali e procedi. Infine, la scelta fondamentale: registra questo workspace su Rocket.Chat Cloud, oppure mantienilo standalone.

La registrazione abilita le notifiche push su dispositivi mobili tramite il gateway di Rocket.Chat e il marketplace degli add-on, accettando un collegamento del control-plane con il cloud di Rocket.Chat. La modalità standalone mantiene il server completamente privato e senza dipendenze, ma le notifiche push su iOS e Android smetteranno di funzionare; questo accade perché Apple e Google non permettono a un'app personalizzata di gestire i certificati push — le app ufficiali utilizzano il gateway cloud. Scegli standalone se la priorità è la privacy e gli utenti utilizzano l'app web; scegli la registrazione se le notifiche push su mobile sono indispensabili. È possibile cambiare scelta in seguito nella sezione Admin.

Configura la sicurezza prima di invitare utenti

Rocket.Chat include la registrazione aperta — di default il Registration Form è impostato su Public, quindi chiunque trovi l'URL può creare un account. Su un hostname pubblico, questo rappresenta una vulnerabilità. Vai su Admin → Settings → Accounts → Registration e imposta Registration Form su Disabled, per creare gli account manualmente o tramite link di invito, oppure su Secret URL. In questa sezione, disabilita Allow Anonymous Read e Allow Anonymous Write, a meno che non sia necessario un canale pubblico in sola lettura.

Configura anche la destinazione degli upload. Lo storage predefinito per File Upload è GridFS, che salva ogni immagine e allegato all'interno di MongoDB. Questa configurazione è semplice, ma comporta una crescita illimitata del database — e di ogni mongodump effettuato — a causa degli screenshot inviati dagli utenti. Sotto Admin → Settings → File Upload è possibile cambiare lo storage verso il filesystem locale o un bucket compatibile con S3, e impostare un limite massimo per la dimensione dei file. Per team di piccole dimensioni GridFS è adeguato; considera però che i backup diventeranno progressivamente più pesanti.

Backup con mongodump

Tutti i dati risiedono nel volume mongodb_data. Non copiare il volume mentre il database è in esecuzione: utilizza mongodump per creare un dump consistente, inviando l'output a un file sull'host:

sudo docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > rocketchat-$(date +%F).archive.gz

L'archivio gzipped contiene l'intero workspace: utenti, canali, messaggi, impostazioni e, se gli upload sono su GridFS, anche i file. Se hai spostato gli upload sul filesystem o su S3, esegui un backup separato di quel repository. Per il ripristino su un nuovo stack, inizializza prima il replica set, quindi esegui:

sudo docker compose exec -T mongodb mongorestore --archive --gzip --drop < rocketchat-2026-07-15.archive.gz

Copia l'archivio fuori dalla macchina — su object storage, un altro server o comunque in una posizione esterna al VPS — ed esegui il dump tramite cron ogni notte. Un backup che non è mai stato ripristinato è solo una speranza, non un backup; prova il ripristino su un VPS di prova per verificare il corretto funzionamento prima di una necessità reale.

Upgrades: pin tags, read the notes, respect the Mongo matrix

Due regole rendono gli upgrade semplici. Primo, aggiorna Rocket.Chat una versione major alla volta. Il sistema esegue migrazioni dello schema all'avvio e non permette salti tra versioni major; tentare di passare direttamente dalla 6.x alla 8.x causa un errore di migrazione invece di corrompere i dati. Aggiorna il tag dell'immagine all'ultima release della versione major successiva, leggi le note di rilascio per eventuali breaking changes, esegui docker compose up -d e verifica che i log confermino la fine della migrazione prima di procedere. Secondo, rispetta la matrice di supporto di MongoDB. Ogni release di Rocket.Chat supporta un set specifico di versioni di MongoDB; curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions indica quali sono supportate. Quando aggiorni MongoDB — ad esempio dalla 7.0 alla 8.0 — procedi una versione major alla volta e imposta la feature-compatibility version dopo ogni passaggio. Su MongoDB 8.0, quel comando richiede un confirm: true esplicito, altrimenti il sistema restituisce un errore chiedendo di rieseguire il comando con il flag di conferma:

sudo docker compose exec mongodb mongosh --eval 'db.adminCommand({setFeatureCompatibilityVersion: "8.0", confirm: true})'

Esegui un mongodump prima di ogni upgrade di uno dei due componenti. Questa è l'unica misura di sicurezza necessaria.

Modalità di errore, con le stringhe esatte

Rocket.Chat entra in un ciclo di riavvio subito dopo docker compose up, e docker compose logs rocketchat si riempie di MongoServerSelectionError. MongoDB è in esecuzione ma il driver non può selezionare un primary; la stringa esatta indica l'errore commesso. Server selection timed out after 30000 ms con un topology type di ReplicaSetNoPrimary indica che non è stato eseguito rs.initiate() — il set non ha ancora una configurazione. getaddrinfo ENOTFOUND seguito da un hash casuale indica che l'inizializzazione è avvenuta senza il comando esplicito host: "mongodb:27017", quindi MongoDB ha pubblicizzato un hostname del container non risolvibile. Diagnostica con sudo docker compose exec mongodb mongosh --eval 'rs.status()': se restituisce l'errore MongoServerError: no replset config has been received, inizializza il set; se mostra un membro il cui name è un hash casuale, riavvia l'inizializzazione usando il nome del servizio.

L'interfaccia web si carica ma il login rimane in caricamento infinito. Apri la console del browser per visualizzare WebSocket connection to 'wss://chat.example.com/websocket' failed. Si tratta quasi sempre di un mismatch di ROOT_URL o di un proxy che non inoltra gli upgrade headers. Verifica che ROOT_URL corrisponda all'indirizzo pubblico esatto, incluso https://, e che il blocco nginx location imposti Upgrade e Connection "upgrade" con proxy_http_version 1.1. Apporta le modifiche e riavvia docker compose up -d.

Un container si arresta continuamente e docker compose ps mostra Restarting. docker compose logs si interrompe a metà riga e sudo dmesg | tail mostra Out of memory: Killed process 12345 (mongod) da parte dell'oom-killer; il codice di uscita è 137. Il sistema ha esaurito la RAM. La soluzione definitiva è un VPS più grande — minimo 4 GB. Come soluzione temporanea, aggiungi lo swap e limita la cache di MongoDB con --wiredTigerCacheSizeGB 1 nel suo command, ma lo swap ritarda solo il prossimo OOM sotto carico reale:

sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

docker compose up fallisce con Error response from daemon: driver failed programming external connectivity ... bind: address already in use. Una risorsa occupa già la porta 3000 — spesso un precedente container di Rocket.Chat non terminato correttamente, o un'altra applicazione. Individua il processo con sudo ss -ltnp | grep :3000, arresta il processo o il container, oppure cambia la parte host della mappatura in 127.0.0.1:3001:3000 e aggiorna il proxy_pass del proxy per corrispondere.

FAQ

Rocket.Chat richiede davvero un replica set di MongoDB?

Sì, anche per un singolo server con un solo nodo database. Rocket.Chat invia i messaggi in tempo reale utilizzando i MongoDB change streams. I change streams sono una funzionalità disponibile solo per i replica set; un mongod standalone non può aprirne uno. Non sono necessarie più macchine; è sufficiente eseguire un container MongoDB avviato con --replSet rs0 e inizializzare un set a un solo membro con rs.initiate(). Se si salta questo passaggio, il driver non trova un primary; di conseguenza Rocket.Chat entra in un ciclo di riavvio con MongoServerSelectionError: Server selection timed out e non completa l'avvio.

Quanta RAM richiede Rocket.Chat in self-hosting?

Prevedere 4 GB come minimo pratico e 8 GB per un team numeroso. Il processo Node di Rocket.Chat utilizza circa 1-1.5 GB. MongoDB utilizza circa la metà della RAM rimanente per la sua cache WiredTiger. Su una macchina da 2 GB, le due istanze entrano in conflitto e l'out-of-memory killer termina mongod sotto qualsiasi carico reale, mostrando Killed nei log e codice di uscita 137. 2 GB sono sufficienti solo per valutare il software con pochi utenti di test.

Come posso mettere Rocket.Chat dietro HTTPS?

Eseguire un reverse proxy sullo stesso VPS che termini il TLS e inoltri a 127.0.0.1:3000, e impostare la variabile ROOT_URL del container al proprio indirizzo https:// pubblico. Il proxy deve inoltrare gli header di WebSocket upgrade, altrimenti il login si bloccherà. Certbot con nginx è la configurazione più semplice per una singola applicazione; Traefik è più efficiente se si eseguono più container dietro un unico proxy e si desidera la gestione automatica dei certificati.

Come posso eseguire il backup di un Rocket.Chat in self-hosting?

Eseguire un dump consistente del database con mongodump invece di copiare il volume: docker compose exec -T mongodb mongodump --db rocketchat --archive --gzip > backup.archive.gz. L'archivio contiene utenti, canali, messaggi e impostazioni, oltre ai file caricati se lo storage è su GridFS. Copiare l'archivio fuori dal server, automatizzare il processo ogni notte con cron e testare un mongorestore su una macchina di prova per verificare che il ripristino funzioni correttamente.

Come posso aggiornare Rocket.Chat senza corrompere MongoDB?

Aggiornare Rocket.Chat una versione major alla volta — il software esegue le migrazioni all'avvio e non permette di saltare le versioni major — e leggere le note di ogni release prima di aggiornare il tag dell'immagine. Verificare quali versioni di MongoDB sono supportate dalla versione di destinazione con curl -s https://releases.rocket.chat/<version>/info | jq .compatibleMongoVersions. Quando si aggiorna MongoDB, procedere una versione major alla volta e impostare setFeatureCompatibilityVersion con confirm: true dopo ogni passaggio. Effettuare sempre un mongodump prima di procedere.