Headscale: Sariling Tailscale Control Server sa VPS
Magpatakbo ng sariling Tailscale control server sa VPS: i-install ang opisyal na .deb, itakda ang server_url bago simulan, at i-join ang unang node.
Ano ang headscale
Ang headscale ay isang self-hosted na implementasyon ng Tailscale control server. Dahil dito, ang machine na nag-oorganisa ng iyong private network ay isang VPS na pagmamay-ari mo. Community project ito at hindi pinapatakbo ng Tailscale Inc. Gumagamit pa rin ang bawat machine ng opisyal na tailscale client, na itinuturo sa iyong server gamit ang isang flag, --login-server.
Ang control server ang nakaaalam kung sino ang kabilang sa network. Binibigyan nito ang bawat node ng address mula sa 100.64.0.0/10, namamahagi ng public keys, at sinasabi sa mga node kung saan nila mahahanap ang isa't isa. Nanatiling WireGuard ang mga tunnel at direktang itinatayo ang mga ito sa pagitan ng mga node. Hindi dumadaan sa headscale box ang traffic sa pagitan ng dalawa mong machine, maliban kung hindi makabuo ng direktang path at kailangang gumamit ang mga node ng relay.
Nagsisilbi ang headscale ng isang tailnet (isang Tailscale network) bawat instance. Ayon sa project, angkop ito para sa personal na paggamit o maliit na organisasyon. Kung tatlo o apat ang machine, mas kaunting software ang kailangang patakbuhin at mas kaunti ang maaaring masira sa isang plain WireGuard VPN sa VPS na pagmamay-ari mo. Mas kapaki-pakinabang ang headscale kapag ayaw mo nang manu-manong magsulat ng [Peer] block para sa bawat bagong laptop. Para sa mas malawak na paghahambing ng dalawang modelong ito, tingnan ang pagkakaiba ng WireGuard at Tailscale.
Mga kailangan bago mag-install
- Isang VPS na nagpapatakbo ng Ubuntu 24.04, may pampublikong IPv4 address, at may sudo access. Kung bago ang server, sundin muna ang unang sampung minuto sa bagong VPS.
- Isang DNS A record na nakaturo sa address na iyon. Ginagamit ng gabay na ito ang
headscale.example.com. - Isang hiwalay na domain o subdomain para sa MagicDNS. Ginagamit ng gabay na ito ang
tailnet.example.net. Hindi ito dapat kapareho ng domain saserver_url. - Isang client machine na isasali, na nagpapatakbo ng Linux, macOS, Windows, Android o iOS.
Mag-install ng headscale mula sa opisyal na .deb
Naglalabas ang proyekto ng mga package na .deb sa releases page nito sa GitHub. Noong July 2026, ang kasalukuyang release ay 0.29.3. Suriin muna ang architecture dahil kasama ito sa file name.
sudo apt update
sudo apt install -y wget
dpkg --print-architectureNagpi-print ito ng amd64 sa karaniwang x86 VPS at arm64 sa Ampere o Graviton-style plan. Ilagay ang sagot sa variable sa ibaba.
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 versionKinakailangan ang ./ bago ang file name. Kung wala ito, hahanapan ng apt ang repositories mo ng package na headscale.deb at mabibigo.
Gumagawa ang package ng headscale system user, nagsusulat ng default na /etc/headscale/config.yaml, at nag-i-install ng systemd unit. Hindi nito sinisimulan ang service, at tama ang pagkakasunod na ito. Tinutukoy ng configuration na kasama sa package ang server_url sa http://127.0.0.1:8080, na hindi address na maaabot ng alinman sa mga client mo. Kaya magiging mali ang service kung sisimulan ito ngayon, kahit umandar pa ito. Kapag pinatakbo ang sudo systemctl is-active headscale sa puntong ito, pini-print nito ang inactive. Inaasahan ito at hindi ito error.
I-configure ang server_url bago simulan ang service
I-edit ang /etc/headscale/config.yaml gamit ang sudo nano /etc/headscale/config.yaml, o ilapat ang parehong tatlong pagbabago gamit ang sed. Magtabi ng kopya ng orihinal, dahil mahaba at maraming komento ang file at ito ang pinakamainam mong sanggunian para sa iba pang setting.
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.yamlAng server_url ang address na isinusulat ng headscale sa bawat client registration. Kumokonekta ang mga client sa eksaktong string na iyon mula noon, kaya dapat itong public name na may https:// sa unahan, at hindi kailanman 127.0.0.1.
Ang listen_addr ang address kung saan nagba-bind ang process. Iwan itong nakatakda sa loopback. Isang reverse proxy sa parehong server ang nagtatapos ng TLS (transport layer security) at nagpapasa ng request dito, kaya walang nasa labas ng server ang kailangang makaabot sa port 8080.
Ang base_domain ang MagicDNS suffix, ang domain kung saan nakukuha ng iyong mga node ang kanilang mga pangalan. Dapat itong fully qualified domain name na walang trailing dot, at dapat itong ibang domain kaysa sa nasa server_url, dahil kung hindi ay magsasapawan ang dalawang namespace.
Huwag baguhin ang database section. Ang default ay SQLite sa /var/lib/headscale/db.sqlite, sa isang directory na ginawa at pagmamay-ari ng package, at sapat ang SQLite para sa tailnet na ganito ang laki.
Simulan ang headscale at patunayang gumagana ito
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/healthIpinapakita ng is-active ang active, at ipinapakita ng curl ang 200. Ginagawa ng enable --now ang parehong bahagi ng trabaho: sinisimulan nito ang service at itinatakda itong awtomatikong magsimula pagkatapos ng reboot.
Kung ipinapakita ng is-active ang failed, basahin ang journal gamit ang sudo journalctl -u headscale -n 50 --no-pager. Halos palaging configuration file ang sanhi ng failure sa yugtong ito, dahil binabasa ng headscale ang buong file bago ito magbukas ng socket. Kaya pinipigilan ng maling indentation o hindi kilalang key ang process na magsimula bago ito makinig sa anumang port. Ayusin ang file, pagkatapos ay gamitin ang sudo systemctl restart headscale. Kailangan ang parehong restart sa bawat susunod na pagbabago sa configuration. Awtomatikong kumokonekta muli ang mga client pagkatapos nito. Kung bago sa iyo ang mga systemd unit, ipinapaliwanag sa pagpapatakbo ng sarili mong service at timer gamit ang systemd ang mga command na ginagamit dito.
Suriin ang state file habang nasa shell ka:
stat -c '%U %n' /var/lib/headscale/db.sqlite /var/lib/headscale/noise_private.keyNagsisimula ang parehong linya sa headscale, ang unprivileged user na ginawa ng package. Ang noise_private.key ang identity ng server para sa mga client nito. Panatilihin ito. Kapag tinanggal mo ito, gagawa ang headscale ng bago at kailangang mag-register muli ang bawat node.
Ilagay ang TLS sa harapan ng headscale
Dapat maabot ng mga client ang server_url sa HTTPS. Ang Caddy ang pinakamaikling paraan dahil awtomatiko nitong hinihiling at nire-renew ang certificate.
sudo apt install -y caddyPalitan ang /etc/caddy/Caddyfile ng block mula sa dokumentasyon ng 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 caddyIpinapakita ng validate ang adapted config to JSON kapag tama ang parsing ng file. Cosmetic lamang ang babalang hindi naka-format ang file. Mula sa iyong laptop, dapat ding ipakita ng curl -sS -o /dev/null -w '%{http_code}\\n' https://headscale.example.com/health ang 200. Pinatutunayan ng iisang pagsusuring ito na gumagana nang magkakasama ang DNS, firewall, certificate at proxy.
Narito ang detalye ng proxy na maaaring umubos ng isang gabi sa pag-troubleshoot. Ang control connection ng Tailscale ay isang HTTP upgrade. Sinisimulan ito gamit ang POST sa halip na GET, at ang value ng Upgrade header ay tailscale-control-protocol. Ipinapasa ito ng Caddy nang walang karagdagang configuration. Hindi ito ginagawa ng nginx, kaya kailangan ng nginx front end ang upgrade map:
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;
}
}Kung aalisin mo ang mga linyang iyon, magpapatuloy pa ring magtagumpay ang mga ordinaryong request. Kaya 200 ang ibinabalik ng /health at mukhang maayos ang lahat, pero hindi nabubuo ang matagalang control connection at nagre-register ang iyong mga node, pagkatapos ay nananatiling offline. Kung nginx ang gagamitin mo, saklaw ng Certbot sa Ubuntu 24.04 gamit ang nginx ang bahagi para sa certificate.
Mga port na bubuksan sa UFW
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verboseDumaan ang lahat ng komunikasyon ng client sa port 443. Nariyan lamang ang port 80 para sa ACME (automatic certificate management environment) HTTP challenge at pag-redirect sa HTTPS. Kailangan din ito ng Caddy para makakuha ng certificate.
Nananatiling sarado ang port 8080. Ang listen_addr ay 127.0.0.1:8080, kaya ina-access ng proxy ang headscale gamit ang loopback interface at walang kailangang firewall rule. Kapag binuksan ang 8080 sa internet, magkakaroon ang mga client ng cleartext control channel ngunit wala itong pakinabang. Tandaan na karamihan ng provider ay may hiwalay na second firewall sa kanilang control panel, bukod sa UFW. Dahil dito, maaaring bukas ang isang port sa server ngunit sarado pa rin ito sa edge. Ipinaliliwanag nang mas detalyado ng Mga pangunahing kaalaman sa UFW firewall sa isang VPS ang syntax ng rule.
Gumawa ng user at preauth key
sudo headscale users create alice
sudo headscale users listAng command na headscale ay isang client. Nakikipag-ugnayan ito sa tumatakbong daemon sa unix socket na /var/run/headscale/headscale.sock, na may mode na 0770 at pagmamay-ari ng group na headscale. Dalawang bagay ang resulta nito. Nabibigo ang command kapag nakahinto ang service; ito ang isa pang dahilan kung bakit mahalaga ang pagkakasunod-sunod sa gabay na ito. Kailangan din nito ng sudo maliban kung idaragdag mo ang sarili mong account sa group na headscale.
Nagpi-print ang users list ng ID sa tabi ng bawat pangalan. Kailangan mo ang numerong iyon dahil numeric user ID ang tinatanggap ng key command, hindi pangalan.
sudo headscale preauthkeys create --user 1 --expiration 24hMinsan lang ipinapakita ang key. Kopyahin ito ngayon. Isahang gamit ang preauth key at valid ito nang isang oras maliban kung magtakda ka ng iba, kaya makabubuting itakda ang --expiration 24h habang nagsusuri ka pa. Idagdag ang --reusable para sa key na mag-e-enroll ng ilang machine, at ituring ito na parang password dahil maaaring sumali sa network mo ang sinumang may hawak nito.
Ikonekta ang unang client gamit ang --login-server
Sa machine na gusto mong isali:
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 -4Ipinapakita ng tailscale ip -4 ang address na itinalaga ng headscale, gaya ng 100.64.0.1. Bumalik sa server at ipinapakita ng sudo headscale nodes list ang node kasama ang ID nito, user nito, at online state nito.
Dapat eksaktong tumugma ang value ng --login-server sa server_url, kasama ang scheme at walang trailing slash. Bilang mga string ang paghahambing sa mga ito. Kapag hindi tugma, nagre-register ang client sa isang address at pagkatapos ay sinasabihang kumonekta sa ibang address.
Ang machine na dati nang naka-sign in sa hosted service ng Tailscale ay mananatili sa login na iyon. Patakbuhin muna rito ang sudo tailscale logout, pagkatapos ay patakbuhin ang tailscale up gamit ang --login-server.
Kung aalisin mo ang --auth-key, magpi-print ang client ng URL. Buksan ito. Ipinapakita ng page ang identifier para sa registration attempt na iyon, na dapat mong i-approve sa server:
sudo headscale auth register --user alice --auth-id PASTE-THE-ID-FROM-THE-PAGEMas praktikal ang paraang ito para sa sarili mong laptop. Mas angkop ang preauth keys para sa mga scripted na operasyon dahil walang kailangang taong magbantay.
DERP at kung ano ang nagre-relay ng network traffic kapag nabigo ang direct path
Ang DERP (designated encrypted relay for packets) ang fallback path. Kapag hindi makapagbukas ng direct WireGuard connection ang dalawang node, karaniwan dahil parehong nasa likod sila ng mahigpit na NAT (network address translation), ipinapadala nila ang mga packet sa isang relay. Walang hawak na key ang relay, kaya hindi nito mababasa ang traffic mo. Nakikita nito kung aling mga node ang nag-uusap at gaano karaming data ang dumadaloy.
Maging malinaw sa ginagawa ng default configuration. Ang Headscale ay naka-configure na tumuro sa https://controlplane.tailscale.com/derpmap/default gamit ang auto_update_enabled: true at update_frequency: 3h, kaya nasa iyo ang control plane habang sa Tailscale naman ang iyong mga relay. Para sa karamihan, makatuwiran ang trade-off na ito. Kung hindi ito katanggap-tanggap, magpatakbo ng sarili mong relay.
Para magpatakbo ng sarili mong relay, itakda ang enabled: true sa ilalim ng derp.server sa config.yaml, i-restart ang headscale, at buksan ang STUN (session traversal utilities for NAT) port gamit ang sudo ufw allow 3478/udp. Malinaw na nakasaad sa configuration file ang requirement: dapat gumamit ng https ang server_url, dahil nangangailangan ang DERP ng TLS. Kapag inalis ang laman ng listahan ng derp.urls, inaalis nito ang mga relay ng Tailscale sa map. Kung gagawin mo ito nang walang gumaganang embedded relay, hindi makakakonekta ang anumang pares ng node na hindi direktang makakonekta.
Mula sa isang client, ipinapakita ng tailscale netcheck ang latency sa bawat relay region na alam nito, at minamarkahan ng tailscale status ang bawat peer bilang direct na may address o relay na may region code. Ang peer na nananatili sa relay ay problema sa NAT, hindi problema sa headscale.
Bakit ipinapakitang offline ang isang node?
Ibinabagsak ng proxy ang upgrade. Ito ang karaniwang sanhi. Makikita rito na maayos ang lahat ng iba pa: nagbabalik ang /health ng 200, ipinapakita ng headscale nodes list ang node, pero hindi kailanman nagiging online ang node. Ang control connection ay isang POST na nagdadala ng Upgrade: tailscale-control-protocol. Kapag hindi ito ipinapasa ng proxy, nawawala ang tanging channel na nag-uulat ng estado ng node. Ihambing ang iyong nginx configuration sa map block sa itaas, o lumipat sa Caddy para maalis ang proxy bilang sanhi.
Nagbago ang server_url matapos mag-register ang mga node. Patuloy na ginagamit ng mga node ang value na ibinigay sa kanila noong registration. Kung in-edit mo ito, patakbuhin ang sudo tailscale up --login-server https://headscale.example.com --force-reauth sa bawat node.
Hindi tumatakbo ang client. Sa node, patakbuhin ang sudo systemctl is-active tailscaled at sudo journalctl -u tailscaled -n 50 --no-pager. Isinusulat ng client na hindi ma-resolve o maabot ang iyong domain ang mga retry nito roon.
Nag-expire ang key. Tinalakay ito sa susunod na seksyon.
Para ma-monitor ang server side habang nagsasagawa ka ng test, patakbuhin ang sudo journalctl -u headscale -f sa VPS at i-restart ang tailscaled sa client. Agad na gumagawa ng mga log line ang node kapag naaabot nito ang headscale. Kapag walang lumalabas na log, hindi dumarating ang request. Suriin muna ang DNS, firewall at proxy bago ang headscale.
Pag-expire ng key, at ang node na humihinto makalipas ang ilang linggo
May 2 magkahiwalay na expiry, at nakasasayang ng oras kapag napagpapalit ang mga ito.
Mabilis mag-expire ang mga preauth key ayon sa disenyo. Ang default ay 1 oras at 1 paggamit. Kung tumanggi ang tailscale up sa key, bumuo ng panibago sa server sa halip na mag-edit ng anuman sa client.
Ang mga node key ang pangmatagalang bahagi. Itinatakda ng seksyong node ng config.yaml ang expiry: 0, at nangangahulugan ang 0 na walang default na expiry: mananatiling valid ang isang registered node hanggang i-expire mo ito. Hindi kailanman nag-e-expire ang mga tagged node. Itakda ang expiry: 180d kung gusto mong awtomatikong mag-expire ang mga registration, at unawain ang hinihingi nito: kailangang magkaroon ang bawat node na hindi tagged ng sudo tailscale up --login-server https://headscale.example.com --force-reauth ayon sa iskedyul na iyon, at awtomatikong mawawala sa network ang isang headless server na walang muling nag-a-authenticate rito.
Manwal itong gawin kapag may nawalan ng laptop. Ibinibigay ng sudo headscale nodes list ang ID, pagkatapos ay nilo-log out ng sudo headscale nodes expire -i 3 ang node na iyon, at inaalis ito ng sudo headscale nodes delete -i 3 sa network nang tuluyan.
Mga backup at upgrade
/var/lib/headscale at /etc/headscale ang bumubuo sa buong server. Ihinto ang service bago kopyahin ang mga ito, dahil maaaring may kasalukuyang write operation ang SQLite at maaaring maging inconsistent ang database kapag kinopya habang ginagamit.
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-*.tgzIlipat sa labas ng box ang dalawang file. Naglalaman ang mga ito ng private key at lahat ng registration, kaya kailangan ang mga ito ng kaparehong pag-iingat na ibinibigay sa server mismo. Saklaw ng mga restic backup mula sa VPS ang pag-setup nito ayon sa iskedyul at gamit ang encryption.
Ulitin sa mga upgrade ang proseso ng installation: i-download ang bagong .deb at sudo apt install ./headscale.deb, pagkatapos ay i-restart at muling patakbuhin ang mga check na is-active at /health. Simula 0.29, mahigpit ang upgrade path. Hindi pinapayagan ang paglaktaw sa isang minor version, at hindi rin pinapayagan ang pag-downgrade sa mas lumang minor version. Mag-upgrade ng isang minor version bawat hakbang, gumawa ng backup bago ang bawat hakbang, at basahin muna ang release notes ng bersyong iyon, dahil binago ng release na iyon ang behavior ng ACL policy at inilipat ang ilang configuration key.
FAQ
Bakit hindi agad gumagana ang headscale pagkatapos kong i-install ang .deb?
Ini-install ng package ang unit pero iniiwang naka-stop ang service, at ang default na /etc/headscale/config.yaml ay template lamang at hindi gumaganang configuration. I-edit muna ang server_url, listen_addr at base_domain, pagkatapos ay patakbuhin ang sudo systemctl enable --now headscale at kumpirmahin gamit ang sudo systemctl is-active headscale. Kung hindi pa rin ito gumana, tinutukoy ng sudo journalctl -u headscale -n 50 --no-pager ang problema. Sa yugtong ito, halos palaging YAML error ito dahil bina-parse ng headscale ang buong file bago ito makinig sa isang port.
Kailangan ko pa bang i-install ang karaniwang Tailscale client sa aking mga machine?
Oo. Pinapalitan ng Headscale ang control server lamang. Ang bawat node ay nagpapatakbo ng official client mula sa Tailscale, at itinuturo mo ito sa iyong server gamit ang sudo tailscale up --login-server https://headscale.example.com. Bahagi ang flag na iyon ng standard client, kaya walang kailangang i-patch o i-rebuild.
Dumadaan ba ang traffic ko sa headscale server?
Karaniwan, hindi. Kino-coordinate ng Headscale ang network at namamahagi ng mga key at address, habang direktang dumadaan sa WireGuard sa pagitan ng iyong mga node ang data path. Lilihis lamang ang traffic kapag hindi direktang maabot ng dalawang node ang isa't isa at gumamit sila ng DERP relay. Sa ipinadalang configuration, mga pampublikong relay ng Tailscale ang mga iyon. Patakbuhin ang tailscale status sa isang node para makita kung ang isang peer ay direct o nasa relay.
Bakit nananatiling offline ang node ko pagkatapos nitong mag-register?
Ang node na lumilitaw sa headscale nodes list pero hindi kailanman nagiging online ay karaniwang nawalan ng control connection sa reverse proxy. HTTP upgrade ang connection na iyon, ipinapadala bilang POST na may header na Upgrade: tailscale-control-protocol, at ibinabagsak ito ng nginx maliban kung idagdag mo ang block na map $http_upgrade $connection_upgrade at ang katugmang mga linyang proxy_set_header. Ipinapasa ito ng Caddy nang walang karagdagang configuration, kaya mabilis itong gamitin upang subukan kung ang proxy ang sanhi ng problema.
Kailangan ko ba ng domain name at TLS para sa headscale?
Sa aktuwal na paggamit, oo. Kumokonekta ang mga client sa string na inilagay mo sa server_url, iniisyu ang mga certificate para sa mga pangalan at hindi para sa mga bare IP address, at sinasabi ng configuration file na nangangailangan ng TLS ang DERP. Tumatagal nang humigit-kumulang limang minuto ang pag-set up ng domain kasama ang Caddy, at nagbibigay ito ng HTTPS endpoint na awtomatikong nagre-renew. Kung patatakbuhin ang control server gamit ang plain HTTP, tatawid sa internet nang walang encryption ang bawat pag-uusap ng client dito.