L’objectif de ce guide est de déployer Termix dans Docker, de le rendre accessible uniquement depuis un réseau Tailscale, et de placer Traefik devant l’application afin d’obtenir une URL HTTPS valide de la forme https://docktermix.tailnet-example.ts.net.
L’idée importante n’est pas simplement d’ajouter Tailscale à une VM Docker : le nœud Tailscale appartient ici à la stack elle-même. Le serveur Linux conserve son propre nom, par exemple srv-termix-001, tandis que le service exposé dans le tailnet porte le nom logique docktermix.
Toutes les valeurs de ce billet sont anonymisées. Le domaine tailnet-example.ts.net, les IP, les noms d’hôtes et les clés doivent être remplacés par les valeurs de votre environnement.
1. Résultat recherché

Cette architecture a un avantage majeur : aucun port applicatif Termix n’est publié sur le LAN. Le port 8080 de Termix et le port 4822 de guacd restent sur un réseau Docker privé. Le seul point d’entrée utilisateur est le port 443 de Traefik, lui-même porté par le namespace réseau du conteneur Tailscale.
Autrement dit, une machine du réseau local ne peut pas contourner Traefik en allant directement sur http://IP_DU_SERVEUR:8080. Le service n’est accessible que par le tailnet et toujours via HTTPS.
2. Pourquoi séparer le nom de la VM et celui du service
Le hostname de la VM n’a aucune obligation d’être identique au hostname Tailscale du conteneur. Dans cet exemple, la VM est appelée srv-termix-001, mais le conteneur Tailscale reçoit TS_HOSTNAME=docktermix. MagicDNS crée alors le nom docktermix dans le tailnet et le FQDN associé devient docktermix.tailnet-example.ts.net.
Cette séparation est pratique en production : le nom de l’infrastructure décrit le serveur, tandis que le nom Tailscale décrit le service rendu.
3. Prérequis
- Une VM Linux 64 bits ; Ubuntu 24.04 LTS convient parfaitement.
- Docker Engine et le plugin Docker Compose officiels.
- Un tailnet Tailscale avec MagicDNS activé.
- La fonction HTTPS Certificates activée dans la page DNS de l’administration Tailscale.
- Une clé d’authentification Tailscale pour l’enrôlement initial du conteneur.
- Le périphérique
/dev/net/tundisponible sur l’hôte.
Pour un serveur persistant, privilégiez une clé Tailscale one-shot, non éphémère, pré-approuvée si nécessaire et idéalement associée à un tag. Une clé réutilisable est plus sensible en cas de fuite.
4. Installer Docker Engine et Docker Compose
Si Docker est déjà installé depuis le dépôt officiel, cette section peut être ignorée. Sur Ubuntu, l’installation depuis le dépôt Docker permet d’obtenir Docker Engine, Buildx et le plugin Compose actuel.
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
sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
sudo apt update
sudo apt install -y \
docker-ce \
docker-ce-cli \
containerd.io \
docker-buildx-plugin \
docker-compose-plugin
Vérifiez ensuite :
sudo systemctl status docker --no-pager sudo docker version sudo docker compose version sudo docker run --rm hello-world
Si vous souhaitez utiliser Docker sans sudo, vous pouvez ajouter votre compte au groupe docker. Attention : l’appartenance à ce groupe confère des privilèges comparables à ceux de root.
sudo usermod -aG docker $USER
Déconnectez-vous puis reconnectez-vous avant de tester :
docker ps
5. Préparer l’arborescence
sudo mkdir -p /opt/docktermix/{tailscale/state,tailscale/run,traefik,termix/data}
sudo chown -R $USER:$USER /opt/docktermix
cd /opt/docktermix
L’arborescence finale est la suivante :
/opt/docktermix/
├── compose.yaml
├── .env
├── tailscale/
│ ├── state/
│ └── run/
├── traefik/
│ ├── traefik.yml
│ └── dynamic.yml
└── termix/
└── data/
6. Préparer Tailscale
6.1 MagicDNS et HTTPS
Dans la console Tailscale, activez MagicDNS, puis la génération de certificats HTTPS. Les certificats Tailscale sont émis pour le FQDN complet machine.tailnet.ts.net et non pour le nom court. Le navigateur devra donc utiliser https://docktermix.tailnet-example.ts.net.
Attention à la confidentialité des noms. Les certificats HTTPS publics sont enregistrés dans les journaux Certificate Transparency. Le FQDN du nœud ayant reçu un certificat devient donc public, même si l’accès au service reste totalement contrôlé par Tailscale. Choisissez un nom de machine qui ne révèle pas d’information sensible.
6.2 Stocker la clé d’enrôlement
Créez le fichier /opt/docktermix/.env :
TS_AUTHKEY=tskey-auth-REDACTED
chmod 600 /opt/docktermix/.env
Ne publiez jamais ce fichier et ne copiez pas une sortie complète de docker compose config : Compose résout les variables et peut afficher la clé en clair. Pour valider uniquement la syntaxe, utilisez docker compose config --quiet.
Le paramètre TS_AUTH_ONCE=true associé à un répertoire d’état persistant permet au conteneur de conserver son identité entre les redémarrages sans refaire inutilement l’authentification.
7. Le fichier compose.yaml complet
name: docktermix
services:
###########################################################################
# Tailscale
###########################################################################
tailscale:
image: tailscale/tailscale:latest
container_name: docktermix-tailscale
hostname: docktermix
environment:
TS_AUTHKEY: ${TS_AUTHKEY}
TS_HOSTNAME: docktermix
TS_STATE_DIR: /var/lib/tailscale
TS_SOCKET: /var/run/tailscale/tailscaled.sock
TS_USERSPACE: "false"
TS_ACCEPT_DNS: "true"
TS_AUTH_ONCE: "true"
TS_EXTRA_ARGS: "--accept-routes"
volumes:
- ./tailscale/state:/var/lib/tailscale
- ./tailscale/run:/var/run/tailscale
devices:
- /dev/net/tun:/dev/net/tun
cap_add:
- NET_ADMIN
- NET_RAW
networks:
- termix-backend
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "tailscale status --json >/dev/null 2>&1"]
interval: 10s
timeout: 5s
retries: 12
start_period: 10s
###########################################################################
# Traefik
###########################################################################
traefik:
image: traefik:v3
container_name: docktermix-traefik
network_mode: "service:tailscale"
depends_on:
tailscale:
condition: service_healthy
volumes:
- ./traefik/traefik.yml:/etc/traefik/traefik.yml:ro
- ./traefik/dynamic.yml:/etc/traefik/dynamic.yml:ro
- ./tailscale/run:/var/run/tailscale:ro
security_opt:
- no-new-privileges:true
restart: unless-stopped
###########################################################################
# Termix
###########################################################################
termix:
image: ghcr.io/lukegus/termix:latest
container_name: docktermix-termix
environment:
PORT: "8080"
ENABLE_GUACAMOLE: "true"
GUACD_URL: "tcp://guacd:4822"
GUACD_TUNNEL_HOST: "termix"
volumes:
- ./termix/data:/app/data
expose:
- "8080"
depends_on:
- guacd
networks:
- termix-backend
restart: unless-stopped
###########################################################################
# Guacamole daemon
###########################################################################
guacd:
image: guacamole/guacd:1.6.0
container_name: docktermix-guacd
expose:
- "4822"
networks:
- termix-backend
restart: unless-stopped
#############################################################################
# Network
#############################################################################
networks:
termix-backend:
driver: bridge
8. Comprendre le Compose
8.1 Le conteneur Tailscale
TS_HOSTNAME=docktermix impose le nom du nœud dans le tailnet. TS_STATE_DIR=/var/lib/tailscale stocke l’identité dans un volume persistant. TS_USERSPACE=false demande l’utilisation du réseau kernel TUN, d’où le montage de /dev/net/tun et les capacités NET_ADMIN/NET_RAW.
TS_EXTRA_ARGS=--accept-routes permet au conteneur d’utiliser les routes de sous-réseaux annoncées par d’autres nœuds Tailscale. Si votre Termix n’a besoin que d’adresses Tailscale natives, ce paramètre peut être retiré.
8.2 Traefik partage le namespace réseau Tailscale
La ligne network_mode: service:tailscale est le cœur de l’architecture. Traefik ne reçoit pas sa propre interface réseau : il utilise celle du conteneur Tailscale. Lorsqu’il écoute sur :443, il écoute donc sur l’IP Tailscale du nœud docktermix.
Traefik monte également le socket /var/run/tailscale/tailscaled.sock. C’est par cette LocalAPI que le resolver Tailscale de Traefik demande le certificat correspondant au domaine .ts.net.
8.3 Termix et guacd restent privés
Termix et guacd sont uniquement attachés au réseau Docker termix-backend. Le mot-clé expose documente leurs ports pour les autres conteneurs, mais ne les publie pas sur l’hôte. Il n’y a volontairement aucun bloc ports: pour ces services.
Termix utilise GUACD_URL=tcp://guacd:4822. GUACD_TUNNEL_HOST=termix indique à guacd comment revenir vers le backend Termix lorsqu’une fonction de tunnel ou de jump host l’exige.
9. Configurer Traefik
9.1 Configuration statique
Créez /opt/docktermix/traefik/traefik.yml :
entryPoints:
websecure:
address: ":443"
providers:
file:
filename: /etc/traefik/dynamic.yml
watch: true
certificatesResolvers:
tailscale:
tailscale: {}
api:
dashboard: false
log:
level: INFO
accessLog: {}
Un seul entrypoint est nécessaire : websecure sur le port 443. Il n’est pas utile d’exposer un port 80 pour un challenge ACME classique, car le certificat est récupéré par le resolver Tailscale.
9.2 Configuration dynamique
Créez /opt/docktermix/traefik/dynamic.yml :
http:
routers:
termix:
rule: "Host(`docktermix.tailnet-example.ts.net`)"
entryPoints:
- websecure
service: termix
tls:
certResolver: tailscale
services:
termix:
loadBalancer:
passHostHeader: true
servers:
- url: "http://termix:8080"
Traefik déduit le domaine du certificat à partir du matcher Host(). Le resolver nommé tailscale ne considère que les noms au format Tailscale machine.tailnet.ts.net.
Termix utilise notamment des WebSockets. Avec Traefik, aucune directive particulière n’est nécessaire dans cette configuration : le reverse proxy gère la montée de connexion automatiquement.
10. Valider la configuration sans exposer les secrets
cd /opt/docktermix docker compose config --quiet
Si la commande ne retourne rien et termine avec un code 0, le fichier Compose est syntaxiquement valide.
11. Démarrage progressif
Pour simplifier le diagnostic, il est préférable de démarrer les couches l’une après l’autre plutôt que l’ensemble de la stack d’un coup.
11.1 Démarrer uniquement Tailscale
cd /opt/docktermix docker compose up -d tailscale docker compose ps docker compose logs --tail=100 tailscale
Vérifiez ensuite l’identité et l’adresse Tailscale :
docker exec docktermix-tailscale tailscale status docker exec docktermix-tailscale tailscale ip -4 docker exec docktermix-tailscale tailscale status --self
Le nœud doit apparaître avec le nom docktermix et une adresse du bloc Tailscale, typiquement 100.x.x.x.
11.2 Tester MagicDNS
Depuis un poste déjà connecté au même tailnet :
ping docktermix
Sous PowerShell, pour tester explicitement la résolution DNS :
Resolve-DnsName docktermix Resolve-DnsName docktermix.tailnet-example.ts.net Resolve-DnsName docktermix.tailnet-example.ts.net -Server 100.100.100.100
Le résolveur 100.100.100.100 est le résolveur local Tailscale (« Quad100 »). Sur Windows, Resolve-DnsName est généralement un meilleur test de MagicDNS que nslookup.
11.3 Démarrer guacd et Termix
docker compose up -d guacd termix docker compose ps
Testez le backend sur le réseau Docker privé :
docker run --rm --network docktermix_termix-backend curlimages/curl:latest -I http://termix:8080
Une réponse HTTP confirme que Termix fonctionne avant même d’introduire Traefik dans le chemin.
11.4 Démarrer Traefik
docker compose up -d traefik docker compose ps docker compose logs --tail=100 traefik
Depuis un client Tailscale sous Windows :
Test-NetConnection docktermix.tailnet-example.ts.net -Port 443 curl.exe -I https://docktermix.tailnet-example.ts.net
L’URL finale est alors https://docktermix.tailnet-example.ts.net. Le navigateur doit présenter la page de connexion Termix avec un certificat TLS valide.
12. Ce qui n’est volontairement pas exposé
LAN du serveur Docker TCP/8080 : non publié TCP/4822 : non publié TCP/443 : non publié directement par Docker sur l'interface LAN Internet Termix : non publié Traefik : non publié Tailnet Tailscale TCP/443 : accessible via docktermix.tailnet-example.ts.net TCP/8080 : non accessible directement TCP/4822 : non accessible directement
C’est une différence essentielle par rapport à un Compose classique qui publierait 8080:8080 sur toutes les interfaces de l’hôte.
13. Contrôles de sécurité recommandés
- Clé Tailscale : utilisez une clé one-shot quand c’est possible et ne la commitez jamais dans Git.
- Fichier .env : permissions 600 et accès limité au compte d’administration.
- Tailscale ACL / Grants : limitez les utilisateurs ou groupes autorisés à joindre le nœud Termix sur TCP/443.
- Tags Tailscale : appliquez par exemple un tag de service afin que l’identité du serveur ne dépende pas d’un compte utilisateur.
- Nom du nœud : souvenez-vous que le FQDN ayant reçu un certificat sera visible dans les journaux publics Certificate Transparency.
- Docker : considérez tout membre du groupe docker comme un administrateur root de la machine.
- Termix : utilisez un mot de passe administrateur robuste et activez les mécanismes d’authentification disponibles adaptés à votre environnement.
14. Sauvegarde
Les deux emplacements importants sont /opt/docktermix/termix/data et /opt/docktermix/tailscale/state. Le premier contient les données persistantes de Termix. Le second contient l’identité Tailscale du nœud et doit être traité comme une donnée sensible.
sudo tar czf /var/backups/docktermix-termix-data.tar.gz /opt/docktermix/termix/data sudo tar czf /var/backups/docktermix-tailscale-state.tar.gz /opt/docktermix/tailscale/state
Chiffrez les sauvegardes qui contiennent l’état Tailscale et limitez strictement leur accès. En cas de perte volontaire de cet état, il est souvent plus propre de ré-enrôler le conteneur comme un nouveau nœud.
15. Mise à jour de la stack
cd /opt/docktermix docker compose pull docker compose up -d docker image prune
Avant une mise à jour importante de Termix, sauvegardez son répertoire de données. Pour un environnement strictement maîtrisé, remplacez les tags latest ou v3 par des versions explicitement validées afin d’éviter les changements non planifiés.
16. Commandes de diagnostic utiles
# Etat de la stack docker compose ps # Logs docker compose logs --tail=100 tailscale docker compose logs --tail=100 traefik docker compose logs --tail=100 termix docker compose logs --tail=100 guacd # Etat Tailscale docker exec docktermix-tailscale tailscale status docker exec docktermix-tailscale tailscale ip -4 # Vérifier l'écoute réseau du namespace Tailscale docker exec docktermix-tailscale ss -lntp # Vérifier la résolution du backend depuis le réseau Docker docker run --rm --network docktermix_termix-backend curlimages/curl:latest -I http://termix:8080
17. Points de dépannage
Le FQDN ne se résout pas
Vérifiez que MagicDNS est activé, que le client est bien connecté au tailnet et que le nœud docktermix apparaît dans la console Tailscale. Sous Windows, utilisez Resolve-DnsName pour vos tests.
Le port 443 ne répond pas
Commencez par vérifier docker compose ps, puis les logs Traefik. Comme Traefik partage le namespace réseau de Tailscale, il doit démarrer après que Tailscale soit devenu healthy.
Le certificat n’est pas délivré
Vérifiez que HTTPS Certificates est activé dans Tailscale, que le router Traefik utilise bien tls.certResolver: tailscale et que la règle Host() contient le FQDN complet en .ts.net.
Traefik répond mais Termix est inaccessible
Testez directement http://termix:8080 depuis le réseau docktermix_termix-backend. Si ce test échoue, le problème se situe entre Termix, son démarrage ou le réseau Docker et non dans TLS/Tailscale.
RDP/VNC ne fonctionne pas
Vérifiez que guacd est démarré, que GUACD_URL pointe vers tcp://guacd:4822 et que GUACD_TUNNEL_HOST vaut termix. Si vous activez l’enregistrement des sessions Guacamole, prévoyez en plus un volume partagé entre Termix et guacd.
18. Pourquoi cette architecture est intéressante
Cette mise en œuvre combine trois couches complémentaires. Tailscale fournit l’accès privé et l’identité réseau ; Traefik fournit le reverse proxy HTTPS avec un certificat reconnu par les navigateurs ; Termix fournit l’interface d’administration distante. Aucun port applicatif n’a besoin d’être publié sur Internet ni sur le LAN.
Le résultat reste également simple à maintenir : chacun des composants conserve sa propre image et son propre cycle de mise à jour, tout en se comportant du point de vue de l’utilisateur comme une seule appliance accessible à https://docktermix.tailnet-example.ts.net.
Références
- Tailscale – Docker configuration parameters
- Tailscale – Connect a Docker container to your tailnet
- Tailscale – Traefik certificates on Tailscale
- Tailscale – Enabling HTTPS
- Tailscale – MagicDNS
- Tailscale – Auth keys
- Traefik – Tailscale certificate resolver
- Termix – Docker installation
- Termix – Environment variables
- Termix – Reverse proxy
- Docker – Install Docker Engine on Ubuntu
- Docker – Install Docker Compose plugin
- Docker – Linux post-installation


