> Source: https://meetrix.io/blogs/coturn-docker-compose-it/
> Markdown copy of that page. Cite the URL above, not this file.

Development

# Coturn Docker: un server TURN con Docker Compose

[By Shalomi Umeshika](https://meetrix.io/blogs/authors/shalomi-umeshika/) • October 14, 2026 • 11 min read

[English](https://meetrix.io/blogs/coturn-docker-compose/)[Deutsch](https://meetrix.io/blogs/coturn-docker-compose-de/)[Español](https://meetrix.io/blogs/coturn-docker-compose-es/)[Français](https://meetrix.io/blogs/coturn-docker-compose-fr/)Italiano

Eseguire coturn in Docker sembra semplice finché la prima chiamata non si connette e ti accorgi che il container non ha mai avuto un vero percorso per il traffico di relay. L'immagine è piccola e l'installazione è davvero rapida, cinque minuti se non va storto nulla, ma quasi tutti sbattono contro lo stesso muro al primo tentativo: la rete predefinita di Docker e i requisiti di porte di un server TURN non vanno d'accordo.

Qui trovi il docker-compose.yml che funziona davvero, le impostazioni di turnserver.conf che contano e l'unica scelta di rete che decide se il tuo container coturn Docker regge un vero carico di chiamate o solo un rapido test in locale.

Risposta rapida

Scarica l'immagine ufficiale coturn/coturn, monta il tuo turnserver.conf (l'immagine non accetta realm, secret o credenziali come variabili d'ambiente) ed esegui il container con network\_mode: host. La normale pubblicazione delle porte di Docker non riesce a gestire l'ampio intervallo di relay UDP di cui TURN ha bisogno senza avviare migliaia di processi proxy.

## Prerequisiti

Prima di iniziare

-   Docker Engine e Docker Compose installati su un host Linux (la rete dell'host, la modalità usata in questa guida, è una funzione di Docker solo per Linux e si comporta diversamente su Docker Desktop per Mac/Windows).
-   Un server con un indirizzo IP pubblico e statico. Il relay TURN non funziona dietro CGNAT né con un IP residenziale che cambia.
-   Un nome di dominio che punti a quell'IP se vuoi il supporto TLS (turns:), più un certificato per quel dominio.
-   Le porte 3478 e 5349 (TCP e UDP) e l'intervallo di porte di relay scelto aperti sul firewall dell'host e in qualsiasi security group cloud posto davanti.

## L'immagine Docker ufficiale di coturn

L'immagine da usare è [coturn/coturn](https://hub.docker.com/r/coturn/coturn) su Docker Hub, mantenuta direttamente dal progetto coturn e non da terzi. È disponibile in una variante basata su Debian e in una più leggera basata su Alpine (tag come `4.14-alpine`), ed entrambe vengono aggiornate di pari passo con le versioni di coturn. In produzione fissa un tag specifico; non usare `latest` per poi scoprire che il comportamento del relay è cambiato nella stessa settimana di un incidente.

L'immagine offre tre modi per configurare il server: un `turnserver.conf` montato, opzioni da riga di comando aggiunte dopo il nome dell'immagine, oppure un piccolo insieme di variabili d'ambiente per il rilevamento automatico dell'IP. È su questa terza opzione che inciampa la maggior parte delle persone, ne parliamo più sotto.

## Coturn Docker Compose: la configurazione

Ecco un docker-compose.yml che funziona davvero, non la versione ridotta che gestisce solo la parte STUN:

```yaml
services:
  coturn:
    image: coturn/coturn:4.14-alpine
    container_name: coturn
    restart: unless-stopped
    network_mode: host
    volumes:
      - ./turnserver.conf:/etc/coturn/turnserver.conf:ro
      - ./certs:/etc/coturn/certs:ro
      - coturn-data:/var/lib/coturn

volumes:
  coturn-data:
```

Due cose da notare. Non c'è un blocco `ports:`, e non è un errore: la rete dell'host lo rende superfluo e Compose lo ignorerebbe comunque. E `coturn-data` è un volume con nome invece di un bind mount, perché in `/var/lib/coturn/` c'è solo stato di sessione temporaneo che non hai bisogno di ispezionare dall'host.

Avvialo nel modo consueto:

```bash
docker compose up -d
docker compose logs -f coturn
```

## Configurare turnserver.conf

È questo il file che fa il lavoro vero. Una versione minima ma utilizzabile in produzione:

```plaintext
listening-port=3478
tls-listening-port=5349
min-port=49152
max-port=65535

external-ip=203.0.113.10
realm=turn.example.com

use-auth-secret
static-auth-secret=replace-this-with-a-long-random-string

cert=/etc/coturn/certs/fullchain.pem
pkey=/etc/coturn/certs/privkey.pem

no-cli
no-multicast-peers
log-file=stdout
```

Alcune di queste impostazioni contano più di quanto sembri:

-   **external-ip:** impostalo sull'IP pubblico reale del server, non lasciarlo vuoto. Dietro NAT (tecnicamente lo è la maggior parte delle VM cloud, anche con un IP pubblico associato), bisogna dire a coturn quale indirizzo comunicare ai client, altrimenti i candidati di relay tornano con l'IP privato e non si connette nulla.
-   **realm:** è uno dei termini che le persone cercano direttamente, ed è semplice: è solo una stringa, di solito il tuo dominio, che delimita l'ambito di autenticazione. Non serve che il DNS punti da qualche parte perché TURN funzioni, mentre al certificato TLS serve.
-   **use-auth-secret / static-auth-secret:** è il meccanismo di credenziali a lungo termine (coppie di nome utente e password basate su HMAC e a durata limitata), lo stesso approccio usato nell'[AMI Coturn per AWS](https://meetrix.io/blogs/coturn-developer-guide/) di Meetrix. È più semplice da gestire di un elenco statico di nomi utente e password e non richiede un database.
-   **no-cli:** disattiva la CLI di amministrazione di coturn via telnet sulla porta 5766. Non c'è motivo di esporla dentro un container, e lasciarla attiva è una superficie di attacco inutile.

## Variabili d'ambiente o file di configurazione

Se cercavi proprio le variabili d'ambiente di coturn per Docker per impostare `TURN_REALM` o simili, ecco la risposta diretta: l'immagine ufficiale non le ha. Supporta solo quattro variabili d'ambiente, e riguardano tutte il rilevamento automatico dell'IP, non il comportamento di TURN:

| Variabile | Scopo |
| --- | --- |
| `DETECT_EXTERNAL_IP` | Rileva automaticamente all'avvio l'indirizzo IPv4 esterno del container |
| `DETECT_RELAY_IP` | Rileva automaticamente l'indirizzo IPv4 da usare per le allocazioni di relay |
| `DETECT_EXTERNAL_IPV6` | Come sopra, per IPv6 |
| `DETECT_RELAY_IPV6` | Come sopra, per il relay IPv6 |

Tutto il resto, il realm, lo static-auth-secret, i certificati TLS, l'intervallo di porte, deve passare da `turnserver.conf` o da un'opzione aggiunta alla riga di comando di `docker run` o di Compose. Se hai visto un articolo o una risposta su Stack Overflow in cui una variabile d'ambiente `TURN_REALM=` funziona, quasi certamente si tratta di un'immagine di terze parti e non di quella ufficiale, e non funzionerà più se in futuro cambi immagine.

## Perché conta la rete dell'host

È questo il punto che fa fallire la maggior parte dei primi tentativi, compresi quelli copiati da tutorial che lo saltano. TURN ha bisogno di un ampio intervallo di porte UDP aperto per le allocazioni di relay, da 49152 a 65535 per impostazione predefinita, oltre 16.000 porte. Il modo normale di Docker di esporre le porte, un flag `-p` o un blocco `ports:` per porta, avvia un processo `docker-proxy` separato in user space per ciascuna. Sedicimila di questi mettono in ginocchio un host molto prima che arrivi traffico reale.

`network_mode: host` aggira del tutto il problema: il container condivide direttamente lo stack di rete dell'host, senza NAT, senza proxy per porta e senza uno strato di traduzione con cui coturn debba combattere quando prova a capire il proprio IP esterno.

Se non puoi usare la rete dell'host

Alcuni ambienti, come Docker Desktop su Mac o Windows o certe piattaforme di container gestite, non supportano affatto la rete dell'host. In quel caso restringi l'intervallo di relay in turnserver.conf (min-port=49152, max-port=49352 è un buon punto di partenza per i test) e pubblica esplicitamente quell'intervallo più piccolo. Funziona, ma impone un tetto rigido alle sessioni inoltrate simultanee, e torni a combattere con la traduzione NAT per l'IP esterno.

Un'altra cosa da sapere prima del deploy: una issue di coturn su GitHub ([#966](https://github.com/coturn/coturn/issues/966)) documenta esattamente questo problema, che si presenta come chiamate che si connettono e poi cadono, con «allocation watchdog determined stale session state» nei log, causato dal container che indovina l'IP esterno sbagliato con la rete dell'host. Impostare `external-ip` esplicitamente, come nella configurazione sopra, è ciò che lo evita.

## Testare il container

Con il container in esecuzione, genera un nome utente e una password temporanei a partire dal tuo `static-auth-secret`:

```bash
secret=replace-this-with-a-long-random-string && \
time=$(date +%s) && \
expiry=86400 && \
username=$(( $time + $expiry )) && \
echo username:$username && \
echo password:$(echo -n $username | openssl dgst -binary -sha1 -hmac $secret | openssl base64)
```

Poi inserisci l'indirizzo del server, il nome utente e la password generati nello [strumento di test Trickle ICE](https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/), avvia la raccolta dei candidati e cerca un candidato di tipo relay nei risultati. Nessun candidato relay significa che il container è in esecuzione ma non è ancora utilizzabile come server TURN, di solito per un problema di external-ip, del firewall o dell'intervallo di porte e non di coturn in sé. Il nostro [tester ICE WebRTC](https://meetrix.io/features/webrtc-ice-tester/) esegue lo stesso controllo e spiega in modo chiaro ogni candidato che trova.

## Risoluzione dei problemi

-   **Il container si avvia ma non compare nessun candidato relay:** controlla che `external-ip` in turnserver.conf corrisponda all'IP pubblico reale del server e che il security group o il firewall davanti consenta l'intero intervallo UDP da min-port a max-port, non solo 3478 e 5349.
-   **Funziona in locale ma non per gli utenti remoti:** quasi sempre è un buco nel firewall o nel security group sull'intervallo di porte di relay, oppure il container gira senza rete dell'host con solo 3478/5349 pubblicate.
-   **L'handshake TLS sulla 5349 fallisce:** il percorso di montaggio del volume dei certificati deve corrispondere esattamente a quello a cui puntano `cert` e `pkey` dentro il container. Un errore di battitura in un percorso relativo qui fallisce in silenzio nei log, a meno che tu non li tenga d'occhio.
-   **CPU alta sotto carico:** se non usi la rete dell'host, molto spesso è il sovraccarico di docker-proxy descritto sopra e non coturn in sé ad andare in affanno.

## Quando evitare l'installazione fai da te

Eseguire coturn in Docker è la scelta giusta quando vuoi il pieno controllo della configurazione, usi già uno stack basato su Docker o stai facendo test prima di impegnarti su un'infrastruttura. È però anche un servizio in più di cui sei responsabile: aggiornare l'immagine, tenere d'occhio disco e memoria, rinnovare i certificati ed essere chi riceve l'allarme quando un cambiamento di rete di cui nessuno ti ha parlato blocca l'intervallo di porte di relay.

Se preferisci non farti carico di queste reperibilità, Meetrix mantiene un'[AMI Coturn per AWS](https://meetrix.io/blogs/coturn-developer-guide/) preconfigurata e un'[immagine per GCP](https://meetrix.io/blogs/coturn-gcp-developer-guide/) corrispondente, entrambe con rete, TLS e credenziali a lungo termine già configurati correttamente. C'è anche un'[opzione di distribuzione con un clic](https://meetrix.io/blogs/switch-to-hassle-free-setup-of-coturn-explore-one-click-deployment-at-75-saving/) se vuoi il risparmio del self-hosting senza toccare un docker-compose.yml. E se il server TURN serve proprio per una distribuzione di Jitsi Meet, la [guida alla configurazione TURN per Jitsi](https://meetrix.io/blogs/setting-up-a-turn-server-for-jitsi-meet/) e la [guida Jitsi su AWS](https://meetrix.io/blogs/setup-jitsi-meet-aws-dev-guide/) (o la sua [controparte GCP](https://meetrix.io/blogs/setup-jitsi-meet-on-gcp-developer-guide/)) coprono le differenze di configurazione specifiche di quel caso.

## Domande frequenti

Qual è l'immagine Docker ufficiale di coturn?

È coturn/coturn su Docker Hub, mantenuta dal progetto coturn stesso. È disponibile in varianti basate su Debian e su Alpine (i tag alpine sono decisamente più leggeri). Esistono anche immagini di terze parti come instrumentisto/coturn, ma quella ufficiale è la prima da scegliere.

L'immagine Docker di coturn supporta variabili d'ambiente come TURN\_REALM?

No. L'immagine ufficiale espone solo poche variabili d'ambiente per il rilevamento automatico dell'IP (DETECT\_EXTERNAL\_IP, DETECT\_RELAY\_IP e i loro equivalenti IPv6). Il realm, lo static-auth-secret e ogni altra impostazione TURN arrivano da un file turnserver.conf montato o da opzioni da riga di comando, non da variabili d'ambiente.

Perché coturn ha bisogno di network\_mode: host in Docker?

Coturn inoltra i flussi media su un ampio intervallo di porte UDP, da 49152 a 65535 per impostazione predefinita. Pubblicare tutto quell'intervallo con la normale mappatura delle porte di Docker avvia un processo docker-proxy per porta, che soffoca l'host sotto un vero carico di chiamate. La rete dell'host salta del tutto quello strato.

Posso eseguire coturn in Docker senza la rete dell'host?

Sì, se prima restringi l'intervallo di relay. Imposta min-port e max-port su qualcosa come 49152-49352 in turnserver.conf, poi pubblica quell'intervallo più piccolo insieme a 3478 e 5349 nel file compose. Funziona, ma limita quante chiamate inoltrate simultanee il server può gestire.

Come genero le credenziali TURN per testare un container coturn?

Con use-auth-secret e uno static-auth-secret impostati in turnserver.conf, genera un nome utente e una password a durata limitata con uno script HMAC-SHA1 (openssl dgst -hmac), poi provali sul container con lo strumento WebRTC Trickle ICE prima di collegarlo alla tua applicazione.

Dove salva i dati coturn in Docker?

L'immagine scrive per impostazione predefinita in /var/lib/coturn/, soprattutto lo stato SQLite del processo turnserver. Se usi solo credenziali a lungo termine tramite static-auth-secret invece di un database di utenti, puoi montare quel percorso come tmpfs, perché non c'è nulla che valga la pena conservare tra un riavvio e l'altro.

## Salta del tutto l'installazione con Docker

Avvia un server coturn preconfigurato su AWS, con rete, TLS e credenziali già gestiti correttamente.

[Ottieni Coturn su AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-zrea7eq3c4jbe)

Meetrix Store

Coturn

TURN/STUN for WebRTC, no per-minute relay fees

[Deploy it](https://meetrix.io/store/coturn/)

Meetrix Store New

Deploy what this guide covers, pre-configured.

-    [Coturn TURN/STUN for WebRTC, no per-minute relay fees](https://meetrix.io/store/coturn/)
-    [Jitsi Meet Self-hosted video calls for 50 to 500 users](https://meetrix.io/store/jitsi-meet/)
-    [RustDesk Remote desktop AMI, a TeamViewer alternative](https://meetrix.io/store/rustdesk/)
-    [OpenVPN Encrypted remote access, no per-user fees](https://meetrix.io/store/openvpn/)
-    [Mailcow Business email on your own domain](https://meetrix.io/store/mailcow/)
-    [Listmonk Newsletters with no per-subscriber fees](https://meetrix.io/store/listmonk/)

[Browse all products](https://meetrix.io/store/)
