📍 Article principal de la série : Headscale 2026 : guide pratique.
Vos applications internes (back-office, dashboards, services métier) n’ont aucune raison d’être exposées sur Internet public. En combinant Headscale (mesh VPN auto-hébergé) et Coolify (PaaS open source), vous obtenez une plateforme de déploiement où chaque app est accessible uniquement aux membres de votre tailnet. Aucune URL publique, aucune attaque DDoS possible, conformité maximale.
Prérequis
- Headscale en production avec ACL configurées.
- Coolify v4 installé sur un VPS (voir Guide Coolify).
- Au moins un client Tailscale connecté côté équipe.
- Niveau attendu : avancé.
- Temps estimé : 2 à 3 heures.
Étape 1 — Architecture cible
Schéma : utilisateur (laptop avec Tailscale) → mesh Headscale → VPS Coolify (Tailscale installé) → applications déployées (chacune écoute uniquement sur l’IP Tailscale).
Aucun port 80/443 du VPS Coolify n’est exposé sur l’IP publique. Le firewall UFW bloque tout entrant sauf 22 (SSH) et UDP 41641 (Tailscale).
Étape 2 — Connecter le VPS Coolify à Headscale
Sur le VPS Coolify :
curl -fsSL https://tailscale.com/install.sh | sh
PREAUTH=$(headscale preauthkeys create -u servers --expiration 720h)
tailscale up --login-server=https://headscale.votre-entreprise.com \
--auth-key=$PREAUTH \
--advertise-tags=tag:coolify,tag:server-prod \
--ssh
tailscale ip -4
# Note l'IP attribuée, par exemple 100.64.0.5
Étape 3 — Configurer Coolify pour bind sur l’IP Tailscale
Coolify expose son interface admin sur le port 8000 par défaut. Restreindre :
# Edit /data/coolify/source/.env
APP_URL=https://coolify-tail.tailnet.ts.net
SSL_MODE=off # On gère HTTPS via Caddy interne
Configurer Caddy interne de Coolify pour servir uniquement sur l’IP Tailscale :
coolify-tail.tailnet.ts.net:80 {
bind 100.64.0.5
reverse_proxy localhost:8000
}
Bloquer toutes les autres connexions :
ufw default deny incoming
ufw allow 22/tcp
ufw allow 41641/udp
ufw enable
Étape 4 — Déployer une application interne
Dans Coolify, déployer une app standard (Next.js, Hono, Astro). Dans la section Domains, au lieu d’un domaine public, mettre :
https://outline.tailnet.ts.net
Coolify génère un certificat Let’s Encrypt via DNS-01 (cert manager) pour le sous-domaine MagicDNS. Le service écoute sur l’IP Tailscale uniquement.
Étape 5 — Configurer MagicDNS Headscale
Dans /etc/headscale/config.yaml :
dns:
magic_dns: true
base_domain: tailnet.ts.net
override_local_dns: true
nameservers:
global:
- 1.1.1.1
Restart : systemctl restart headscale. Tous les clients Tailscale réauthentifiés résolvent désormais nom-machine.tailnet.ts.net automatiquement.
Étape 6 — ACL pour accès aux apps
Compléter /etc/headscale/policy.hujson :
{
"acls": [
{
"action": "accept",
"src": ["group:devs", "group:marketing"],
"dst": ["tag:coolify:443"]
}
]
}
Étape 7 — Test depuis un client
Sur le laptop d’un développeur connecté au tailnet :
tailscale ping coolify-tail
# Doit répondre direct ou via DERP
curl https://outline.tailnet.ts.net
# Doit retourner la home Outline
Ouvrir le navigateur sur https://outline.tailnet.ts.net : l’app charge. Sur un appareil non connecté au tailnet : connexion refusée (timeout). Sécurité maximale.
Étape 8 — Apps multi-utilisateurs
Pour Vaultwarden, Forgejo, Outline, Grafana, Plausible : déployer chacun comme app Coolify standard avec domaine MagicDNS dédié. Toutes héritent du contrôle d’accès Headscale.
Étape 9 — Backups et failover
Conserver un domaine public de fallback pour Coolify lui-même : coolify.votre-entreprise.com sur l’IP publique avec UFW restreint à votre IP admin uniquement. En cas de panne Headscale, vous gardez l’accès au panel admin.
Erreurs fréquentes
| Erreur | Cause | Solution |
|---|---|---|
| App inaccessible depuis tailnet | Coolify écoute sur 0.0.0.0 mais firewall bloque | Bind explicite sur IP Tailscale |
| Let’s Encrypt échoue sur tailnet | HTTP-01 challenge impossible sans IP publique | DNS-01 via Cloudflare API ou cert auto-signé interne |
| MagicDNS ne résout pas | Client Tailscale pas redémarré | tailscale down && tailscale up |
| Performance dégradée | Pas de DERP régional | Configurer DERP en Europe ou Afrique |
| SSH inaccessible si Headscale down | UFW bloque 22 par défaut | Garder règle UFW allow 22 from votre-ip-admin |
| Coolify déploie mais services ne tournent pas | Containers en network bridge sans accès tailnet | Configurer containers en network host |
Mise en œuvre dans le contexte sénégalais
Trois précisions. Bande passante : pour 30 employés accédant aux apps internes, comptez 5 Mb/s sortant agrégé. Hetzner CX23 offre 1 Gb/s, largement suffisant. Coût total : Headscale (4,51 €) + Coolify (3,99 €) + nom de domaine (12 €/an) = ~ 100 € la première année. Conformité : architecture parfaitement compatible RGPD, ARTCI, NESA. Aucune donnée ne quitte les serveurs européens, aucun service public n’est exposé.
Tutoriels frères
FAQ
Pourquoi pas Tailscale Funnel pour exposer publiquement ? Funnel est une feature Tailscale Cloud uniquement, pas Headscale. Pour exposer publiquement, garder un Caddy classique avec domaine public.
Comment monitorer la disponibilité des apps internes ? Uptime Kuma déployé dans Coolify, accessible via tailnet, monitore les autres apps via leurs URLs MagicDNS.
Mobile : comment accéder aux apps depuis téléphone ? App Tailscale officielle, MagicDNS activé, taper l’URL outline.tailnet.ts.net dans Safari/Chrome.
Que faire si Headscale tombe ? Coolify reste accessible via SSH. Apps inaccessibles tant que Headscale ne redémarre pas. Plan de rollback : exposer temporairement Coolify avec UFW + cert.
Cette architecture passe-t-elle un audit SOC2 ? Avec logs Loki + monitoring + ACL documentées, oui. C’est une architecture Zero Trust standard.
Pour creuser ce sujet
- 🔝 Retour au guide général : guide pratique Headscale 2026
- Documentation Coolify : coolify.io/docs
Tester ce setup sur votre propre serveur
Le moyen le plus rapide de tester ce tutoriel en conditions réelles : prendre un petit VPS Hostinger.
Lien d affiliation. Si vous achetez via ce lien, le blog reçoit une petite commission sans surcoût pour vous.
Pourquoi associer Headscale et Coolify pour un PaaS souverain
Coolify expose par defaut un dashboard web sur le port 8000 et chaque application sur des sous-domaines publics. Pour une PME ouest-africaine qui heberge des donnees sensibles (factures clients, paie, donnees medicales), exposer le panneau d’admin sur Internet equivaut a peindre une cible. Headscale, le serveur de coordination Tailscale open source, permet d’acceder a Coolify uniquement depuis votre laptop, vos serveurs prod et le poste de votre comptable a Dakar, sans jamais ouvrir un port en clair.
Avant de commander quoi que ce soit, verifiez que votre fournisseur de domaine supporte les enregistrements wildcard CNAME. Sans wildcard, vous devrez creer manuellement un sous-domaine pour chaque service deploye via Coolify, ce qui annule 80 % de l’interet du PaaS.
Etape 1 : commander deux VPS Hetzner et configurer Headscale
Prenez un VPS CPX11 (4,15 EUR/mois, 2 720 FCFA) pour Headscale et un CX32 (7,42 EUR/mois, 4 870 FCFA) pour Coolify. Sur le premier :
apt install -y headscale
headscale generate config > /etc/headscale/config.yaml
systemctl enable --now headscale
Editez la valeur server_url dans la config pour pointer vers https://headscale.exemple.sn. Sans HTTPS valide (Let’s Encrypt via Caddy ou Nginx), les clients Tailscale refusent de se connecter.
Etape 2 : creer un namespace et generer une preauth key
headscale namespaces create coolify-prod
headscale --namespace coolify-prod preauthkeys create --reusable --expiration 24h
La cle generee (format mkey:xxxx…) sera utilisee pour enroler chaque noeud. Reusable permet d’enroler plusieurs machines avec la meme cle, expiration 24h limite la fenetre d’attaque si la cle fuit dans un log.
Etape 3 : enroler le VPS Coolify dans le mesh
Sur le second VPS :
curl -fsSL https://tailscale.com/install.sh | sh
tailscale up --login-server=https://headscale.exemple.sn --authkey=mkey:xxxx --hostname=coolify-prod
Verifiez avec tailscale status que la machine apparait avec une IP 100.x.x.x. C’est cette IP que vous utiliserez plus tard pour acceder au dashboard, jamais l’IP publique du VPS.
Etape 4 : installer Coolify v4 sur le VPS
curl -fsSL https://cdn.coollabs.io/coolify/install.sh | bash
L’installeur deploie Docker, Traefik et le dashboard Coolify v4 en 5 a 8 minutes. A la fin il affiche l’URL d’admin (port 8000) et les identifiants temporaires. Notez-les immediatement, ils ne sont plus reaffiches.
Etape 5 : enrouler le firewall pour bloquer le port 8000 public
L’erreur classique : laisser le 8000 ouvert. Bloquez-le immediatement :
ufw default deny incoming
ufw allow 22/tcp
ufw allow in on tailscale0
ufw enable
Apres activation, testez depuis votre laptop hors VPN : curl http://IP_PUBLIQUE:8000 doit timeout. Depuis votre laptop connecte a Headscale : curl http://100.64.0.2:8000 doit retourner le HTML du dashboard. Sinon, ufw bloque tailscale0 : verifiez avec ufw status verbose.
Etape 6 : configurer le wildcard DNS pour les apps deployees
Coolify deploie chaque app sur un sous-domaine. Creez chez votre registrar :
*.apps.exemple.sn CNAME coolify-prod.headscale.exemple.sn
Ce CNAME pointe vers le hostname Headscale, donc resolvable uniquement par les noeuds du mesh. Resultat : meme si une app est deployee, son URL est inaccessible depuis Internet ouvert. Parfait pour un staging interne ou un outil RH.
Etape 7 : deployer la premiere application depuis Git
Dans le dashboard Coolify, ajoutez votre repo GitHub via une Deploy Key. Choisissez le type (Node.js, Python, Docker Compose), Coolify detecte le Dockerfile ou le buildpack adapte. Le premier deploiement prend 3 a 6 minutes selon la taille de l’image. Une fois live, l’URL https://monapp.apps.exemple.sn est accessible uniquement aux machines du mesh.
Etape 8 : automatiser les sauvegardes des bases de donnees
Coolify cree automatiquement une base PostgreSQL ou MariaDB par projet. Activez les backups vers un bucket S3 compatible (Backblaze B2, 0,006 USD/Go/mois soit 4 FCFA) directement dans le panneau Database > Backups. Programmez un dump quotidien a 3h du matin et conservez 14 jours. Testez la restauration une fois par mois sur un VPS jetable. Dans la continuité, lisez notre guide durcissement VPS et notre tutoriel n8n auto-heberge.
Etape 9 : monitorer la sante du mesh avec Headplane
Headplane est l’interface graphique officielle communautaire pour Headscale. Deployez-la dans Coolify lui-meme via Docker Compose. Elle affiche en temps reel les machines connectees, le trafic par tunnel, les latences entre noeuds. Pour un mesh de 10 a 30 machines (taille typique d’une PME ouest-africaine), la consommation est negligeable : moins de 50 Mo de RAM. Configurez les notifications par email ou Telegram quand un noeud passe offline plus de 5 minutes, ce qui detecte une coupure ADSL chez un commercial a Saint-Louis avant qu’il appelle le support.
Etape 10 : implementer les ACL pour cloisonner les acces
Par defaut, tout noeud du mesh peut joindre tout autre noeud sur tout port. C’est rarement souhaitable. Ecrivez un fichier ACL au format HuJSON :
{
"groups": {
"group:admins": ["alice@exemple.sn"],
"group:devs": ["bob@exemple.sn", "claire@exemple.sn"]
},
"acls": [
{"action":"accept", "src":["group:admins"], "dst":["*:*"]},
{"action":"accept", "src":["group:devs"], "dst":["coolify-prod:80,443,8000"]}
]
}
Chargez via headscale acls load /etc/headscale/acl.hujson. Verifiez avec headscale acls test qu’aucune regle ne se contredit. Une ACL mal formee bloque silencieusement tout le mesh : ayez toujours une session SSH directe sur le serveur Headscale en parallele pour pouvoir rollback.
Etape 11 : preparer la haute disponibilite Coolify
Coolify v4 ne supporte pas encore le mode multi-master. Pour atteindre 99,5 % de dispo, utilisez la replication asynchrone : un second VPS Hetzner CX32 dans un autre datacenter (Helsinki + Nuremberg), un cron qui rsync /data/coolify toutes les 15 minutes, et un script de bascule documente. La perte maximale en cas de panne est de 15 minutes de configuration, ce qui est acceptable pour un PaaS interne. Pour la prod critique, evaluez plutot Kubernetes managed (DOKS DigitalOcean a 12 USD/mois, 7 870 FCFA, par noeud) qui apporte la HA native mais multiplie la complexite operationnelle par 5.
Etape 12 : journaliser les acces et alimenter Wazuh
Configurez Headscale pour ecrire les evenements (login, logout, key creation, ACL change) dans /var/log/headscale/audit.log au format JSON. Cette source est ingerable directement par un agent Wazuh installe sur le VPS Headscale. Vous obtenez ainsi une vision unifiee des acces reseau dans le meme dashboard que vos preuves PCI DSS et GDPR. Une tentative de connexion echouee declenche immediatement une alerte Wazuh de niveau 7, et trois echecs en moins de 60 secondes basculent l’IP dans la blocklist via active response.
Etape 13 : documenter le runbook de reset complet
Une fois par semestre, simulez un sinistre total : perte des deux VPS Hetzner et du backup primaire. Combien de temps pour remonter Headscale + Coolify + une app exemple a partir des seules sauvegardes restic Backblaze B2 ? Notre cible chez les clients est 90 minutes. Ecrivez chaque commande, chaque validation, chaque ecran attendu. Imprimez le runbook et stockez-le hors-ligne, parce que le jour ou vous en aurez besoin, votre Notion partage sera potentiellement aussi inaccessible que vos serveurs. Lectures complémentaires, lisez notre guide strategie backup 3-2-1.
Etape 14 : verrouiller les budgets cloud avant de scaler
Hetzner facture a la consommation. Sans alerte budget, un script qui boucle peut creer 50 snapshots et generer une facture inattendue de 80 EUR (52 480 FCFA) en 24 heures. Activez les notifications cout dans la console Hetzner Cloud (menu Security > Spending limit) et fixez un plafond mensuel a 30 EUR. Couplez avec un cron quotidien qui appelle l’API Hetzner et envoie un resume Telegram du nombre d’instances actives, du trafic sortant cumule et de l’estimation a fin de mois. Ce reflexe budgetaire evite 90 % des mauvaises surprises de facturation infrastructure pour une PME a Dakar ou Abidjan.