SSD Nodes Learn 🎉 VPS da $5.50/mese
Guide Matt ConnorDi Matt Connor · Aggiornato 2026-08-13

Come installare Discourse su un VPS con Docker

Guida all’installer Docker ufficiale di Discourse: RAM e swap, dominio reale, SMTP, file app.yml, comando rebuild, TLS e gestione dei reverse proxy.

Installare Discourse su un VPS: un container, un file di configurazione

Per installare Discourse su un VPS, esegui l'installer del progetto, rispondi a una breve procedura guidata e attendi la build. Discourse viene distribuito come un unico container Docker che contiene l'applicazione Rails, PostgreSQL, Redis e nginx. Tutto ciò che modificherai in seguito si trova nel file /var/discourse/containers/app.yml e ogni modifica raggiunge il sito tramite una nuova build.

L'installazione ufficiale è discourse_docker: uno script shell launcher e un insieme di template YAML. Discourse non supporta un file Compose scritto autonomamente e il container non è progettato per essere suddiviso manualmente. Se sei abituato a eseguire servizi su un VPS con Docker Compose, troverai un'architettura diversa. Qui non esiste docker compose up -d e ./launcher rebuild app è il deploy.

Cosa richiede Discourse prima di iniziare

Quattro requisiti causano problemi e ciascuno può bloccare l'installazione prima di arrivare alla pagina di login.

  • Memoria. Un container esegue PostgreSQL, Redis, Sidekiq e un server web Ruby. La fase di build compila gli asset e richiede più memoria del sito in esecuzione.
  • Un nome di dominio reale. La configurazione di esempio fornita lo dichiara chiaramente: "Discourse non funziona con un semplice indirizzo IP."
  • Un percorso per la posta in uscita. L'attivazione degli account, il ripristino delle password, gli inviti degli amministratori e i messaggi di riepilogo vengono inviati tramite SMTP (simple mail transfer protocol).
  • Le porte 80 e 443 libere sull'host, a meno che Discourse non venga messo deliberatamente dietro un proxy già in uso.
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
  }
]

La documentazione ufficiale di installazione indica come requisito minimo 1 GB di RAM con swap e 10 GB di spazio su disco, e raccomanda 2 GB di RAM con 20 GB di spazio su disco. Interpreta la prima riga come il valore che consente all'installer di completare l'operazione, non come il valore necessario per gestire una community. La differenza è importante perché il picco di memoria si verifica durante la build, non in base al traffico.

Indirizzare il dominio al server prima dell'installazione

Create un record A per il nome host che userete, quindi verificatelo direttamente dal server.

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

Entrambi i comandi devono restituire lo stesso indirizzo. Devono corrispondere perché la procedura guidata di configurazione esegue un test di connessione sul nome host. Se il record punta ancora altrove, il test non riesce. Un record creato due minuti fa potrebbe inoltre essere ancora memorizzato nella cache. Attendete quindi la scadenza del vecchio TTL (time to live), invece di forzare la procedura guidata.

Decidete ora se il record sarà sottoposto a proxy tramite una CDN. Un record sottoposto a proxy nasconde l'indirizzo del server. La richiesta del certificato da parte del container fallisce quindi perché la challenge ACME (automatic certificate management environment) riceve risposta dal proxy invece che da Discourse. Per la prima installazione, mantenete il record senza proxy.

Esegui l'installer ufficiale

Un solo comando installa git, installa Docker tramite lo script di installazione fornito da Docker, clona discourse_docker in /var/discourse e avvia la procedura guidata di configurazione.

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

Se Docker è già installato sul server e vuoi visualizzare ogni passaggio, esegui manualmente le stesse operazioni.

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

Esegui il comando come root. Se viene avviato da un utente normale, discourse-setup si interrompe subito con This script must be run as root. Please sudo or log in as root first.. Se Docker non è installato sul server, si interrompe con Docker is not installed. Please install Docker first., perché la clonazione manuale non installa alcuna dipendenza.

Cosa chiede la procedura guidata e cosa scrive

Ad agosto 2026 discourse-setup è un wrapper sottile. Esegue discourse/setup-wizard:release come container, con la rete dell'host e il socket Docker montato, così la procedura guidata può analizzare la macchina che sta configurando. Chiede il nome host e gli indirizzi email degli amministratori, quindi i parametri SMTP. Scrive containers/app.yml, poi esegue nuovamente la build.

Prima di iniziare, è utile conoscere due comportamenti. Se la macchina ha poca memoria e non dispone di swap, la procedura guidata si interrompe e propone di crearla: il wrapper crea quindi un /swapfile da 2 GB, lo aggiunge a /etc/fstab, imposta vm.swappiness = 10 in /etc/sysctl.d/30-discourse-swap.conf e riavvia la procedura guidata. Al termine, la procedura guidata stampa Rebuilding app in 5 seconds (Ctrl+C to cancel)... ed esegue ./launcher rebuild app sull'host. Questa build richiede diversi minuti su un VPS di piccole dimensioni. La prima è la più lenta perché ogni asset viene compilato da zero.

./discourse-setup --help elenca i flag rilevanti quando si verifica un problema. --skip-rebuild scrive la configurazione senza eseguire la build, mentre --skip-connection-test salta i controlli DNS e delle porte. Usa --skip-connection-test solo quando sai già perché il test non riesce, ad esempio quando l'host si trova dietro un firewall di rete sotto il tuo controllo.

Leggere app.yml prima della prima ricompilazione

La procedura guidata scrive un file che ora devi gestire direttamente. Aprilo con sudo nano /var/discourse/containers/app.yml. Queste sono le impostazioni che determinano quasi tutto.

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 è l'indirizzo a cui il sito risponde e Discourse lo usa per generare i collegamenti. Un valore errato può quindi far caricare il sito una volta e poi reindirizzarti altrove. DISCOURSE_DEVELOPER_EMAILS è un elenco separato da virgole. Gli indirizzi indicati diventano automaticamente amministratori alla prima registrazione. Inserisci qui il tuo indirizzo e registrati con quello, perché è così che viene creato il primo account amministratore.

Il file contiene la password SMTP in testo in chiaro. Limita quindi l'accesso alla directory con sudo chmod 700 /var/discourse/containers. Il formato è YAML, quindi gli spazi sono significativi: una chiave non allineata correttamente interrompe la compilazione con un errore di analisi e il sito non viene avviato. Un problema specifico è documentato nel file di esempio. Un # all'interno di una password non racchiusa tra virgolette introduce un commento. Racchiudi quindi tra virgolette qualsiasi password che lo contiene.

L’email è il passaggio che blocca la maggior parte delle installazioni

Ad agosto 2026 la procedura guidata consente di ignorare la configurazione SMTP e usare invece gli accessi Discourse ID. Anche app.yml include un’opzione DISCOURSE_SKIP_EMAIL_SETUP corrispondente, descritta come esclusione della convalida della configurazione email. Ignorare questo passaggio è ragionevole per una prima valutazione del software. È una scelta inadatta per una community, perché senza email in uscita nessuno può attivare un account o reimpostare una password.

Il problema pratico è che la maggior parte dei provider VPS blocca la porta in uscita 25, quindi un semplice mail server installato sul server non recapita i messaggi. Usa un relay autenticato sulla porta 587 oppure sulla porta 465 con TLS implicito (Transport Layer Security). Per la porta 465 imposta DISCOURSE_SMTP_FORCE_TLS: true, come raccomandato dalla configurazione di esempio per questa porta. Verifica la raggiungibilità dal server prima di ricreare l’installazione.

nc -vz smtp.example.com 587

Un risultato corretto è una singola riga che termina con succeeded!. Se un comando resta in attesa e poi va in timeout, significa che la porta è bloccata lungo il percorso in uscita dal VPS. Nessuna impostazione di Discourse può risolvere il problema. Usa una porta consentita dal provider oppure chiedi al provider di aprirla.

Dopo che il sito è online, invia un messaggio di test dalla pagina Email nella sezione Admin, quindi leggi le schede Skipped e Bounced nella stessa pagina. In queste schede Discourse registra le email che ha rifiutato di inviare e quelle rifiutate dal relay. Le schede indicano anche il motivo, quindi il controllo è più rapido della lettura dei log.

TLS: lasciare che il container ottenga il proprio certificato

Se Discourse usa le porte 80 e 443, utilizza il meccanismo integrato per il rilascio dei certificati. Decommenta le due righe del template SSL mostrate sopra, quindi ricrea il container. Il template configura acme.sh, archivia i certificati nel volume condiviso in /shared/ssl, li rinnova secondo una pianificazione interna al container e configura Discourse per forzare HTTPS.

La porta 80 deve restare raggiungibile da Internet perché questa procedura funzioni: la challenge HTTP riceve la risposta su quella porta. Un firewall che consente soltanto la porta 443 permette di completare la build, ma il certificato non viene mai rilasciato. Verifica l'esito con ./launcher logs app subito dopo la ricreazione.

Nginx o Caddy davanti al servizio?

Se Discourse è l'unico servizio web sul VPS, non è necessario. Il container esegue già un nginx ottimizzato. Un secondo proxy aggiunge un passaggio, un altro certificato da rinnovare e una nuova possibile fonte di problemi con gli header.

Usalo se lo stesso VPS pubblica altri siti. Aggiungi templates/web.socketed.template.yml all'elenco dei template, commenta entrambe le righe expose e lascia commentati i due template SSL. Il container resterà in ascolto su un socket Unix in /var/discourse/shared/standalone/nginx.http.sock e non utilizzerà alcuna porta. In questo modo le porte 80 e 443 saranno disponibili per il proxy che gestisci direttamente.

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

I due punti finali dopo .sock fanno parte della sintassi del socket Unix di nginx. sudo nginx -t rifiuta la configurazione se mancano. Anche X-Forwarded-Proto è obbligatorio. Discourse genera URL assoluti. Senza quell'header, in una pagina HTTPS genera link http:// e i browser li bloccano come contenuto misto. Quando il container usa un socket, la gestione di TLS spetta a te. Emetti quindi il certificato sull'host con Certbot su Ubuntu 24.04 e nginx. Se non hai ancora scelto il proxy, il confronto tra nginx, Caddy e Traefik illustra i compromessi della scelta.

Ricostruzioni, aggiornamenti e comandi da usare realmente

cd /var/discourse
./launcher rebuild app

rebuild elimina il container in esecuzione, ne crea uno nuovo da app.yml e lo avvia. Il sito resta offline per tutta la durata della build. Considera quindi ogni modifica alla configurazione come un'interruzione programmata di alcuni minuti.

Le modifiche ai soli valori in env: non richiedono questo intervento. ./launcher destroy app && ./launcher start app ricrea il container dall'immagine già compilata e richiede pochi secondi. Qualsiasi modifica in templates: o hooks: cambia l'immagine stessa e richiede quindi una ricostruzione completa.

Gli aggiornamenti arrivano in due modi. Gli aggiornamenti di versione vengono applicati dall'interfaccia web in /admin/upgrade, tramite il plugin docker_manager che app.yml clona durante la build. Le modifiche all'immagine di base o ai template provengono da git.

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

Le ricostruzioni sono il punto in cui i server di piccole dimensioni possono fallire, perché la compilazione degli asset rappresenta il picco massimo di consumo di memoria dell'intero sistema. Se una build si interrompe a metà e dmesg mostra una riga come Out of memory: Killed process che indica un processo ruby, durante la build si è esaurita la memoria, anche se prima il sito funzionava correttamente. Aggiungi la swap ed esegui di nuovo la ricostruzione.

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

logs stampa l'output del container, enter apre una shell al suo interno e cleanup rimuove i container arrestati da più di 24 ore. Esegui cleanup periodicamente, perché ogni ricostruzione lascia un vecchio container e lo spazio su disco di un VPS di piccole dimensioni si esaurisce senza segnali evidenti.

Backup e file non incluso nel backup

Crea i backup dalla pagina Backups in Admin. L'archivio viene salvato sull'host in /var/discourse/shared/standalone/backups/default/. Lo stesso processo può essere eseguito anche da una shell.

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

discourse restore <filename> esegue l'operazione inversa e i ripristini vengono rifiutati finché non esegui discourse enable_restore. Questo controllo impedisce a un comando eseguito per errore di sovrascrivere un forum attivo.

Devi colmare personalmente due lacune. L'archivio contiene il database e include i file caricati solo quando è attiva l'impostazione del backup che include gli upload. Controlla quindi questa impostazione prima di fare affidamento sul backup. L'archivio non contiene mai app.yml. Per questo, dopo un ripristino su un VPS nuovo, devi configurare nuovamente il nome host e il blocco SMTP. Devi quindi copiare quel file anche all'esterno del server.

Inoltre, l'archivio si trova sullo stesso disco del sito che protegge. Questo non è un backup. Copialo periodicamente in un'altra posizione.

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

Quanto costa in RAM un forum molto attivo

Lo script di bootstrap imposta UNICORN_WORKERS e db_shared_buffers in base alla memoria e alla CPU rilevate; la configurazione di esempio limita i buffer condivisi a un quarto della memoria totale. Ogni worker Unicorn è un processo Ruby completo e Sidekiq esegue i job in background insieme agli altri processi. Di conseguenza, il consumo di memoria dipende dalle richieste simultanee, non dal numero di membri registrati. Un forum poco attivo con qualche centinaio di membri non rappresenta un carico elevato.

Non dimensionare il server in base a un numero riportato in un articolo, incluso questo. Misura il tuo ambiente.

free -m
docker stats --no-stream

L'uso costante della swap insieme a pagine lente indica che la RAM non è sufficiente. Se la memoria resta stabile ma le pagine sono lente, la causa è probabilmente diversa: consulta ./launcher logs app prima di acquistare un piano più grande. Aggiungi anche un controllo dall'esterno del server, perché un forum che esaurisce la memoria alle 3am può interrompersi senza segnalarlo: un monitor di stato Uptime Kuma self-hosted su un host separato ti avvisa prima dei membri.

Quando Discourse non è la scelta giusta

Discourse è un'applicazione complessa, con un'installazione pesante e un ciclo di rebuild per ogni impostazione presente in app.yml. Questo costo offre strumenti di moderazione completi e una ricerca che continua a funzionare anche quando l'archivio è molto esteso. Per trenta persone che cercano semplicemente uno spazio per discutere, è una soluzione più complessa di quanto richieda la conversazione. Leggi prima il confronto tra i software per forum self-hosted e scegli Discourse perché ti servono le sue funzionalità, non perché è il nome che già conoscevi.

FAQ

Posso installare Discourse su un VPS senza un nome di dominio?

No. La configurazione fornita specifica che Discourse non funziona con un semplice indirizzo IP e che è obbligatorio DISCOURSE_HOSTNAME. Discourse genera i link assoluti a partire da quel nome host; se viene usato un indirizzo IP, i link non funzionano e il rilascio del certificato viene impedito. Crea un record A prima di iniziare e verifica con dig +short forum.example.com che risolva all'indirizzo del tuo server.

Devo configurare SMTP per completare l'installazione?

Ad agosto 2026 puoi ignorare questo passaggio. La procedura guidata offre invece gli accessi Discourse ID e app.yml include un'opzione per saltare la convalida della configurazione email. Per qualsiasi utilizzo oltre una prima prova, configurala, perché l'attivazione degli account e i reset delle password vengono inviati tramite email. Usa un relay autenticato sulla porta 587 o 465, perché la maggior parte dei provider VPS blocca la porta 25 in uscita.

Perché la ricompilazione di Discourse si è interrotta?

La causa più comune è la memoria. La compilazione degli asset durante il build richiede più memoria rispetto al sito in esecuzione. Per questo, un server che gestisce correttamente il forum può comunque non riuscire a ricompilarlo. Se dmesg mostra Out of memory: Killed process riferito a un processo ruby, aggiungi swap, considerando che il file di swap della procedura guidata ha dimensione pari a 2 GB, quindi esegui di nuovo ./launcher rebuild app. Se il build si interrompe per un errore YAML, la causa è invece probabilmente un errore di indentazione in app.yml.

Discourse deve essere eseguito dietro il mio nginx o Caddy?

Solo se il VPS ospita anche altri siti. Se Discourse è l'unico servizio sul server, lascia che il container utilizzi le porte 80 e 443 e rilasci il proprio certificato. In questo modo ci sono meno componenti da gestire. Per condividere il server, aggiungi templates/web.socketed.template.yml, commenta le righe expose e configura il proxy verso il socket Unix /var/discourse/shared/standalone/nginx.http.sock. Inoltra X-Forwarded-Proto, altrimenti Discourse genera link http:// in una pagina HTTPS.

Come posso eseguire il backup di un'installazione self-hosted di Discourse?

Usa la pagina Backups nell'area Admin oppure esegui discourse backup dopo ./launcher enter app. Gli archivi vengono salvati sull'host in /var/discourse/shared/standalone/backups/default/. Verifica che sia attiva l'impostazione che include gli upload, copia /var/discourse/containers/app.yml insieme all'archivio e trasferisci entrambi su un altro computer. Un backup salvato sullo stesso disco del sito non sopravvive al guasto per cui è stato creato.