# Contenitori di app multipli per un singolo sito Discourse

**URL:** <https://meta.discourse.org/t/multiple-app-containers-for-a-single-discourse-site/393913>\
**Category:** Self-hosting\
**Tags:** hosting\
**Created:** [19 Gennaio 2026, 10:03am UTC](https://meta.discourse.org/t/multiple-app-containers-for-a-single-discourse-site/393913 "2026-01-19T10:03:43Z")\
**Posts on this page:** 1\
**Showing post:** 1

<div class="post-metadata">

**Author:** ![Ethsim2](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ethsim2/32/522255_2.png) [@Ethsim2](https://meta.discourse.org/u/Ethsim2)\
**Post date:** [19 Gennaio 2026, 10:03am UTC](https://meta.discourse.org/t/multiple-app-containers-for-a-single-discourse-site/393913/1 "2026-01-19T10:03:43Z")

</div>

Puoi ospitare più installazioni autonome di Discourse su un singolo server (container separati / porte separate / app.yml separati) senza utilizzare la funzione “multisito” di Discourse.

È più manuale rispetto al multisito, ma mantiene le istanze isolate e rende più semplice migrare un singolo sito su un server dedicato in seguito.

Un modello pratico è:

• Postgres esterno (istanza singola)  
• Redis esterno (istanza singola)  
• più container web di Discourse  
• un nodo Sidekiq  
• reverse proxy con controlli di integrità (health checks)

Questo evita completamente il multisito, consentendo comunque risparmi sui costi per configurazioni a basso traffico.

* * *

> [@](#):
>
> > **Contesto — perché pubblico questo approccio**
> >
> > La discussione sopra ha esplorato due percorsi diversi:
> > 
> > • Discourse multisito (applicazione Rails condivisa)  
> > • Un singolo server che ospita più installazioni autonome di Discourse
> > 
> > Questo post documenta la seconda opzione.
> > 
> > L’obiettivo non è raccomandarlo come configurazione predefinita, ma mostrare un modello pratico che:
> > 
> > • mantiene le istanze isolate  
> > • evita l’accoppiamento del multisito  
> > • permette la migrazione successiva per singolo sito  
> > • funziona bene per sperimentazioni e comunità nelle fasi iniziali
> > 
> > Questo è intenzionalmente diverso dal multisito e non utilizza multisite.yml.
> 
> * * *
> 
> > **Stato del supporto e aspettative**
> >
> > Questo approccio si colloca al di fuori del modello di installazione ufficialmente supportato da Discourse.
> > 
> > Potrebbe comportare:
> > 
> > • più container autonomi  
> > • Postgres esterno condiviso  
> > • Redis esterno condiviso  
> > • reverse proxy personalizzato
> > 
> > A causa di ciò, gli assistenti potrebbero chiedere che i problemi vengano riprodotti su un’installazione standard a sito singolo prima di procedere con un debug più approfondito.
> > 
> > Ciò non significa che la configurazione sia invalida, ma solo che il supporto potrebbe essere fornito al meglio delle possibilità.
> 
> * * *
> 
> > **Cosa dovrebbe cambiare per un supporto completo**
> >
> > Per rimanere completamente all’interno delle configurazioni supportate, si applicherebbe una delle seguenti opzioni:
> > 
> > • multisito utilizzando multisite.yml  
> > • server separati per ogni installazione di Discourse
> > 
> > Qualsiasi soluzione intermedia è tecnicamente valida, ma non garantisce il supporto.
> 
> * * *
> 
> > **Perché documentarlo comunque**
> >
> > Questo modello è ancora utile per:
> > 
> > • apprendere l’interno di Discourse  
> > • ambienti di staging  
> > • sperimentazioni iniziali  
> > • test con costi controllati  
> > • amministratori a proprio agio nel debug autonomo
> > 
> > Questa documentazione è condivisa come riferimento pratico, non come raccomandazione per la produzione.
> > 
> > * * *
> > 
> > ### Modello di trasporto (nessun socket Unix)
> > 
> > Questo approccio non utilizza socket di dominio Unix.
> > 
> > Tutta la comunicazione avviene via TCP:
> > 
> > • Discourse → Postgres tramite pg:5432  
> > • Discourse → Redis tramite redis:6379  
> > • HAProxy → Discourse tramite porte host (8001, 8002)
> > 
> > Questo è intenzionale.
> > 
> > L’uso di TCP evita dipendenze da file system condivisi tra i container e mantiene ogni istanza completamente portatile.
> > 
> > Semplifica anche la successiva migrazione su un server separato, poiché non è necessario riprodurre percorsi di socket o permessi.
> > 
> > I socket Unix sono comunemente utilizzati in configurazioni single-host non containerizzate, ma offrono pochi benefici in un’architettura basata su Docker come questa.

* * *

RUNBOOK PER L’ESECUZIONE MULTI-CONTAINER DI DISCOURSE  
Postgres esterno + Redis + HAProxy + app1 / app2

* * *

1. PACCHETTI HOST

| Passaggio | Comando |
| --- | --- |
| Aggiorna il sistema | `apt-get update` |
| Installa strumenti di base | `apt-get install -y ca-certificates curl gnupg lsb-release` |
| Installa HAProxy + certbot + socat | `apt-get install -y haproxy certbot socat` |

* * *

1. RETE DOCKER (OBBLIGATORIA)

È necessaria una rete Docker definita dall’utente in modo che i container possano risolversi per nome.

| Passaggio | Comando |
| --- | --- |
| Crea la rete | `docker network create discourse-net` |
| Verifica | `docker network ls | grep discourse-net` |

Questo permette:

• DISCOURSE\_DB\_HOST=pg  
• DISCOURSE\_REDIS\_HOST=redis

di funzionare correttamente.

* * *

1. SEGRETI

| Scopo | Comando |
| --- | --- |
| Superutente Postgres | `export PG_SUPERPASS='REPLACE_ME_super_strong'` |
| Password DB Discourse | `export DISCOURSE_DBPASS='REPLACE_ME_discordb_strong'` |
| Password Redis | `export REDIS_PASS='REPLACE_ME_redis_strong'` |
| Chiave segreta base | `export SECRET_KEY_BASE="$(openssl rand -hex 64)"` |

* * *

1. CONTAINER POSTGRES

| Passaggio | Comando |
| --- | --- |
| Crea directory | `mkdir -p /var/discourse/external/postgres` |
| Esegui container | `docker run -d --name pg --restart=always --network=discourse-net -e POSTGRES_PASSWORD="$PG_SUPERPASS" -v /var/discourse/external/postgres:/var/lib/postgresql/data postgres:15` |
| Verifica | `docker ps | grep pg` |

* * *

1. CREA DATABASE

| Passaggio | Comando |
| --- | --- |
| Crea ruolo | `docker exec -it pg psql -U postgres -c "CREATE ROLE discourse LOGIN PASSWORD '$DISCOURSE_DBPASS';"` |
| Crea DB | `docker exec -it pg psql -U postgres -c "CREATE DATABASE discourse OWNER discourse ENCODING 'UTF8' TEMPLATE template0;"` |
| Ricerca testuale | `docker exec -it pg psql -U postgres -d discourse -c "ALTER DATABASE discourse SET default_text_search_config = 'pg_catalog.english';"` |
| Test accesso | `docker exec -it pg psql -U discourse -d discourse -c "select 1;"` |

* * *

1. ESTENSIONE PGVECTOR

Necessaria per le versioni moderne di Discourse.

| Passaggio | Comando |
| --- | --- |
| Installa | `docker exec -it pg bash -lc 'apt-get update && apt-get install -y postgresql-15-pgvector && rm -rf /var/lib/apt/lists/*'` |
| Crea estensione | `docker exec -it pg psql -U postgres -d discourse -c "CREATE EXTENSION IF NOT EXISTS vector;"` |
| Verifica | `docker exec -it pg psql -U postgres -d discourse -c "SELECT extname FROM pg_extension WHERE extname='vector';"` |

* * *

1. CONTAINER REDIS

| Passaggio | Comando |
| --- | --- |
| Crea directory | `mkdir -p /var/discourse/external/redis` |

Modello di configurazione Redis:

```plaintext
requirepass REPLACE_ME_REDIS
appendonly yes
save 900 1
save 300 10
save 60 10000

```

| Passaggio | Comando |
| --- | --- |
| Scrivi configurazione | `tee /var/discourse/external/redis/redis.conf >/dev/null <<EOF` |
| Inserisci password | `sed -i "s/REPLACE_ME_REDIS/$REDIS_PASS/" /var/discourse/external/redis/redis.conf` |
| Esegui Redis | `docker run -d --name redis --restart=always --network=discourse-net -v /var/discourse/external/redis:/data -v /var/discourse/external/redis/redis.conf:/usr/local/etc/redis/redis.conf redis:7-alpine redis-server /usr/local/etc/redis/redis.conf` |
| Test autenticazione | `docker exec -it redis redis-cli -a "$REDIS_PASS" ping` |

* * *

1. STRUTTURA DELLE DIRECTORY DI DISCOURSE

| Passaggio | Comando |
| --- | --- |
| Crea directory base | `mkdir -p /var/discourse` |
| Entra | `cd /var/discourse` |
| Clona repository | `git clone https://github.com/discourse/discourse_docker.git` |
| Directory container | `mkdir -p /var/discourse/containers` |
| Log condivisi | `mkdir -p /var/discourse/shared/web-only/log/var-log` |
| Collega container | `ln -sfn /var/discourse/containers /var/discourse/discourse_docker/containers` |
| Collega launcher | `ln -sfn /var/discourse/discourse_docker/launcher /var/discourse/launcher` |

* * *

1. CONTAINER APPLICAZIONE

app1.yml  
• web + sidekiq  
• porta 8001

```plaintext
docker_args: "--network=discourse-net"
expose:
  - "8001:80"

```

app2.yml  
• solo web  
• porta 8002  
• sidekiq disabilitato

```plaintext
docker_args: "--network=discourse-net"
expose:
  - "8002:80"

run:
  - exec: bash -lc 'mkdir -p /etc/service/sidekiq && touch /etc/service/sidekiq/down'

```

* * *

1. BOOTSTRAP

| Passaggio | Comando |
| --- | --- |
| Entra | `cd /var/discourse/discourse_docker` |
| Bootstrap app1 | `./launcher bootstrap app1` |
| Avvia app1 | `./launcher start app1` |
| Bootstrap app2 | `./launcher bootstrap app2` |
| Avvia app2 | `./launcher start app2` |

* * *

1. CONTROLLI DI INTEGRITÀ (HEALTH CHECKS)

| Passaggio | Comando |
| --- | --- |
| app1 | `curl -sSf http://127.0.0.1:8001/srv/status` |
| app2 | `curl -sSf http://127.0.0.1:8002/srv/status` |
| sidekiq app1 | `docker exec -it app1 pgrep -fa sidekiq` |
| sidekiq app2 | `docker exec -it app2 pgrep -fa sidekiq |

* * *

1. CERTIFICATO TLS

| Passaggio | Comando |
| --- | --- |
| Ferma proxy | `systemctl stop haproxy` |
| Emetti certificato | `certbot certonly --standalone -d example.com --agree-tos -m you@example.com --non-interactive` |
| Avvia proxy | `systemctl start haproxy` |

* * *

1. LOGICA HAPROXY

```plaintext
frontend fe_discourse
    bind :80
    bind :443 ssl crt /etc/letsencrypt/live/example.com/haproxy.pem

    http-request set-header X-Forwarded-Proto https if { ssl_fc }
    http-request set-header X-Forwarded-Proto http if !{ ssl_fc }

    redirect scheme https code 301 if !{ ssl_fc }

    use_backend be_discourse if { nbsrv(be_discourse) gt 0 }
    default_backend be_maint

```

```plaintext
backend be_discourse
    balance roundrobin
    option httpchk GET /srv/status
    server app1 127.0.0.1:8001 check
    server app2 127.0.0.1:8002 check

```

```plaintext
backend be_maint
    http-request return status 503 content-type text/html string "<h1>Maintenance</h1>"

```

* * *

1. RICOSTRUZIONI SENZA TEMPO DI INATTIVITÀ (ZERO-DOWNTIME)

| Passaggio | Comando |
| --- | --- |
| Disabilita app1 | `echo "disable server be_discourse/app1" | socat stdio /run/haproxy/admin.sock` |
| Ricrea app1 | `./launcher rebuild app1` |
| Abilita app1 | `echo "enable server be_discourse/app1" | socat stdio /run/haproxy/admin.sock` |

| Passaggio | Comando |
| --- | --- |
| Disabilita app2 | `echo "disable server be_discourse/app2" | socat stdio /run/haproxy/admin.sock` |
| Ricrea app2 | `./launcher rebuild app2` |
| Abilita app2 | `echo "enable server be_discourse/app2" | socat stdio /run/haproxy/admin.sock` |

* * *

FINE

Rete Docker richiesta  
Postgres e Redis esterni  
pgvector installato  
Sidekiq isolato su app1  
Controlli di integrità HAProxy abilitati  
Fallback per manutenzione attivo  
Ricostruzioni rotative supportate

> **Migrare un sito su un server dedicato in seguito**
>
> Un vantaggio dell’eseguire installazioni completamente autonome di Discourse (invece del multisito) è che la migrazione è semplice e a basso rischio.
> 
> Ogni istanza di Discourse ha già:
> 
> • il proprio container  
> • i propri upload  
> • il proprio database  
> • il proprio utilizzo di Redis  
> • il proprio app.yml
> 
> Non è necessario alcun disaccoppiamento del multisito.
> 
> * * *
> 
> Passaggi generali per la migrazione
> 
> 1. Provisiona un nuovo VPS
> 
> Installa Docker e Discourse normalmente sul nuovo server.  
> Non configurare il multisito.
> 
> * * *
> 
> 1. Crea un backup completo
> 
> Dal sito di origine:
> 
> Admin → Backups → Crea Backup
> 
> Scarica il file di backup.
> 
> Questo include:
> 
> • database  
> • upload  
> • utenti  
> • impostazioni  
> • temi
> 
> * * *
> 
> 1. Ripristina sul nuovo server
> 
> Sul nuovo server:
> 
> • completa la configurazione iniziale  
> • accedi come amministratore  
> • carica il backup  
> • ripristina
> 
> Discourse gestisce automaticamente la compatibilità dello schema.
> 
> * * *
> 
> 1. Cambio DNS
> 
> Aggiorna il record A del dominio per puntare al nuovo IP del server.
> 
> Una volta propagato il DNS, gli utenti verranno spostati in modo trasparente.
> 
> * * *
> 
> 1. Smantella il vecchio container
> 
> Sul server originale:
> 
> • ferma il vecchio container  
> • rimuovilo quando sei sicuro
> 
> Le altre installazioni di Discourse sullo stesso host non sono influenzate.
> 
> * * *
> 
> Perché è più semplice del multisito
> 
> Nei setup multisito, la migrazione richiede spesso:
> 
> • separazione dei database  
> • estrazione dei dati specifici del sito  
> • aggiustamento di multisite.yml  
> • riprogettazione di Sidekiq  
> • riconfigurazione di upload ed email
> 
> Con installazioni autonome, nulla di tutto ciò è necessario.
> 
> Ogni sito è già indipendente.
> 
> * * *
> 
> Riepilogo
> 
> Questo approccio sacrifica un po’ di complessità operativa all’inizio  
> per una separazione molto semplice in seguito.
> 
> Funziona particolarmente bene durante le sperimentazioni  
> o la costruzione di comunità nelle fasi iniziali.

* * *

Quando questo approccio probabilmente non è adatto

Questa configurazione di solito non è una buona idea se:

• i siti si aspettano traffico moderato o elevato fin dall’inizio  
• fai molto affidamento sul supporto ufficiale di Discourse  
• non ti senti a tuo agio nel debug di Docker, reti o reverse proxy  
• i requisiti di uptime sono rigorosi o critici per il business  
• più siti sono strettamente accoppiati a livello operativo  
• prevedi frequenti sperimentazioni con plugin su tutte le istanze

In questi casi, o:

• un setup multisito supportato  
o  
• un’installazione di Discourse per server

porterà generalmente a meno sorprese operative.

* * *

Nota importante

Questo approccio aumenta la flessibilità dell’infrastruttura,  
ma aumenta anche la responsabilità dell’amministratore.

Funziona meglio quando la persona che lo gestisce è a proprio agio nel gestire l’intera stack  
e considera eventuali guasti occasionali come parte del processo di apprendimento.

Se stabilità e supporto sono gli obiettivi principali,  
un configurazione supportata è quasi sempre la scelta migliore.

---

_[View the full topic](https://meta.discourse.org/t/multiple-app-containers-for-a-single-discourse-site/393913)._
