Faire tourner coturn dans Docker paraît simple jusqu'à ce que le premier appel échoue et que vous compreniez que le conteneur n'a jamais eu de vrai chemin pour le trafic de relais. L'image est petite et l'installation vraiment rapide, cinq minutes si tout se passe bien, mais presque tout le monde se heurte au même mur dès le premier essai : le réseau par défaut de Docker et les besoins en ports d'un serveur TURN ne font pas bon ménage.

Voici le docker-compose.yml qui fonctionne vraiment, les réglages de turnserver.conf qui comptent, et la seule décision réseau qui détermine si votre conteneur coturn Docker tient une vraie charge d'appels ou seulement un rapide test en local.

Réponse rapide

Récupérez l'image officielle coturn/coturn, montez votre propre turnserver.conf (l'image n'accepte ni le realm, ni le secret, ni les identifiants en variables d'environnement) et lancez le conteneur avec network_mode: host. La publication de ports habituelle de Docker ne peut pas gérer la large plage de relais UDP dont TURN a besoin sans lancer des milliers de processus proxy.

Prérequis

Avant de commencer

  • Docker Engine et Docker Compose installés sur un hôte Linux (le réseau de l'hôte, le mode utilisé dans ce guide, est une fonctionnalité de Docker propre à Linux et se comporte autrement sous Docker Desktop pour Mac/Windows).
  • Un serveur avec une adresse IP publique et fixe. Le relais TURN ne fonctionne pas derrière un CGNAT ni avec une IP résidentielle qui change.
  • Un nom de domaine pointant vers cette IP si vous voulez la prise en charge de TLS (turns:), ainsi qu'un certificat pour ce domaine.
  • Les ports 3478 et 5349 (TCP et UDP) et la plage de ports de relais choisie ouverts sur le pare-feu de l'hôte et dans tout groupe de sécurité cloud placé devant.

L'image Docker officielle de coturn

L'image à utiliser est coturn/coturn sur Docker Hub, maintenue directement par le projet coturn et non par un tiers. Elle existe en variante basée sur Debian et en variante plus légère basée sur Alpine (des tags comme 4.14-alpine), et les deux suivent les versions de coturn. Épinglez un tag précis en production ; n'utilisez pas latest pour découvrir qu'un comportement du relais a changé la semaine même d'un incident.

L'image propose trois façons de configurer le serveur : un turnserver.conf monté, des options en ligne de commande ajoutées après le nom de l'image, ou un petit ensemble de variables d'environnement pour la détection automatique d'IP. C'est sur cette troisième option que la plupart des gens trébuchent, nous y revenons plus bas.

Coturn Docker Compose : la configuration

Voici un docker-compose.yml qui fonctionne vraiment, et non la version allégée qui ne gère que la partie STUN :

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:

Deux points à remarquer. Il n'y a pas de bloc ports:, et ce n'est pas une erreur : le réseau de l'hôte le rend inutile et Compose l'ignorerait de toute façon. Et coturn-data est un volume nommé plutôt qu'un bind mount, puisque la seule chose qui vit dans /var/lib/coturn/ est un état de session transitoire que vous n'avez pas besoin d'inspecter depuis l'hôte.

Lancez-le de la manière habituelle :

docker compose up -d
docker compose logs -f coturn

Configurer turnserver.conf

C'est ce fichier qui fait le vrai travail. Une version minimale mais utilisable en production :

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

Certains de ces réglages comptent plus qu'il n'y paraît :

  • external-ip : indiquez l'IP publique réelle de votre serveur, ne laissez pas ce champ vide. Derrière un NAT (c'est techniquement le cas de la plupart des VM cloud, même avec une IP publique associée), il faut dire à coturn quelle adresse annoncer aux clients, sinon les candidats de relais reviennent avec l'IP privée et rien ne se connecte.
  • realm : c'est l'un des termes que les gens recherchent directement, et il est simple : ce n'est qu'une chaîne, généralement votre domaine, qui délimite le domaine d'authentification. Il n'a pas besoin que le DNS pointe quelque part pour que TURN fonctionne, mais votre certificat TLS, si.
  • use-auth-secret / static-auth-secret : c'est le mécanisme d'identifiants à long terme (des paires nom d'utilisateur/mot de passe basées sur HMAC et à durée limitée), la même approche que dans l'AMI Coturn pour AWS de Meetrix. C'est plus simple à exploiter qu'une liste statique de noms d'utilisateur et de mots de passe, et cela ne demande pas de base de données.
  • no-cli : désactive l'interface d'administration en ligne de commande de coturn, accessible par telnet sur le port 5766. Rien ne justifie de l'exposer dans un conteneur, et la laisser active est une surface d'attaque inutile.

Variables d'environnement ou fichier de configuration

Si vous cherchiez précisément des variables d'environnement coturn pour Docker afin de définir TURN_REALM ou équivalent, voici la réponse directe : l'image officielle n'en a pas. Elle ne prend en charge que quatre variables d'environnement, toutes liées à la détection automatique d'IP et non au comportement de TURN :

Variable Rôle
DETECT_EXTERNAL_IP Détecte automatiquement au démarrage l'adresse IPv4 externe du conteneur
DETECT_RELAY_IP Détecte automatiquement l'adresse IPv4 à utiliser pour les allocations de relais
DETECT_EXTERNAL_IPV6 Comme ci-dessus, pour IPv6
DETECT_RELAY_IPV6 Comme ci-dessus, pour le relais IPv6

Tout le reste, le realm, le static-auth-secret, les certificats TLS, la plage de ports, doit passer par turnserver.conf ou par une option ajoutée à la ligne de commande de docker run ou de Compose. Si vous avez vu un article ou une réponse Stack Overflow où une variable d'environnement TURN_REALM= fonctionne, il s'agit presque certainement d'une image tierce et non de l'officielle, et cela ne suivra pas si vous changez d'image plus tard.

Pourquoi le réseau de l'hôte compte

C'est le point qui fait échouer la plupart des premiers essais, y compris ceux copiés de tutoriels qui l'omettent. TURN a besoin d'une large plage de ports UDP ouverte pour les allocations de relais, de 49152 à 65535 par défaut, soit plus de 16 000 ports. La façon habituelle de Docker d'exposer des ports, une option -p ou un bloc ports: par port, lance un processus docker-proxy distinct en espace utilisateur pour chacun. Seize mille de ces processus mettent un hôte à genoux bien avant l'arrivée du vrai trafic.

network_mode: host contourne entièrement le problème : le conteneur partage directement la pile réseau de l'hôte, sans NAT, sans proxy par port et sans couche de traduction contre laquelle coturn devrait lutter pour déterminer sa propre IP externe.

Si vous ne pouvez pas utiliser le réseau de l'hôte

Certains environnements, comme Docker Desktop sur Mac ou Windows ou certaines plateformes de conteneurs gérées, ne prennent pas du tout en charge le réseau de l'hôte. Dans ce cas, réduisez la plage de relais dans turnserver.conf (min-port=49152, max-port=49352 est un bon point de départ pour les tests) et publiez explicitement cette plage réduite. Cela fonctionne, mais impose un plafond strict de sessions relayées simultanées, et vous revoilà aux prises avec la traduction NAT pour l'IP externe.

Encore une chose à savoir avant de déployer : un ticket coturn sur GitHub (#966) documente exactement ce problème, qui se manifeste par des appels qui se connectent puis se coupent, avec « allocation watchdog determined stale session state » dans les journaux, et dont l'origine est que le conteneur devine mal l'IP externe en réseau hôte. Définir external-ip explicitement, comme dans la configuration ci-dessus, permet de l'éviter.

Tester le conteneur

Une fois le conteneur lancé, générez un nom d'utilisateur et un mot de passe temporaires à partir de votre static-auth-secret :

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)

Saisissez ensuite l'adresse du serveur, le nom d'utilisateur et le mot de passe générés dans l'outil de test Trickle ICE, lancez la collecte des candidats et cherchez un candidat de type relay dans les résultats. Pas de candidat relay signifie que le conteneur tourne mais n'est pas encore utilisable comme serveur TURN, généralement à cause d'external-ip, du pare-feu ou de la plage de ports plutôt que de coturn lui-même. Notre testeur ICE WebRTC effectue la même vérification et explique clairement chaque candidat trouvé.

Dépannage

  • Le conteneur démarre mais aucun candidat relay n'apparaît : vérifiez que external-ip dans turnserver.conf correspond à l'IP publique réelle du serveur, et que le groupe de sécurité ou le pare-feu placé devant autorise toute la plage UDP de min-port à max-port, pas seulement 3478 et 5349.
  • Ça marche en local mais échoue pour les utilisateurs distants : c'est presque toujours une lacune du pare-feu ou du groupe de sécurité sur la plage de ports de relais, ou le conteneur tourne sans réseau hôte avec seulement 3478/5349 publiés.
  • La négociation TLS échoue sur le 5349 : le chemin de montage du volume des certificats doit correspondre exactement à ce que pointent cert et pkey dans le conteneur. Une faute de frappe dans un chemin relatif échoue ici sans bruit dans les journaux, à moins de les surveiller de près.
  • CPU élevé sous charge : si vous n'êtes pas en réseau hôte, c'est très souvent la surcharge de docker-proxy décrite plus haut, et non coturn lui-même qui peine.

Quand éviter l'installation maison

Faire tourner coturn dans Docker est le bon choix quand vous voulez un contrôle total de la configuration, que vous utilisez déjà une pile basée sur Docker, ou que vous testez avant de vous engager sur une infrastructure. C'est aussi un service de plus dont vous êtes responsable : mettre à jour l'image, surveiller le disque et la mémoire, renouveler les certificats, et être celui qu'on appelle quand un changement réseau dont personne ne vous a parlé bloque la plage de ports de relais.

Si vous préférez ne pas assurer ces astreintes vous-même, Meetrix maintient une AMI Coturn pour AWS préconfigurée et une image GCP équivalente, toutes deux avec le réseau, TLS et les identifiants à long terme déjà correctement configurés. Il existe aussi une option de déploiement en un clic si vous voulez les économies de l'auto-hébergement sans jamais toucher à un docker-compose.yml. Et si le serveur TURN est destiné à un déploiement Jitsi Meet, le guide de configuration TURN pour Jitsi et le guide Jitsi sur AWS (ou son équivalent GCP) couvrent les différences de configuration propres à ce cas.

Questions fréquentes

Quelle est l'image Docker officielle de coturn ?

C'est coturn/coturn sur Docker Hub, maintenue par le projet coturn lui-même. Elle existe en variantes basées sur Debian et sur Alpine (les tags alpine sont nettement plus légers). Des images tierces comme instrumentisto/coturn existent aussi, mais l'image officielle est celle à privilégier.

L'image Docker de coturn prend-elle en charge des variables d'environnement comme TURN_REALM ?

Non. L'image officielle n'expose qu'une poignée de variables d'environnement pour la détection automatique d'IP (DETECT_EXTERNAL_IP, DETECT_RELAY_IP et leurs équivalents IPv6). Le realm, le static-auth-secret et tous les autres réglages TURN viennent d'un fichier turnserver.conf monté ou d'options en ligne de commande, pas de variables d'environnement.

Pourquoi coturn a-t-il besoin de network_mode: host dans Docker ?

Coturn relaie les flux média sur une large plage de ports UDP, de 49152 à 65535 par défaut. Publier toute cette plage avec le mappage de ports habituel de Docker lance un processus docker-proxy par port, ce qui étouffe l'hôte sous une vraie charge d'appels. Le réseau de l'hôte contourne entièrement cette couche.

Puis-je faire tourner coturn dans Docker sans le réseau de l'hôte ?

Oui, si vous réduisez d'abord la plage de relais. Réglez min-port et max-port sur quelque chose comme 49152 à 49352 dans turnserver.conf, puis publiez cette plage réduite ainsi que 3478 et 5349 dans votre fichier compose. Cela fonctionne, mais limite le nombre d'appels relayés simultanés que le serveur peut gérer.

Comment générer des identifiants TURN pour tester un conteneur coturn ?

Avec use-auth-secret et un static-auth-secret définis dans turnserver.conf, générez un nom d'utilisateur et un mot de passe à durée limitée avec un script HMAC-SHA1 (openssl dgst -hmac), puis testez-les contre le conteneur avec l'outil WebRTC Trickle ICE avant de le brancher sur votre application.

Où coturn stocke-t-il ses données dans Docker ?

L'image écrit par défaut dans /var/lib/coturn/, principalement l'état SQLite du processus turnserver. Si vous n'utilisez que des identifiants à long terme via static-auth-secret plutôt qu'une base d'utilisateurs, vous pouvez monter ce chemin en tmpfs, car rien ne mérite d'être conservé entre deux redémarrages.

Évitez complètement l'installation Docker

Lancez un serveur coturn préconfiguré sur AWS, avec le réseau, TLS et les identifiants déjà correctement réglés.

Obtenir Coturn sur AWS Marketplace