Tailscale est merveilleux mais c’est un service hébergé chez Tailscale Inc. Pour les organisations qui veulent souveraineté complète, sécurité maximum, ou éviter les coûts pour grosses équipes, Headscale est une implémentation open-source du serveur de coordination Tailscale. Vous l’auto-hébergez sur votre VPS, vos clients Tailscale standard s’y connectent comme à Tailscale.com, et tout reste chez vous. Voici le tutoriel d’installation 2026.
Voir notre guide Tailscale pour les bases.
Pourquoi Headscale
- Souveraineté complète : aucune donnée chez Tailscale Inc. Toutes vos clefs publiques, ACLs, sessions restent sur votre serveur.
- Pas de coût par utilisateur : illimité pour le prix d’un VPS
- Compatible clients Tailscale officiels : pas de fork, mêmes apps Linux/macOS/iOS/Android
- Open-source BSD
- Implémentation Go : binaire unique, simple à déployer
Limites vs Tailscale Cloud
- Pas de dashboard web officiel inclus (mais headscale-ui communautaire existe)
- Mises à jour à votre charge
- SSO/OIDC à configurer manuellement
- Pas de DERP relay automatique (vous devez en déployer un)
- Logs et audit à gérer vous-même
Prérequis
- Un VPS public avec IP fixe (Hetzner CX23 suffit largement)
- Un domaine pointant sur le VPS (
headscale.exemple.sn) - Reverse proxy HTTPS (Caddy ou Traefik)
- Niveau attendu : avancé
- Temps : 2-3 heures
Étape 1 — Installer Headscale
# Télécharger le binaire (vérifier la dernière version sur GitHub)
HEADSCALE_VERSION=0.23.0
wget -O /tmp/headscale.deb https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_amd64.deb
sudo dpkg -i /tmp/headscale.deb
sudo systemctl enable headscale
Étape 2 — Configurer headscale.yaml
# /etc/headscale/config.yaml
server_url: https://headscale.exemple.sn
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 0.0.0.0:50443
grpc_allow_insecure: false
private_key_path: /var/lib/headscale/private.key
noise:
private_key_path: /var/lib/headscale/noise_private.key
prefixes:
v4: 100.64.0.0/10
v6: fd7a:115c:a1e0::/48
derp:
server:
enabled: false
urls:
- https://controlplane.tailscale.com/derpmap/default
database:
type: sqlite3
sqlite:
path: /var/lib/headscale/db.sqlite
log:
format: text
level: info
dns:
magic_dns: true
base_domain: tailnet.exemple.sn
nameservers:
global:
- 1.1.1.1
- 8.8.8.8
Étape 3 — Reverse proxy Caddy
# /etc/caddy/Caddyfile
headscale.exemple.sn {
reverse_proxy 127.0.0.1:8080
@ws {
header Connection *Upgrade*
header Upgrade websocket
}
reverse_proxy @ws 127.0.0.1:8080
}
sudo systemctl restart caddy
sudo systemctl start headscale
sudo systemctl status headscale
Étape 4 — Créer un user et un device
# Créer un user
sudo headscale users create admin
# Générer une pre-auth key (clef d'inscription)
sudo headscale --user admin preauthkeys create --reusable --expiration 24h
# Copier la clef retournée
Étape 5 — Connecter un client Tailscale
# Sur la machine client (Linux/macOS)
sudo tailscale up \
--login-server https://headscale.exemple.sn \
--authkey LA_PRE_AUTH_KEY \
--hostname laptop-1
# Vérifier
tailscale status
Sur Windows et iOS/Android, l’option login-server se configure dans les Settings/Préférences avancées de l’app Tailscale.
Étape 6 — ACLs Headscale
# /etc/headscale/acl_policy.json
{
"groups": {
"group:admins": ["admin"]
},
"tagOwners": {
"tag:prod": ["group:admins"]
},
"acls": [
{
"action": "accept",
"src": ["group:admins"],
"dst": ["*:*"]
}
],
"ssh": [
{
"action": "accept",
"src": ["group:admins"],
"dst": ["tag:prod"],
"users": ["root", "deploy"]
}
]
}
# Référencer dans config.yaml
policy:
mode: file
path: /etc/headscale/acl_policy.json
sudo systemctl restart headscale
Étape 7 — Backup
# Backup quotidien de la base et des clefs
#!/bin/bash
DATE=$(date +%Y%m%d)
tar czf /tmp/headscale-$DATE.tar.gz /var/lib/headscale/ /etc/headscale/
aws s3 cp /tmp/headscale-$DATE.tar.gz s3://backups/headscale/
rm /tmp/headscale-$DATE.tar.gz
Headscale-UI (dashboard web)
Plusieurs UIs communautaires existent : headscale-ui, headplane. Déployables via Docker à côté de Headscale, accédant au gRPC sur le port 50443. Pratique pour les non-CLI.
Adaptation Afrique de l’Ouest
Pour une PME africaine qui veut souveraineté totale, Headscale sur un VPS local (chez Maktoob, DigitalRise) ou européen offre tout Tailscale sans dépendance externe. Coût : ~5 €/mois pour le VPS Headscale + 0 €/mois utilisateurs illimités. Excellent ROI face à 18 USD/user de Tailscale Premium.
Erreurs fréquentes
| Erreur | Cause | Solution |
|---|---|---|
| « server_url is invalid » | HTTPS non valide | Vérifier certificat Caddy/Let’s Encrypt |
| Tailscale client ne se connecte pas | WebSocket non proxifié | Ajouter handler @ws dans Caddyfile |
| Lent, latence élevée | DERP relay tiers | Déployer son propre DERP |
| Magic DNS ne marche pas | base_domain mauvais | Sous-domaine spécifique tailnet |
Pour approfondir
- guide pratique Tailscale
- ACLs et tags
- Tailscale SSH bastion
- Repository Headscale : github.com/juanfont/headscale
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.
Etape 1 : comprendre pourquoi Headscale plutot que Tailscale officiel
Tailscale offre un service hebergement gere par l’editeur americain, ce qui pose deux problemes pour un public ouest-africain : la latence vers les serveurs de coordination europeens et la dependance a un compte SaaS dont les conditions peuvent changer. Headscale est l’implementation libre du serveur de coordination Tailscale, ecrite en Go, qui se deploie sur votre propre VPS.
En 2026, Headscale a depasse la version 0.24 et supporte la quasi totalite des fonctionnalites du protocole : ACL, magic DNS, subnet routers, exit nodes, taildrop. Pour une PME a Dakar qui veut interconnecter trois bureaux et deux developpeurs nomades, c’est l’outil ideal.
Etape 2 : provisionner un VPS et installer Go
Choisissez un VPS Hostinger, OVH ou Scaleway situe a Paris pour minimiser la latence avec Dakar (autour de 60 ms). Le plan a 8 000 FCFA/mois suffit pour 100 noeuds. Connectez-vous en SSH puis installez Go 1.22 ou superieur.
sudo apt update && sudo apt install -y wget
wget https://go.dev/dl/go1.22.5.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.22.5.linux-amd64.tar.gz
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc
source ~/.bashrc
go version
# Sortie attendue : go version go1.22.5 linux/amd64
Si la sortie affiche une version anterieure, Headscale refusera de compiler certaines fonctions liees a WireGuard. La presence du binaire go dans le PATH est la confirmation que la base est saine.
Etape 3 : installer Headscale via le paquet officiel
Plutot que de compiler depuis les sources, utilisez le paquet .deb publie sur le depot GitHub officiel. C’est plus rapide et la mise a jour se fait avec apt.
HEADSCALE_VERSION="0.24.0"
wget --output-document=headscale.deb "https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_amd64.deb"
sudo apt install -y ./headscale.deb
sudo systemctl enable --now headscale
systemctl status headscale
La sortie attendue affiche active (running) en vert. Si vous voyez failed, consultez journalctl -u headscale -n 50 pour identifier l’erreur (souvent un port deja occupe ou une mauvaise config TLS).
Etape 4 : configurer le fichier /etc/headscale/config.yaml
Le fichier de configuration vit dans /etc/headscale/config.yaml. Editez-le pour pointer vers votre nom de domaine et activer HTTPS via Let’s Encrypt. Remplacez vpn.exemple.sn par votre propre domaine.
server_url: https://vpn.exemple.sn:443
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 127.0.0.1:9090
private_key_path: /var/lib/headscale/private.key
ip_prefixes:
- 100.64.0.0/10
- fd7a:115c:a1e0::/48
database:
type: sqlite3
sqlite:
path: /var/lib/headscale/db.sqlite
dns:
magic_dns: true
base_domain: vpn.exemple.sn
Sauvegardez puis relancez avec sudo systemctl restart headscale. Une erreur de syntaxe YAML est la cause la plus frequente d’echec a ce stade : verifiez l’indentation a deux espaces, jamais de tabulation.
Etape 5 : exposer Headscale derriere Caddy avec TLS automatique
Headscale ecoute en HTTP sur le port 8080. Pour TLS, le plus simple est de placer Caddy en reverse proxy. Caddy obtient et renouvelle automatiquement le certificat Let’s Encrypt sans configuration supplementaire.
# /etc/caddy/Caddyfile
vpn.exemple.sn {
reverse_proxy localhost:8080
}
Lancez sudo systemctl reload caddy puis testez avec curl -I https://vpn.exemple.sn. Vous devez recevoir un code 200 et un en-tete server: Caddy. Si le certificat echoue, verifiez que les ports 80 et 443 sont ouverts dans le pare-feu UFW ou dans le panneau Hostinger.
Etape 6 : creer un utilisateur et generer une cle de pre-authentification
Headscale organise les noeuds par utilisateur (au sens namespace, pas authentification). Creez un utilisateur pour l’equipe technique et generez une cle reutilisable.
sudo headscale users create equipe-dakar
sudo headscale --user equipe-dakar preauthkeys create --reusable --expiration 24h
# Sortie attendue : un token de 64 caracteres hex
Copiez ce token : il sera utilise sur chaque appareil pour rejoindre le reseau sans interaction manuelle. La duree de 24h est un bon compromis entre securite et confort de deploiement sur le terrain.
Etape 7 : connecter un client Linux au reseau
Sur un poste Ubuntu, installez le client Tailscale officiel (qui parle au serveur Headscale sans modification) puis enregistrez-le avec votre token.
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --login-server https://vpn.exemple.sn --authkey VOTRE_TOKEN
tailscale status
# Sortie attendue : la liste des noeuds avec leur IP 100.64.x.x
Si tailscale status renvoie not logged in, relancez tailscale up en verifiant l’URL du login-server. C’est l’erreur la plus commune : oublier le https:// ou laisser un slash final.
Etape 8 : ajouter un smartphone Android
Sur Android, l’application Tailscale ne permet pas par defaut de specifier un serveur custom. Solution : activer l’option developpeur en tapant 7 fois sur la version dans les parametres de l’app, puis renseigner l’URL https://vpn.exemple.sn. Connectez-vous via le QR code genere cote serveur.
sudo headscale --user equipe-dakar nodes register --key NODEKEY
# La cle NODEKEY est affichee par l'app au moment de la connexion
Une fois enregistre, le telephone apparait dans tailscale status avec une IP de la plage 100.64.0.0/10. Le tunnel WireGuard est etabli, vous pouvez ssh-er sur le serveur depuis votre telephone meme via une connexion 4G Orange ou Free.
Etape 9 : ecrire des regles ACL pour cloisonner les acces
Par defaut, Headscale autorise tous les noeuds a communiquer entre eux. Pour une entreprise, ce n’est pas acceptable : un poste developpeur ne doit pas atteindre la base de donnees de production. Ecrivez un fichier ACL au format HuJSON.
// /etc/headscale/acl.hujson
{
"groups": {
"group:devs": ["alice@", "bob@"],
"group:ops": ["carol@"]
},
"acls": [
{ "action": "accept", "src": ["group:ops"], "dst": ["*:*"] },
{ "action": "accept", "src": ["group:devs"], "dst": ["100.64.0.10:22,80,443"] }
]
}
Activez le fichier dans config.yaml via policy.path: /etc/headscale/acl.hujson puis redemarrez. Testez ensuite depuis un poste dev qu’il ne peut pas pinger une IP non autorisee : c’est la confirmation que la segmentation est effective.
Etape 10 : sauvegarder la base SQLite et les cles
La perte de /var/lib/headscale/db.sqlite ou de la cle privee oblige a re-enrôler tous les appareils. Programmez une sauvegarde quotidienne sur un stockage externe (Backblaze B2, Wasabi, ou un VPS secondaire en Allemagne).
cat > /usr/local/bin/headscale-backup.sh <<'EOF'
#!/bin/bash
DATE=$(date +%Y%m%d)
sqlite3 /var/lib/headscale/db.sqlite ".backup /tmp/hs-$DATE.db"
rclone copy /tmp/hs-$DATE.db b2:itsc-backup/headscale/
rm /tmp/hs-$DATE.db
EOF
chmod +x /usr/local/bin/headscale-backup.sh
Ajoutez ensuite la ligne 0 3 * * * /usr/local/bin/headscale-backup.sh dans crontab -e. La commande .backup de SQLite est sure meme avec le serveur en marche, contrairement a un simple cp.
Etape 11 : surveiller la sante du service avec Prometheus
Headscale expose des metriques Prometheus sur le port 9090. Branchez un Prometheus local et un dashboard Grafana pour visualiser le nombre de noeuds connectes, le trafic relai DERP et les erreurs.
# prometheus.yml
scrape_configs:
- job_name: 'headscale'
static_configs:
- targets: ['127.0.0.1:9090']
Apres rechargement, l’onglet Targets de Prometheus doit afficher Headscale en vert. Importez ensuite le dashboard Grafana ID 18030 pour avoir une vue prete a l’emploi en moins de cinq minutes.
Etape 12 : aller plus loin avec exit nodes et subnet routers
Un exit node permet de router tout le trafic Internet d’un client a travers un noeud du reseau (utile pour acceder a un service geo-restreint). Un subnet router expose un sous-reseau local (le LAN du bureau de Dakar par exemple) aux autres membres.
# Sur le noeud qui sert de subnet router
sudo tailscale up --login-server https://vpn.exemple.sn --authkey TOKEN --advertise-routes=192.168.1.0/24
# Cote serveur Headscale
sudo headscale routes enable -r 1
Verifiez avec tailscale status --json sur un client distant que la route est bien apprise. Vous pouvez maintenant acceder a un NAS interne depuis Abidjan ou Lome comme si vous etiez physiquement dans le bureau de Dakar. Pour heberger votre code source en parallele, voyez notre guide Forgejo, et pour optimiser le frontend qui consomme ces APIs internes, le tutoriel Astro Server Islands.
Etape 13 : automatiser le renouvellement des cles d’authentification
Les preauthkeys reutilisables expirent. Pour un parc de 50 appareils sur le terrain a Thies ou Saint-Louis, generer manuellement une nouvelle cle chaque semaine est intenable. Ecrivez un petit script qui regenere le token et l’envoie via un canal securise (signal, mattermost interne).
#!/bin/bash
NEW_KEY=$(sudo headscale --user equipe-dakar preauthkeys create \
--reusable --expiration 168h --output json | jq -r '.key')
echo "Nouvelle cle valide 7 jours : $NEW_KEY" | \
mail -s "Cle Headscale hebdomadaire" admin@exemple.sn
Place dans cron tous les lundis a 6h, ce script garantit qu’une cle valide est toujours disponible sans intervention humaine. La sortie attendue est un email recu dans la boite admin contenant la nouvelle cle.
Etape 14 : tester la resilience en coupant le serveur
Une qualite peu connue de Tailscale et donc de Headscale : le tunnel direct entre noeuds continue a fonctionner meme si le serveur de coordination tombe. Validez ce comportement avant de passer en production.
# Sur le serveur Headscale
sudo systemctl stop headscale
# Sur un client deja connecte
ping 100.64.0.5
# Reponse : ok, le tunnel reste actif
sudo systemctl start headscale
Cette demonstration rassure les equipes : meme en cas de panne du VPS principal, les communications etablies entre bureaux ne sont pas interrompues. Seuls les nouveaux enrôlements echoueront tant que le service est down.