Jak zainstalować Headscale na własnym serwerze VPS
Wdrożenie własnego serwera sterującego dla sieci Tailscale. Instrukcja instalacji pakietu .deb, konfiguracji parametru server_url oraz poprawnego dodania pierwszego węzła.
Czym jest headscale
Headscale to samodzielnie hostowana implementacja serwera sterującego Tailscale, dzięki czemu maszyną koordynującą sieć prywatną jest posiadany przez użytkownika VPS. Jest to projekt społecznościowy, niezarządzany przez Tailscale Inc. Każda maszyna nadal uruchamia oficjalnego klienta tailscale, skierowanego na własny serwer za pomocą jednej flagi, --login-server.
Serwer sterujący to komponent, który posiada wiedzę o tym, kto należy do sieci. Przydziela każdemu węzłowi adres z puli 100.64.0.0/10, dystrybuuje klucze publiczne i informuje węzły, gdzie mogą się nawzajem odnaleźć. Tunele pozostają oparte na WireGuard i są budowane bezpośrednio między węzłami. Ruch między dwiema maszynami nie przechodzi przez serwer headscale, chyba że nie można ustanowić bezpośredniego połączenia i węzły muszą skorzystać z przekaźnika (relay). Samodzielne pełnienie roli koordynatora zmienia jedynie podmiot sprawujący kontrolę, a nie zakres jego możliwości, dlatego warto zrozumieć do czego serwer sterujący ma dostęp w tym modelu, zanim potraktuje się tę zmianę jako samodzielny wzrost bezpieczeństwa.
Headscale obsługuje jedną sieć tailnet (jedną sieć Tailscale) na instancję, co według twórców projektu jest rozwiązaniem odpowiednim do użytku osobistego lub dla małych organizacji. W przypadku trzech lub czterech maszyn zwykły VPN WireGuard na własnym VPS oznacza mniej oprogramowania do utrzymania i mniej potencjalnych punktów awarii. Headscale staje się opłacalne, gdy użytkownik nie chce ręcznie tworzyć bloków [Peer] dla każdego nowego laptopa. Często to koszty skłaniają użytkowników do poszukiwań, dlatego warto sprawdzić co faktycznie obejmuje darmowy plan hostowany, zanim zdecyduje się na własny serwer, ponieważ kilka prywatnych maszyn zazwyczaj mieści się w jego limitach. Jeśli ten limit został już przekroczony, należy porównać koszty z cennikiem planów płatnych, które są rozliczane za użytkownika, a nie za urządzenie, ponieważ gospodarstwo domowe korzystające z jednego konta może utrzymać niskie koszty nawet po przekroczeniu dużej liczby urządzeń. Jeśli celem jest samodzielnie hostowana płaszczyzna sterowania, ale preferowany jest własny klient i interfejs webowy do zarządzania węzłami zamiast bezpośredniego zamiennika Tailscale, NetBird na pojedynczym VPS jest alternatywą wartą rozważenia. Szersze porównanie obu modeli znajduje się w artykule jak różnią się WireGuard i Tailscale.
Wymagania wstępne przed instalacją
- Serwer VPS z systemem Ubuntu 24.04, publicznym adresem IPv4 oraz dostępem sudo. W przypadku nowego serwera należy najpierw wykonać kroki opisane w pierwsze dziesięć minut na nowym serwerze VPS.
- Rekord DNS typu A wskazujący na ten adres. W tym przewodniku użyto
headscale.example.com. - Druga domena lub subdomena dla MagicDNS. W tym przewodniku użyto
tailnet.example.net. Nie może to być ta sama domena, która została użyta wserver_url. - Jedno urządzenie klienckie do połączenia z siecią, działające pod kontrolą systemu Linux, macOS, Windows, Android lub iOS.
Instalacja headscale z oficjalnego pakietu .deb
Projekt publikuje pakiety .deb na stronie wydań w serwisie GitHub. Według stanu na październik 2026 r. aktualne wydanie to 0.29.4. Najpierw należy sprawdzić architekturę systemu, ponieważ jest ona zawarta w nazwie pliku.
sudo apt update
sudo apt install -y wget
dpkg --print-architecturePolecenie to wyświetla amd64 na standardowym serwerze VPS x86 oraz arm64 w przypadku planów typu Ampere lub Graviton. Wynik należy podstawić do poniższej zmiennej.
HEADSCALE_VERSION="0.29.4"
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 versionZnak ./ przed nazwą pliku jest wymagany. Bez niego apt szuka pakietu o nazwie headscale.deb w repozytoriach, co kończy się błędem.
Pakiet tworzy użytkownika systemowego headscale, zapisuje domyślny plik /etc/headscale/config.yaml, instaluje jednostkę systemd, a następnie włącza i uruchamia usługę. W momencie zakończenia działania apt usługa headscale już pracuje, a sudo systemctl is-active headscale wyświetla active. Działa ona jednak na domyślnej konfiguracji, która wskazuje server_url na http://127.0.0.1:8080; nie jest to adres dostępny dla żadnego z klientów. Żadne porty nie są jeszcze wystawione, ponieważ domyślny listen_addr również jest powiązany z interfejsem loopback. Nie należy przyłączać żadnych klientów przed ustawieniem server_url i zrestartowaniem usługi, co zostanie wykonane w dwóch kolejnych krokach.
Konfiguracja server_url przed dołączeniem pierwszego klienta
Edytuj /etc/headscale/config.yaml za pomocą sudo nano /etc/headscale/config.yaml lub wprowadź te same trzy zmiany przy użyciu sed. Zachowaj kopię oryginalnego pliku, ponieważ jest on długi, zawiera liczne komentarze i stanowi najlepsze źródło informacji o pozostałych ustawieniach.
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.yamlserver_url to adres, który Headscale wpisuje w rejestracji każdego klienta. Klienci będą łączyć się z tym ciągiem znaków na stałe, dlatego powinien to być publiczny adres z przedrostkiem https://, a nigdy 127.0.0.1. Dokumentacja Headscale dopuszcza użycie zwykłego HTTP lub HTTPS na porcie innym niż 443, jednak w środowiskach produkcyjnych zdecydowanie zaleca się HTTPS na porcie 443 i na takim założeniu opiera się ten przewodnik.
listen_addr to adres, na którym nasłuchuje proces. Pozostaw go na interfejsie loopback. Reverse proxy działające na tym samym serwerze zajmuje się terminacją TLS (transport layer security) i przekazuje ruch dalej, więc żaden zewnętrzny host nie musi mieć bezpośredniego dostępu do portu 8080.
base_domain to sufiks MagicDNS, czyli domena, w której węzły otrzymują swoje nazwy. Musi to być w pełni kwalifikowana nazwa domeny (FQDN) bez kropki na końcu i musi różnić się od domeny użytej w server_url, w przeciwnym razie przestrzenie nazw będą ze sobą kolidować.
Sekcję bazy danych pozostaw bez zmian. Domyślnie używany jest SQLite w /var/lib/headscale/db.sqlite, w katalogu utworzonym i zarządzanym przez pakiet, co jest wystarczające dla tailnetu tej wielkości.
Restart headscale i weryfikacja działania
sudo systemctl restart headscale
sudo systemctl is-active headscale
curl -sS -o /dev/null -w '%{http_code}\\n' http://127.0.0.1:8080/healthis-active wyświetla active, a curl zwraca 200. Restart jest krokiem, który wczytuje wprowadzone zmiany. headscale odczytuje konfigurację tylko raz, podczas uruchamiania, dlatego proces zainicjowany przez pakiet kontynuuje używanie dostarczonego server_url aż do momentu restartu. sudo systemctl enable --now headscale nie wykonuje tego zadania: pakiet już włączył i uruchomił usługę, więc to polecenie niczego nie zmienia, a stara wartość pozostaje aktywna. Uruchamianie przy starcie systemu jest już skonfigurowane, a sudo systemctl is-enabled headscale wyświetla enabled w celu potwierdzenia.
Jeśli is-active wyświetla cokolwiek innego niż active lub curl nie zwraca 200, należy sprawdzić dziennik za pomocą sudo journalctl -u headscale -n 50 --no-pager. Awaria na tym etapie niemal zawsze wynika z pliku konfiguracyjnego, ponieważ headscale analizuje cały plik przed otwarciem gniazda. Błędne wcięcie lub nieznany klucz zatrzymują proces, zanim jakakolwiek usługa zacznie nasłuchiwać. Należy poprawić plik, a następnie wykonać sudo systemctl restart headscale. Każda kolejna zmiana konfiguracji wymaga tego samego restartu. Klienci połączą się ponownie automatycznie. Jeśli jednostki systemd są nowością, uruchamianie własnych usług i timerów za pomocą systemd zawiera opis użytych tutaj poleceń.
Podczas pracy w powłoce należy sprawdzić pliki stanu:
stat -c '%U %n' /var/lib/headscale/db.sqlite /var/lib/headscale/noise_private.keyObie linie zaczynają się od headscale, czyli użytkownika bez uprawnień, utworzonego przez pakiet. noise_private.key stanowi tożsamość serwera dla jego klientów. Należy go zachować. Usunięcie tego pliku spowoduje, że headscale wygeneruje nowy, co wymusi ponowną rejestrację wszystkich węzłów.
Umieszczenie TLS przed headscale
Klienci powinni łączyć się z server_url przez HTTPS. Caddy jest najkrótszą drogą, ponieważ samodzielnie żąda certyfikatu i go odnawia.
sudo apt install -y caddyZastąp /etc/caddy/Caddyfile blokiem z dokumentacji headscale:
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 caddyvalidate wyświetla adapted config to JSON, gdy plik zostanie poprawnie przeanalizowany. Ostrzeżenie o braku formatowania ma charakter kosmetyczny. Z poziomu laptopa polecenie curl -sS -o /dev/null -w '%{http_code}\\n' https://headscale.example.com/health również powinno wyświetlić 200. Ten pojedynczy test potwierdza, że DNS, firewall, certyfikat i proxy działają poprawnie.
Oto szczegół konfiguracji proxy, który często sprawia użytkownikom trudności. Połączenie kontrolne Tailscale to upgrade HTTP, inicjowany metodą POST zamiast GET, a wartość nagłówka Upgrade wynosi tailscale-control-protocol. Caddy przekazuje to bez dodatkowej konfiguracji. nginx tego nie robi, więc frontend nginx musi jawnie przekazywać nagłówki Upgrade oraz Connection. Dokumentacja headscale rozwiązuje to za pomocą mapy upgrade:
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;
}
}Pominięcie tych linii sprawia, że zwykłe żądania nadal kończą się powodzeniem, dlatego /health zwraca kod 200 i wszystko wygląda na sprawne, podczas gdy długotrwałe połączenie kontrolne nigdy nie zostaje nawiązane, a węzły rejestrują się i pozostają w trybie offline. W przypadku wyboru nginx, Certbot na Ubuntu 24.04 z nginx opisuje kwestię certyfikatów.
Które porty otworzyć w UFW
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbosePort 443 obsługuje każde połączenie sterujące klienta z headscale. Jeśli w późniejszym czasie włączony zostanie wbudowany przekaźnik DERP, wymaga on również portu 3478/udp, opisanego poniżej. Port 80 służy do wyzwania HTTP w ramach ACME (automatic certificate management environment) oraz do przekierowania na HTTPS. Caddy może uzyskać certyfikat nawet przy zamkniętym porcie 80, ponieważ w przypadku niepowodzenia wyzwania HTTP przełącza się na wyzwanie TLS-ALPN na porcie 443. Przy otwartym porcie 80 dostępne są oba wyzwania, a zwykłe żądania http:// są przekierowywane.
Port 8080 pozostaje zamknięty. listen_addr to 127.0.0.1:8080, więc proxy łączy się z headscale przez interfejs loopback i żadna reguła zapory nie jest wymagana. Otwarcie portu 8080 w UFW niczego nie udostępni, ponieważ z zewnątrz nic na nim nie nasłuchuje, nie przynosi to również żadnych korzyści. Należy pamiętać, że większość dostawców utrzymuje drugą zaporę w swoim panelu sterowania, niezależną od UFW, więc port może być otwarty na serwerze, a mimo to zablokowany na brzegu sieci. Podstawy zapory UFW na VPS zawiera szczegółowy opis składni reguł.
Tworzenie użytkownika i klucza preauth
sudo headscale users create alice
sudo headscale users listPolecenie headscale jest klientem. Komunikuje się ono z działającym daemonem poprzez gniazdo unixowe w /var/run/headscale/headscale.sock, które posiada uprawnienia 0770 i należy do grupy headscale. Wynikają z tego dwie kwestie. Polecenie kończy się niepowodzeniem, gdy usługa jest zatrzymana, co stanowi kolejny powód, dla którego kolejność działań w tym przewodniku jest istotna. Wymaga ono również uprawnień sudo, chyba że dodasz własne konto do grupy headscale.
users list wyświetla identyfikator obok każdej nazwy. Ten numer jest niezbędny, ponieważ polecenie klucza wymaga numerycznego identyfikatora użytkownika, a nie nazwy.
sudo headscale preauthkeys create --user 1 --expiration 24hKlucz jest wyświetlany tylko raz. Skopiuj go teraz. Klucz preauth jest jednorazowy i ważny przez jedną godzinę, chyba że określisz inaczej, dlatego warto ustawić --expiration 24h podczas testów. Dodaj --reusable dla klucza, który rejestruje wiele maszyn i traktuj go jak hasło, ponieważ każdy, kto go posiada, może dołączyć do Twojej sieci.
Podłączanie pierwszego klienta za pomocą --login-server
Na maszynie, którą chcesz dołączyć:
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 -4tailscale ip -4 wyświetla adres przypisany przez headscale, na przykład 100.64.0.1. Po powrocie na serwer, sudo headscale nodes list pokazuje węzeł wraz z jego identyfikatorem, przypisanym użytkownikiem oraz stanem połączenia.
Wartość --login-server musi dokładnie odpowiadać server_url, wliczając w to schemat oraz brak ukośnika na końcu. Porównywane są one jako ciągi znaków, a niezgodność oznacza, że klient rejestruje się pod jednym adresem, a następnie otrzymuje polecenie komunikacji z innym.
Maszyna, która była wcześniej zalogowana do hostowanej usługi Tailscale, zachowuje to logowanie. Uruchom na niej najpierw sudo tailscale logout, a następnie wykonaj tailscale up z użyciem --login-server.
Jeśli pominiesz --auth-key, klient wyświetli adres URL. Otwórz go, a strona pokaże identyfikator tej próby rejestracji, który zatwierdzasz na serwerze:
sudo headscale auth register --user alice --auth-id PASTE-THE-ID-FROM-THE-PAGETen formularz jest wygodniejszy w przypadku własnego laptopa. Klucze preauth są lepsze dla wszelkich skryptów, ponieważ nie wymagają nadzoru człowieka. Gdy sam VPS stanie się węzłem, może również obsługiwać ruch internetowy innych Twoich maszyn, co opisano w konfiguracji węzła wyjściowego (exit node), z tą różnicą, że anonsowaną trasę zatwierdzasz na serwerze za pomocą polecenia headscale, a nie w hostowanej konsoli administracyjnej. Jeśli Twoim celem jest dostęp do sieci prywatnej znajdującej się za tym VPS-em, a nie wyjście do Internetu, ten sam krok zatwierdzenia dotyczy anonsowania podsieci dla reszty Twojej sieci tailnet. Publikowanie pojedynczej aplikacji z węzła, zamiast trasowania przez niego całych sieci, to inne zadanie, a serve oraz funnel to dwa sposoby jego realizacji. Obie te metody opierają się na własnym mechanizmie certyfikatów i ruchu przychodzącego Tailscale, więc należy traktować je jako funkcje hostowanego tailnetu, a nie coś, co zapewnia bezpośrednio headscale.
DERP oraz mechanizm przekazywania ruchu w przypadku awarii połączenia bezpośredniego
DERP (designated encrypted relay for packets) stanowi ścieżkę zapasową. Gdy dwa węzły nie mogą nawiązać bezpośredniego połączenia WireGuard, zazwyczaj z powodu pracy obu urządzeń za restrykcyjnym NAT (network address translation), przesyłają pakiety za pośrednictwem przekaźnika. Przekaźnik nie posiada kluczy, więc nie może odczytać przesyłanych danych. Rejestruje jedynie, które węzły komunikują się ze sobą oraz wielkość przesyłanych danych.
Należy jasno określić działanie konfiguracji domyślnej. Headscale jest dostarczany z ustawieniami wskazującymi na https://controlplane.tailscale.com/derpmap/default przy użyciu auto_update_enabled: true oraz update_frequency: 3h, co oznacza, że płaszczyzna sterowania należy do użytkownika, natomiast przekaźniki są obsługiwane przez Tailscale. Dla większości użytkowników jest to akceptowalny kompromis. Jeśli jednak jest inaczej, należy uruchomić własny przekaźnik.
Aby uruchomić własny przekaźnik, należy skonfigurować enabled: true w sekcji derp.server pliku config.yaml, zrestartować headscale i otworzyć port STUN (session traversal utilities for NAT) za pomocą sudo ufw allow 3478/udp. Plik konfiguracyjny jasno określa wymaganie: server_url musi korzystać z https, ponieważ DERP wymaga TLS. Opróżnienie listy derp.urls usuwa przekaźniki Tailscale z mapy. Jeśli zostanie to wykonane bez działającego wbudowanego przekaźnika, każda para węzłów, która nie może połączyć się bezpośrednio, utraci możliwość komunikacji.
Z poziomu klienta polecenie tailscale netcheck wyświetla opóźnienia do każdego znanego regionu przekaźników, a tailscale status oznacza każdego partnera jako direct z adresem lub relay z kodem regionu. Węzeł zablokowany na relay wskazuje na problem z NAT, a nie z headscale. Węzeł oznaczony jako direct, który nadal działa wolno, stanowi odrębne zagadnienie, a typowa odpowiedź w takim przypadku dotyczy MTU, a nie samego tunelu.
Dlaczego węzeł jest widoczny jako offline?
Proxy odrzuca upgrade. Jest to częsta przyczyna, której sygnałem jest poprawny stan pozostałych elementów: /health zwraca 200, headscale nodes list wyświetla węzeł, a węzeł nigdy nie przechodzi w stan online. Połączenie sterujące to POST zawierający Upgrade: tailscale-control-protocol, a proxy, które go nie przekazuje, zrywa jedyny kanał raportujący stan węzła. Należy porównać konfigurację nginx z powyższymi liniami dotyczącymi upgrade lub przejść na Caddy, aby wykluczyć problem z proxy.
server_url zmieniło się po zarejestrowaniu węzłów. Węzły próbują łączyć się z wartością, którą otrzymały podczas rejestracji. Jeśli została ona zmieniona, należy uruchomić sudo tailscale up --login-server https://headscale.example.com --force-reauth na każdym węźle.
Klient nie jest uruchomiony. Na węźle należy sprawdzić sudo systemctl is-active tailscaled oraz sudo journalctl -u tailscaled -n 50 --no-pager. Klient, który nie może rozwiązać nazwy domeny lub uzyskać do niej dostępu, zapisuje tam swoje ponowne próby połączenia.
Klucz wygasł. Kwestia ta została omówiona w następnej sekcji.
Aby monitorować stronę serwera podczas testów, należy uruchomić sudo journalctl -u headscale -f na VPS i zrestartować tailscaled na kliencie. Węzeł, który łączy się z headscale, generuje linie w dzienniku natychmiast. Brak logów oznacza, że żądanie nie dociera, dlatego przed sprawdzeniem headscale należy zweryfikować DNS, firewall oraz proxy.
Wygasanie kluczy a węzeł, który przestaje działać po kilku tygodniach
Istnieją dwa oddzielne mechanizmy wygasania, a ich mylenie prowadzi do marnowania czasu.
Klucze preauth wygasają szybko zgodnie z założeniem. Wartością domyślną jest jedna godzina i jedno użycie. Jeśli tailscale up odrzuca klucz, należy wygenerować nowy na serwerze, zamiast edytować cokolwiek po stronie klienta.
Klucze węzłów to komponenty długoterminowe. Sekcja node w pliku config.yaml definiuje expiry: 0, a ustawienie 0 oznacza brak domyślnego wygasania: zarejestrowany węzeł pozostaje ważny, dopóki nie zostanie ręcznie wygaszony. Węzły z tagami nie wygasają nigdy. Należy ustawić expiry: 180d, jeśli wymagane jest automatyczne wygaszanie rejestracji, pamiętając o konsekwencjach: każdy węzeł bez tagu będzie wymagał sudo tailscale up --login-server https://headscale.example.com --force-reauth zgodnie z tym harmonogramem, a serwer bezobsługowy, którego nikt nie uwierzytelni ponownie, zostanie automatycznie usunięty z sieci.
W przypadku utraty laptopa należy wykonać te czynności ręcznie. Polecenie sudo headscale nodes list pozwala uzyskać identyfikator, sudo headscale nodes expire -i 3 wylogowuje dany węzeł, a sudo headscale nodes delete -i 3 całkowicie usuwa go z sieci.
Kopie zapasowe i aktualizacje
/var/lib/headscale oraz /etc/headscale łącznie stanowią cały serwer. Przed skopiowaniem plików należy zatrzymać usługę, ponieważ SQLite może wykonywać zapisy w locie, a baza danych skopiowana pod obciążeniem może być niespójna.
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-*.tgzPrzenieś oba pliki poza serwer. Zawierają one klucze prywatne oraz wszystkie rejestracje, dlatego wymagają takiej samej ochrony jak sam serwer. kopie zapasowe restic z VPS opisuje realizację tego procesu w sposób zaplanowany i zaszyfrowany.
Aktualizacje powtarzają proces instalacji: pobierz nowe .deb, sudo apt install ./headscale.deb, a następnie zrestartuj i uruchom ponownie testy is-active oraz /health. Od wersji 0.29 ścieżka aktualizacji jest rygorystyczna. Pomijanie wersji podrzędnej jest zablokowane, podobnie jak przywracanie starszej wersji podrzędnej. Przechodź o jedną wersję podrzędną na raz, wykonuj kopię zapasową przed każdym krokiem i najpierw zapoznaj się z informacjami o wydaniu danej wersji, ponieważ to samo wydanie zmieniło zachowanie polityki ACL i przeniosło kilka kluczy konfiguracyjnych.
FAQ
Czy headscale uruchamia się od razu po instalacji pakietu .deb?
Tak. Pakiet włącza i uruchamia usługę natychmiast po instalacji, korzystając z dostarczonego /etc/headscale/config.yaml, który nasłuchuje wyłącznie na interfejsie loopback i ustawia server_url na http://127.0.0.1:8080. Ten adres jest bezużyteczny dla klientów, dlatego należy edytować server_url, listen_addr oraz base_domain, a następnie wykonać sudo systemctl restart headscale i potwierdzić operację za pomocą sudo systemctl is-active headscale. Wymagane jest restart: headscale odczytuje plik tylko podczas startu, a enable --now na działającej usłudze nie wprowadza żadnych zmian. Jeśli usługa nie uruchomi się po restarcie, sudo journalctl -u headscale -n 50 --no-pager wskaże przyczynę problemu; na tym etapie niemal zawsze jest to błąd składni YAML, ponieważ headscale parsuje cały plik przed powiązaniem portu.
Czy na moich maszynach nadal instaluję standardowego klienta Tailscale?
Tak. Headscale zastępuje wyłącznie serwer kontrolny. Każdy węzeł uruchamia oficjalnego klienta Tailscale, którego należy skierować na własny serwer za pomocą sudo tailscale up --login-server https://headscale.example.com. Ta flaga jest dostępna w standardowym kliencie, więc nie ma potrzeby stosowania poprawek ani rekompilacji.
Czy mój ruch sieciowy przechodzi przez serwer headscale?
Zazwyczaj nie. Headscale koordynuje sieć oraz przydziela klucze i adresy, natomiast ścieżka danych opiera się na protokole WireGuard bezpośrednio między węzłami. Ruch jest przekierowywany tylko wtedy, gdy dwa węzły nie mogą nawiązać bezpośredniego połączenia i korzystają z przekaźnika DERP; w domyślnej konfiguracji są to publiczne przekaźniki Tailscale. Uruchom tailscale status na węźle, aby sprawdzić, czy dany partner jest połączony direct, czy przez relay.
Dlaczego mój węzeł pozostaje w trybie offline po rejestracji?
Węzeł, który pojawia się w headscale nodes list, ale nie przechodzi w tryb online, zazwyczaj utracił połączenie kontrolne na reverse proxy. Połączenie to jest żądaniem HTTP upgrade wysyłanym jako POST z nagłówkiem Upgrade: tailscale-control-protocol. Nginx odrzuca takie żądania, chyba że nagłówki Upgrade oraz Connection zostaną jawnie przekazane za pomocą proxy_set_header, co w powyższej konfiguracji realizuje blok map $http_upgrade $connection_upgrade. Caddy przekazuje te nagłówki bez dodatkowej konfiguracji, co czyni go szybkim sposobem na sprawdzenie, czy przyczyną problemu jest proxy.
Czy do działania headscale potrzebuję domeny i TLS?
W praktyce tak. Klienci łączą się z adresem podanym w server_url, certyfikaty wystawiane są dla nazw, a nie dla samych adresów IP, a plik konfiguracyjny wskazuje, że DERP wymaga TLS. Konfiguracja domeny wraz z Caddy zajmuje około pięciu minut i zapewnia punkt końcowy HTTPS z automatycznym odnawianiem certyfikatów. Użycie zwykłego HTTP jest możliwe, jednak dokumentacja headscale zdecydowanie zaleca HTTPS na porcie 443 w środowiskach produkcyjnych i zaznacza, że klient Tailscale w określonych sytuacjach zakłada użycie HTTPS na porcie 443.