SSD Nodes Learn Hosting plans →
Gidsen Matt ConnorDoor Matt Connor · Bijgewerkt 2026-08-28

Headscale installeren als eigen Tailscale control server

Beheer uw eigen Tailscale control server op een VPS met Headscale. Installeer het .deb pakket, configureer de server_url voor de start en koppel direct uw eerste node aan.

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, July 30, 2026.

Wat headscale is

Headscale is een zelfgehoste implementatie van de Tailscale-control server, waardoor de machine die uw privénetwerk coördineert een VPS is die u zelf beheert. Het is een communityproject en wordt niet beheerd door Tailscale Inc. Elke machine draait nog steeds de officiële tailscale-client, die met één vlag naar uw server wordt verwezen: --login-server.

De control server is het onderdeel dat weet wie er bij het netwerk hoort. Deze wijst elk knooppunt een adres toe uit 100.64.0.0/10, distribueert publieke sleutels en vertelt knooppunten waar ze elkaar kunnen vinden. De tunnels blijven WireGuard-tunnels, die van knooppunt naar knooppunt worden opgebouwd. Verkeer tussen twee van uw machines loopt niet via de headscale-server, tenzij er geen direct pad kan worden opgebouwd en de knooppunten terugvallen op een relay. Het zelf uitvoeren van deze coördinerende rol verandert wie deze rol beheert, maar niet wat de server kan doen. Het is daarom de moeite waard om te begrijpen wat een control server in dit model wel en niet kan bereiken voordat u deze overstap louter als een beveiligingswinst beschouwt.

Headscale bedient één tailnet (één Tailscale-netwerk) per instantie, wat volgens het project geschikt is voor persoonlijk gebruik of een kleine organisatie. Bij drie of vier machines is een standaard WireGuard VPN op een eigen VPS minder software om te draaien en minder complex om te onderhouden. Headscale is nuttig wanneer u niet langer voor elke nieuwe laptop handmatig een [Peer]-blok wilt schrijven. Kosten zijn vaak de reden waarom mensen op zoek gaan naar alternatieven; het is daarom de moeite waard om te lezen wat het gratis gehoste abonnement daadwerkelijk dekt voordat u een eigen server opzet, aangezien een handvol persoonlijke apparaten daar meestal binnen past. Als u dat limiet al heeft overschreden, vergelijk dit dan met de kosten van de betaalde abonnementen, die per gebruiker in plaats van per apparaat worden berekend, omdat een huishouden op één account goedkoop kan blijven lang nadat het aantal apparaten niet meer relevant is. Als u een zelfgehost control plane wilt, maar liever uw eigen client en een webinterface voor het beheer van peers heeft in plaats van een directe vervanging voor Tailscale, dan is NetBird op een enkele VPS het overwegen waard. Voor een bredere vergelijking tussen de twee modellen, zie hoe WireGuard en Tailscale van elkaar verschillen.

Vereisten voor de installatie

  • Een VPS met Ubuntu 24.04, een publiek IPv4-adres en sudo-toegang. Als de server nieuw is, doorloop dan eerst de eerste tien minuten op een nieuwe VPS.
  • Een DNS A-record dat naar dat adres wijst. Deze handleiding gebruikt headscale.example.com.
  • Een tweede domein of subdomein voor MagicDNS. Deze handleiding gebruikt tailnet.example.net. Dit mag niet hetzelfde domein zijn als het domein in server_url.
  • Eén clientmachine om verbinding te maken, draaiend op Linux, macOS, Windows, Android of iOS.

Headscale installeren via het officiële .deb-bestand

Het project publiceert .deb-pakketten op de GitHub-releasespagina. Sinds juli 2026 is de huidige release 0.29.3. Controleer eerst uw architectuur, aangezien deze in de bestandsnaam is opgenomen.

sudo apt update
sudo apt install -y wget
dpkg --print-architecture

Dit commando toont amd64 op een standaard x86 VPS en arm64 op een Ampere- of Graviton-gebaseerd abonnement. Plaats het resultaat in de onderstaande variabele.

HEADSCALE_VERSION="0.29.3"
HEADSCALE_ARCH="amd64"
wget --output-document=headscale.deb \\
  "https://github.com/juanfont/headscale/releases/download/v${HEADSCALE_VERSION}/headscale_${HEADSCALE_VERSION}_linux_${HEADSCALE_ARCH}.deb"
sudo apt install -y ./headscale.deb
headscale version

De ./ voor de bestandsnaam is vereist. Zonder dit zoekt apt naar een pakket genaamd headscale.deb in uw repositories, wat resulteert in een foutmelding.

Het pakket maakt een headscale-systeemgebruiker aan, schrijft een standaard /etc/headscale/config.yaml en installeert een systemd-unit. De service wordt niet automatisch gestart, wat de juiste volgorde is. De meegeleverde configuratie wijst server_url naar http://127.0.0.1:8080; dit is geen adres dat uw clients kunnen bereiken. Een service die nu wordt gestart, zou dus onjuist geconfigureerd zijn, zelfs als deze zou opstarten. Het uitvoeren van sudo systemctl is-active headscale op dit punt toont inactive. Dit is het verwachte gedrag en geen fout.

Configureer server_url voordat u de service start

Bewerk /etc/headscale/config.yaml met sudo nano /etc/headscale/config.yaml, of pas dezelfde drie wijzigingen toe met sed. Bewaar een kopie van het origineel; het bestand is lang, bevat uitgebreide commentaren en is de beste referentie voor de overige instellingen.

sudo cp /etc/headscale/config.yaml /etc/headscale/config.yaml.orig
sudo sed -i 's|^server_url:.*|server_url: https://headscale.example.com|' /etc/headscale/config.yaml
sudo sed -i 's|^listen_addr:.*|listen_addr: 127.0.0.1:8080|' /etc/headscale/config.yaml
sudo sed -i 's|^  base_domain:.*|  base_domain: tailnet.example.net|' /etc/headscale/config.yaml
sudo grep -E '^(server_url|listen_addr):|^  base_domain:' /etc/headscale/config.yaml

server_url is het adres dat headscale in elke clientregistratie schrijft. Clients maken vanaf dat moment altijd verbinding met die specifieke string, dus het moet de publieke naam zijn met https:// ervoor, nooit 127.0.0.1.

listen_addr is de locatie waar het proces aan bindt. Laat dit op loopback staan. Een reverse proxy op dezelfde machine handelt de TLS (transport layer security) af en stuurt het verkeer door, waardoor niets buiten de server poort 8080 hoeft te bereiken.

base_domain is het MagicDNS-achtervoegsel, het domein waaronder uw nodes namen krijgen. Dit moet een fully qualified domain name zijn zonder afsluitende punt, en het moet een ander domein zijn dan dat in server_url, omdat de twee naamruimten anders met elkaar in conflict komen.

Wijzig de database-sectie niet. De standaardinstelling is SQLite op /var/lib/headscale/db.sqlite, in een map die door het pakket is aangemaakt en wordt beheerd. SQLite is voldoende voor een tailnet van deze omvang.

Start headscale en controleer of het actief is

sudo systemctl enable --now headscale
sudo systemctl is-active headscale
curl -sS -o /dev/null -w '%{http_code}\\n' http://127.0.0.1:8080/health

is-active toont active en curl toont 200. enable --now voert beide taken uit: het start de service en markeert deze om na een herstart automatisch te worden geladen.

Als is-active de melding failed geeft, lees dan het logboek met sudo journalctl -u headscale -n 50 --no-pager. Een fout in dit stadium wordt bijna altijd veroorzaakt door het configuratiebestand. headscale parseert het volledige bestand voordat het een socket opent; een onjuiste inspringing of een onbekende sleutel stopt het proces nog voordat er een poort wordt geopend. Corrigeer het bestand en voer daarna sudo systemctl restart headscale uit. Elke latere configuratiewijziging vereist dezelfde herstart. Clients maken daarna automatisch opnieuw verbinding. Als systemd-units nieuw voor u zijn, behandelt het draaien van eigen services en timers met systemd de commando's die hier worden gebruikt.

Controleer de statusbestanden terwijl u in de shell bent:

stat -c '%U %n' /var/lib/headscale/db.sqlite /var/lib/headscale/noise_private.key

Beide regels beginnen met headscale, de gebruiker zonder privileges die door het pakket is aangemaakt. noise_private.key is de identiteit van de server voor zijn clients. Bewaar dit bestand. Als u het verwijdert, genereert headscale een nieuwe identiteit en moet elk knooppunt zich opnieuw registreren.

TLS voor headscale plaatsen

Clients moeten server_url bereiken via HTTPS. Caddy is de kortste route, omdat deze zelfstandig certificaten aanvraagt en vernieuwt.

sudo apt install -y caddy

Vervang /etc/caddy/Caddyfile door het blok uit de headscale-documentatie:

headscale.example.com {
    reverse_proxy 127.0.0.1:8080 {
        header_up True-Client-IP {remote_host}
        header_up X-Real-IP {remote_host}
    }
}
sudo caddy validate --adapter caddyfile --config /etc/caddy/Caddyfile
sudo systemctl restart caddy
sudo systemctl is-active caddy

validate geeft adapted config to JSON weer wanneer het bestand correct wordt geparseerd. Een waarschuwing dat het bestand niet is geformatteerd is cosmetisch. Vanaf uw laptop zou curl -sS -o /dev/null -w '%{http_code}\\n' https://headscale.example.com/health ook 200 moeten weergeven. Deze enkele controle bewijst dat DNS, de firewall, het certificaat en de proxy correct samenwerken.

Hier is het detail over de proxy dat veel gebruikers een avond kost. De Tailscale-control-verbinding is een HTTP-upgrade, deze wordt gestart met een POST in plaats van een GET, en de waarde van de Upgrade-header is tailscale-control-protocol. Caddy geeft dit door zonder extra configuratie. nginx doet dit niet, dus een nginx-frontend heeft de upgrade-map nodig:

map $http_upgrade $connection_upgrade {
    default keep-alive;
    ''      close;
}

server {
    listen 443 ssl;
    server_name headscale.example.com;
    location / {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_buffering off;
        proxy_pass http://127.0.0.1:8080;
    }
}

Laat deze regels weg en gewone verzoeken slagen nog steeds; dit is de reden waarom /health een 200-statuscode teruggeeft en alles in orde lijkt, terwijl de langdurige control-verbinding nooit tot stand komt en uw nodes wel registreren maar vervolgens offline blijven. Als u voor de nginx-route kiest, behandelt Certbot op Ubuntu 24.04 met nginx het gedeelte over certificaten.

Welke poorten moeten worden geopend in UFW

sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose

Poort 443 verwerkt alle clientcommunicatie. Poort 80 is uitsluitend aanwezig voor de ACME (automatic certificate management environment) HTTP-challenge en de redirect naar HTTPS; Caddy heeft deze poort nodig om überhaupt een certificaat te kunnen verkrijgen.

Poort 8080 blijft gesloten. listen_addr is 127.0.0.1:8080, waardoor de proxy headscale bereikt via de loopback-interface en er geen firewallregel aan te pas komt. Het openstellen van poort 8080 naar het internet geeft clients een onversleuteld controlekanaal en levert geen voordelen op. Houd er rekening mee dat de meeste providers een tweede firewall in hun configuratiescherm hanteren, los van UFW. Een poort kan dus op de server openstaan, terwijl deze aan de rand van het netwerk nog steeds geblokkeerd is. Basisprincipes van de UFW-firewall op een VPS behandelt de syntaxis van de regels in meer detail.

Een gebruiker en een preauth-key aanmaken

sudo headscale users create alice
sudo headscale users list

Het commando headscale is een client. Deze communiceert met de actieve daemon via de unix-socket op /var/run/headscale/headscale.sock, welke de modus 0770 heeft en eigendom is van de groep headscale. Hieruit volgen twee zaken. Het commando faalt wanneer de service is gestopt, wat de reden is waarom de volgorde in deze handleiding van belang is, en het vereist sudo tenzij u uw eigen account toevoegt aan de groep headscale.

users list toont een ID naast elke naam. U heeft dat nummer nodig, omdat het key-commando een numeriek gebruikers-ID vereist en geen naam.

sudo headscale preauthkeys create --user 1 --expiration 24h

De key wordt eenmalig getoond. Kopieer deze nu. Een preauth-key is voor eenmalig gebruik en is standaard één uur geldig, tenzij u anders aangeeft; daarom is --expiration 24h nuttig om in te stellen terwijl u nog aan het testen bent. Voeg --reusable toe voor een key die meerdere machines toevoegt, en behandel deze als een wachtwoord, aangezien iedereen die erover beschikt lid kan worden van uw netwerk.

Verbind uw eerste client met --login-server

Op de machine die u wilt toevoegen:

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up --login-server https://headscale.example.com --auth-key 'hskey-auth-PASTE-YOUR-KEY-HERE'
tailscale status
tailscale ip -4

tailscale ip -4 toont het adres dat headscale heeft toegewezen, bijvoorbeeld 100.64.0.1. Terug op de server toont sudo headscale nodes list de node met het bijbehorende ID, de gebruiker en de online-status.

De waarde van --login-server moet exact overeenkomen met server_url, inclusief het schema en zonder afsluitende slash. Deze worden als strings vergeleken; een mismatch betekent dat de client zich registreert bij het ene adres en vervolgens de instructie krijgt om met een ander adres te communiceren.

Een machine die voorheen was aangemeld bij de gehoste dienst van Tailscale behoudt die login. Voer eerst sudo tailscale logout uit op die machine en voer daarna tailscale up uit met --login-server.

Als u --auth-key weglaat, print de client in plaats daarvan een URL. Open deze URL; de pagina toont de identificatiecode voor die registratiepoging, die u vervolgens op de server goedkeurt:

sudo headscale auth register --user alice --auth-id PASTE-THE-ID-FROM-THE-PAGE

Dat formulier is handiger voor uw eigen laptop. Preauth-keys zijn beter voor gescripte processen, aangezien er geen menselijke tussenkomst vereist is. Zodra de VPS zelf een node is, kan deze ook het internetverkeer van uw andere machines afhandelen. Dit is de exit node configuratie, met als enige verschil dat u de geadverteerde route op de server goedkeurt met het headscale commando in plaats van in een gehoste beheerconsole. Als u toegang wilt tot een privénetwerk achter die VPS in plaats van een uitgang naar het internet, dekt dezelfde goedkeuringsstap het adverteren van dat subnet naar de rest van uw tailnet. Het publiceren van één applicatie vanaf een node, in plaats van volledige netwerken erdoorheen te routeren, is een andere taak. serve en funnel zijn de twee manieren om dit te doen, hoewel beide leunen op de eigen certificaat- en ingress-mechanismen van Tailscale. Beschouw deze daarom als functies van een gehost tailnet in plaats van als functionaliteit die headscale standaard biedt.

DERP, en wat verkeer doorstuurt wanneer een direct pad faalt

DERP (designated encrypted relay for packets) is het fallback-pad. Wanneer twee nodes geen directe WireGuard-verbinding kunnen openen, meestal omdat beide achter strikte NAT (network address translation) zitten, sturen ze pakketten via een relay. De relay bezit geen sleutels, dus kan deze uw verkeer niet inzien. De relay ziet wel welke nodes met elkaar communiceren en hoeveel data er wordt verplaatst.

Wees duidelijk over wat de standaardconfiguratie doet. Headscale wordt geleverd met verwijzingen naar https://controlplane.tailscale.com/derpmap/default met auto_update_enabled: true en update_frequency: 3h, dus uw control plane is van u, terwijl uw relays van Tailscale zijn. Voor de meeste mensen is dat een acceptabele afweging. Als dat niet zo is, draai dan uw eigen relay.

Om uw eigen relay te draaien, stelt u enabled: true in onder derp.server in config.yaml, start u headscale opnieuw op en opent u de STUN (session traversal utilities for NAT) poort met sudo ufw allow 3478/udp. Het configuratiebestand vermeldt de vereiste duidelijk: de server_url moet https gebruiken, omdat DERP TLS vereist. Het leegmaken van de derp.urls lijst verwijdert de relays van Tailscale uit de map. Als u dat doet zonder een werkende ingebouwde relay, kunnen paren nodes die geen directe verbinding kunnen maken, helemaal geen verbinding meer maken.

Vanaf een client toont tailscale netcheck de latentie naar elke relayregio die deze kent. tailscale status markeert elke peer als direct met een adres of als relay met een regiocode. Een peer die op relay blijft hangen, wijst op een NAT-probleem en niet op een probleem met headscale. Een peer die direct is maar nog steeds traag blijft, is weer een ander probleem. De gebruikelijke oorzaak is daar MTU en niet de tunnel zelf.

Waarom wordt een node als offline weergegeven?

De proxy blokkeert de upgrade. Dit is een veelvoorkomend probleem. Het kenmerk is dat de rest van de omgeving gezond lijkt: /health geeft een 200-statuscode terug, headscale nodes list toont de node, maar de node komt nooit online. De controleverbinding is een POST-verzoek met Upgrade: tailscale-control-protocol. Een proxy die dit niet doorstuurt, verbreekt het enige kanaal dat de status van de node rapporteert. Vergelijk uw nginx-configuratie met het bovenstaande map-blok of schakel over naar Caddy om de proxy uit te sluiten als oorzaak.

server_url is gewijzigd nadat de nodes zich hebben geregistreerd. Nodes blijven verbinding maken met de waarde die zij bij registratie hebben ontvangen. Als u deze waarde heeft aangepast, voer dan sudo tailscale up --login-server https://headscale.example.com --force-reauth uit op elke node.

De client draait niet. Controleer op de node sudo systemctl is-active tailscaled en sudo journalctl -u tailscaled -n 50 --no-pager. Een client die uw domein niet kan resolven of bereiken, logt daar de pogingen tot herverbinding.

De sleutel is verlopen. Dit wordt in de volgende sectie behandeld.

Om de serverzijde te monitoren terwijl u test, voert u sudo journalctl -u headscale -f uit op de VPS en herstart u tailscaled op de client. Een node die headscale bereikt, genereert direct logregels. Stilte betekent dat het verzoek niet aankomt. Controleer daarom eerst DNS, de firewall en de proxy voordat u naar headscale kijkt.

Sleutelverloop en de node die na enkele weken stopt met werken

Er zijn twee afzonderlijke vervaldata; deze door elkaar halen kost onnodig tijd.

Preauth-sleutels verlopen volgens ontwerp snel. De standaardinstelling is één uur en éénmalig gebruik. Als tailscale up de sleutel weigert, genereer dan een nieuwe op de server in plaats van instellingen op de client aan te passen.

Node-sleutels zijn de langdurige variant. De sectie node van config.yaml stelt expiry: 0 in, en 0 betekent geen standaard vervaldatum: een geregistreerde node blijft geldig totdat u deze handmatig laat verlopen. Getagde nodes verlopen nooit. Stel expiry: 180d in als u wilt dat registraties na verloop van tijd vervallen, maar wees u bewust van de gevolgen: elke niet-getagde node heeft dan volgens dat schema sudo tailscale up --login-server https://headscale.example.com --force-reauth nodig, en een headless server waar niemand opnieuw op inlogt, zal automatisch uit het netwerk verdwijnen.

Voer dit handmatig uit wanneer iemand een laptop verliest. sudo headscale nodes list toont u het ID, vervolgens logt sudo headscale nodes expire -i 3 die node uit, en sudo headscale nodes delete -i 3 verwijdert deze volledig uit het netwerk.

Backups en upgrades

/var/lib/headscale en /etc/headscale vormen samen de volledige server. Stop de service voordat u deze kopieert, omdat SQLite schrijfacties in uitvoering kan hebben en een database die onder belasting wordt gekopieerd, inconsistent kan zijn.

sudo systemctl stop headscale
sudo tar czf /root/headscale-state.tgz -C /var/lib headscale
sudo tar czf /root/headscale-config.tgz -C /etc headscale
sudo systemctl start headscale
sudo chmod 600 /root/headscale-*.tgz

Verplaats beide bestanden van de server af. Ze bevatten de privésleutels en elke registratie, dus ze vereisen dezelfde zorg als de server zelf. restic backups vanaf een VPS beschrijft hoe u dit volgens een schema en versleuteld uitvoert.

Upgrades herhalen de installatie: download de nieuwe .deb, sudo apt install ./headscale.deb, start vervolgens opnieuw op en voer de is-active en /health controles opnieuw uit. Sinds 0.29 is het upgradepad strikt. Het overslaan van een minor-versie is geblokkeerd, evenals het downgraden naar een oudere minor-versie. Upgrade één minor-versie per keer, maak voor elke stap een backup en lees eerst de release notes van die versie, omdat in die release het gedrag van het ACL-beleid is gewijzigd en diverse configuratiesleutels zijn verplaatst.

FAQ

Waarom start headscale niet direct na de installatie van het .deb-pakket?

Het pakket installeert de unit, maar laat de service gestopt. De standaard /etc/headscale/config.yaml is een sjabloon en geen werkende configuratie. Bewerk eerst server_url, listen_addr en base_domain, voer daarna sudo systemctl enable --now headscale uit en bevestig met sudo systemctl is-active headscale. Als het nog steeds niet lukt, benoemt sudo journalctl -u headscale -n 50 --no-pager het probleem. In dit stadium is het bijna altijd een YAML-fout, omdat headscale het volledige bestand parseert voordat het een poort bindt.

Moet ik nog steeds de normale Tailscale-client op mijn machines installeren?

Ja. Headscale vervangt alleen de control server. Elke node draait de officiële client van Tailscale en u verwijst deze naar uw server met sudo tailscale up --login-server https://headscale.example.com. Die vlag bestaat in de standaard client, dus er hoeft niets gepatcht of opnieuw gebouwd te worden.

Gaat mijn verkeer via de headscale-server?

Meestal niet. Headscale coördineert het netwerk en deelt sleutels en adressen uit, terwijl het datapad WireGuard is, direct tussen uw nodes. Verkeer neemt alleen een omweg wanneer twee nodes elkaar niet direct kunnen bereiken en terugvallen op een DERP-relay. Met de meegeleverde configuratie zijn die relays de publieke servers van Tailscale. Voer tailscale status uit op een node om te zien of een specifieke peer direct is of op een relay zit.

Waarom blijft mijn node offline nadat deze is geregistreerd?

Een node die wel in headscale nodes list verschijnt maar niet online komt, is meestal de verbinding met de control server via de reverse proxy kwijtgeraakt. Die verbinding is een HTTP-upgrade die als POST wordt verzonden met de header Upgrade: tailscale-control-protocol. Nginx blokkeert dit tenzij u het map $http_upgrade $connection_upgrade-blok en de bijbehorende proxy_set_header-regels toevoegt. Caddy stuurt dit zonder extra configuratie door, wat het een snelle manier maakt om te testen of de proxy de oorzaak is.

Heb ik een domeinnaam en TLS nodig voor headscale?

In de praktijk wel. Clients verbinden met de string die u in server_url plaatst, certificaten worden uitgegeven voor namen en niet voor losse IP-adressen, en het configuratiebestand stelt dat DERP TLS vereist. Een domein plus Caddy kost ongeveer vijf minuten en levert een HTTPS-eindpunt op dat zichzelf vernieuwt. De control server draaien via onversleuteld HTTP betekent dat elk gesprek van een client met de server ongecodeerd over het internet gaat.