GuiderNätverk och VPNHeadscale för Tailscale

Kör din egen styrserver för Tailscale med Headscale

Headscale på Debian 13, styrservern med öppen källkod för Tailscales appar, i rootless Podman under en egen användare och med eget relä, så att ditt privata nätverk körs utan Tailscales servrar.

Testad på Headscale 0.29.4 och Tailscale 1.102 på Debian 13 (trixie) på en server hos Melonslab Uppdaterad 26 september 2026

Rekommenderad server för guiden

VC-S Micro · 2 vCPU · 8 GB Minne · 250 GB Lagring

Månadsvis, ingen bindningstid 7 dagars öppet köp

90 kr/mån

Beställ nu
På den här sidan

Det här sätter du upp

Tailscale kopplar ihop dina enheter i ett privat nätverk, ett tailnet, var de än befinner sig. Enheterna ansluter direkt till varandra, krypterat med WireGuard, och var och en får en fast adress och ett namn. En styrserver håller listan över enheter och delar ut deras nycklar. Normalt kör Tailscale Inc. den, och skickar vidare trafik åt enheter som inte når varandra direkt.

Headscale är en styrserver med öppen källkod som du kör själv. Tailscales appar fungerar med den utan ändringar: du pekar dem bara mot din server. Listan över dina enheter stannar på din server, och det gör reläet också, så ingen del av ditt tailnet går genom Tailscales servrar.

Här körs den som en container, under en egen användare som heter headscale, bakom Caddy från Podman-guiden. Bara reläets adresskontroll, STUN, är öppen direkt mot internet.

Varje steg nedan har körts på en server hos Melonslab med Debian 13:

  • Tre Tailscale 1.102-klienter på Linux gick med: två med en nyckel, och en genom inloggningslänken, som telefoner och datorer gör.
  • En klient hemma nådde en på servern direkt, och via serverns eget relä när UDP mellan dem blockerades.
  • STUN rapporterade hemmaklientens riktiga publika adress, och enhetsnamnen slogs upp med MagicDNS.
  • Headscale loggade varje klients riktiga adress, och allt startade igen av sig självt efter en omstart av servern.

Headscale använde omkring 17 MB minne. Stegen för telefoner, Windows och macOS följer Headscales egen dokumentation.

Innan du börjar

Du behöver:

  • en server uppsatt som i Podman-guiden, med Caddy igång;
  • en A-post och en AAAA-post för headscale.example.com som pekar på din server;
  • Tailscale-appen på varje enhet, version 1.80 eller nyare, från tailscale.com/download.

Exemplen använder headscale.example.com, 203.0.113.10 som din servers IPv4-adress och 2001:db8:1f::a som dess IPv6-adress. ip -brief address show eth0 visar dina. Byt ut dem genomgående.

Headscale är ett tailnet där dina enheter når varandra. Vill du i stället surfa på internet via din server, se WireGuard-guiden.

1. Öppna STUN-porten

Enheterna frågar reläet på UDP-port 3478 vilken publik adress de syns med utåt, vilket hjälper dem att ansluta direkt till varandra. Om du använder ufw som i säkerhetsguiden kör du det här som root:

ufw allow 3478/udp

coturn från samtalsguiden använder också port 3478. Kör du den, använd 3479 här och i steg 3 och 4 i stället.

2. Skapa användaren

useradd -m -s /bin/bash headscale
loginctl enable-linger headscale
machinectl shell headscale@

Allt fram till steg 5 körs som headscale.

3. Skriv konfigurationen

mkdir -p ~/headscale ~/.config/containers/systemd

Skapa ~/headscale/config.yaml:

server_url: https://headscale.example.com
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 127.0.0.1:50443
# Caddy's connections reach the container from the server's own IPv4 address.
trusted_proxies:
  - 203.0.113.10/32

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: true
    region_id: 999
    region_code: headscale
    region_name: Headscale
    stun_listen_addr: ":3478"
    private_key_path: /var/lib/headscale/derp_server_private.key
    ipv4: 203.0.113.10
    ipv6: 2001:db8:1f::a
  # Only this server relays traffic, not Tailscale's public relays.
  urls: []

database:
  type: sqlite
  sqlite:
    path: /var/lib/headscale/db.sqlite

dns:
  magic_dns: true
  base_domain: tail.example.com
  nameservers:
    global:
      - 9.9.9.9
      - 149.112.112.112
      - 2620:fe::fe
      - 2620:fe::9

Vad delarna gör:

  • trusted_proxies låter Headscale logga varje enhets riktiga adress, som Caddy skickar vidare.
  • prefixes är adressintervallen som Tailscale normalt använder i ett tailnet.
  • derp slår på Headscales relä. Det förmedlar trafik mellan enheter som inte kan ansluta direkt, och trafiken är fortfarande krypterad hela vägen. urls: [] utesluter Tailscales publika reläer, så att din server är det enda reläet. Vill du behålla deras som reserv anger du https://controlplane.tailscale.com/derpmap/default under urls i stället.
  • dns ger varje enhet ett namn, som laptop.tail.example.com. base_domain får inte vara example.com självt, eftersom serverns eget namn ligger under det, så en underdomän som tail.example.com används. Uppslagningar av andra namn går till Quad9.

4. Definiera containern

Skapa ~/.config/containers/systemd/headscale.container:

[Unit]
Description=Headscale, a Tailscale control server

[Container]
ContainerName=headscale
Image=docker.io/headscale/headscale:0.29
Exec=serve
ReadOnly=true
Tmpfs=/var/run/headscale
Volume=%h/headscale/config.yaml:/etc/headscale/config.yaml:ro
Volume=headscale-data:/var/lib/headscale
# Control and relay, reached through Caddy only
PublishPort=127.0.0.1:8091:8080
# STUN, open to the internet
PublishPort=3478:3478/udp
AutoUpdate=registry

[Service]
Restart=always

[Install]
WantedBy=default.target

Starta den:

systemctl --user daemon-reload
systemctl --user start headscale
podman logs headscale

Loggen visar stun server started at [::]:3478 och listening and serving HTTP on: 0.0.0.0:8080. En varning om att den lyssnar without TLS är normal: Caddy sköter HTTPS.

5. Sätt Caddy framför

Gå tillbaka till root med exit, byt till Caddys användare med machinectl shell caddy@ och lägg till de här två blocken sist i ~/Caddyfile:

http://headscale.example.com {
    # Tailscale checks for captive portals here
    handle /generate_204 {
        respond 204
    }
    handle {
        redir https://{host}{uri}
    }
}

headscale.example.com {
    reverse_proxy 127.0.0.1:8091 {
        header_up True-Client-IP {remote_host}
        header_up X-Real-IP {remote_host}
    }
}

Det första blocket svarar på kontrollen Tailscale gör för att se om ett wifi-nätverk håller tillbaka trafik tills du loggar in, och skickar allt annat till HTTPS. De två header_up-raderna ersätter den adress en enhet själv uppger med den adress Caddy ser, så att ingen kan förfalska sin adress i Headscales logg.

Starta om Caddy med systemctl --user restart caddy, och kontrollera från valfri dator:

curl https://headscale.example.com/health

Svaret är {"status":"pass"}.

6. Skapa en användare

Gå tillbaka till root med exit, sedan till machinectl shell headscale@. Varje enhet hör till en användare. Skapa en, och lista användarna för att se dess id:

podman exec headscale headscale users create anna
podman exec headscale headscale users list

7. Anslut dina enheter

Varje app öppnar en sida på din server med ett kommando att köra. Kör det som användaren headscale, med USERNAME utbytt mot användaren från steg 6, här anna:

podman exec headscale headscale auth register --auth-id hskey-authreq-... --user anna

Kommandot svarar Node ... registered, och enheten går med. Så börjar du från varje app:

  • Linux: sudo tailscale up --login-server https://headscale.example.com, och öppna länken den skriver ut.
  • Windows: i PowerShell, tailscale login --login-server https://headscale.example.com.
  • macOS: tailscale login --login-server https://headscale.example.com, eller håll ned Option, klicka på Tailscale-ikonen och välj Add Account under Debug, Custom Login Server.
  • Android: öppna inställningsmenyn uppe till höger och sedan Accounts. Välj Use an alternate server i menyn med tre punkter, och ange https://headscale.example.com.
  • iPhone och iPad: tryck på kontoikonen uppe till höger och Log in…. Välj Use custom coordination server i alternativmenyn, och ange https://headscale.example.com.

https://headscale.example.com/apple och /windows visar samma steg för de systemen.

För en server, eller en enhet utan webbläsare, skapar du en nyckel i stället. --user tar id:t från users list:

podman exec headscale headscale preauthkeys create --user 1

Nyckeln börjar med hskey- och fungerar en gång inom en timme. På enheten:

sudo tailscale up --login-server https://headscale.example.com --authkey hskey-...

8. Kontrollera det

På valfri ansluten enhet:

tailscale status
tailscale ping laptop
tailscale netcheck

tailscale status listar varje enhet med dess adress. tailscale ping svarar pong from laptop (100.64.0.1) via 203.0.113.10:41641 eller liknande för en direkt anslutning, eller via DERP(headscale) när trafiken går via ditt relä. tailscale netcheck visar Nearest DERP: Headscale, och din publika IPv4-adress under IPv4, som STUN såg den.

Enheterna når varandra på namn, laptop.tail.example.com, så länge appen får ställa in DNS, vilket den får som standard. Alla enheter i ditt tailnet kan nå varandra, vilket passar en person eller en familj. För att begränsa vem som når vad skriver du en policy, som beskrivs i Headscales dokumentation.

9. Håll den uppdaterad

Som headscale slår du på Podmans dagliga uppdateringar:

systemctl --user enable --now podman-auto-update.timer

Med taggen 0.29 får du alla buggfixar inom Headscale 0.29. Headscale måste uppgraderas en minor-version i taget, från 0.29 till 0.30 och så vidare, utan att du hoppar över någon. Läs versionsanteckningarna före varje uppgradering, säkerhetskopiera som i steg 10 och ändra sedan taggen.

10. Säkerhetskopiera

Som headscale:

mkdir -p ~/backup
systemctl --user stop headscale
podman volume export headscale-data --output ~/backup/headscale-data.tar
cp ~/headscale/config.yaml ~/backup/
systemctl --user start headscale

Det sparar databasen, med alla användare och enheter, och serverns privata nycklar. Kopiera sedan ~/backup till en annan maskin och förvara den säkert.

Felsökning

Headscale stannar vid start med server_url cannot be part of base_domain. base_domain i steg 3 innehåller serverns eget namn. Använd en underdomän, som tail.example.com.

tailscale netcheck visar UDP: false, eller ingen Nearest DERP. STUN-porten går inte att nå. Kontrollera ufw-regeln från steg 1, och att porten är densamma i steg 1, 3 och 4.

En enhet förblir offline efter inloggningssidan. Kommandot auth register har inte körts, eller kördes med en användare som inte finns. podman exec headscale headscale nodes list visar varje registrerad enhet.

Kör det på din egen server

VC-S Micro

90 kr/mån

vCPU
2
Minne
8 GB
Lagring
250 GB
Trafik
10 TB
Standard
HDD · RAID 10
  • Full root-åtkomst
  • Nativt /64 IPv6
  • RAID-skyddad lagring
  • Malmö, Sverige
  • Månadsvis, ingen bindningstid
  • 7 dagars öppet köp
Alla guider