SSD Nodes Learn Hosting plans →
Mga Gabay Matt ConnorNi Matt Connor · Na-update 2026-08-28

Paano Mag-self-host ng Tailscale gamit ang headscale

Magpatakbo ng sariling Tailscale control server sa VPS. I-install ang official .deb, itakda ang server_url bago avviahin, at i-join ang unang node.

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

Ano ang headscale

Ang headscale ay isang self-hosted na implementasyon ng Tailscale control server. Dahil dito, ang machine na nagko-coordinate 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 official na tailscale client, na itinuturo sa iyong server gamit ang isang flag, --login-server.

Ang control server ang nakakaalam kung sino ang kabilang sa network. Nagbibigay ito sa bawat node ng address mula sa 100.64.0.0/10, namamahagi ng public keys, at nagsasabi sa mga node kung saan nila mahahanap ang isa't isa. WireGuard pa rin ang mga tunnel at direktang binubuo ang mga ito sa pagitan ng mga node. Hindi dumadaan sa headscale box ang traffic sa pagitan ng dalawa mong machine, maliban kung hindi mabuo ang direct path at mapilitan ang mga node na gumamit ng relay.

Isang tailnet (isang Tailscale network) ang sinusuportahan ng headscale sa bawat instance. Inilalarawan ito ng project bilang angkop para sa personal na paggamit o maliit na organisasyon. Kung tatlo o apat lang ang machine, mas kaunting software ang kailangang patakbuhin at mas kaunting masisira sa simpleng 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. Kung gusto mo ng self-hosted control plane ngunit mas nais mo ang sarili mong client at web interface para sa pag-manage ng peers kaysa sa drop-in replacement para sa Tailscale, ang NetBird sa isang VPS ang alternatibong dapat mong isaalang-alang. 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 public 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 guide na ito ang headscale.example.com.
  • Isang hiwalay na domain o subdomain para sa MagicDNS. Ginagamit ng guide na ito ang tailnet.example.net. Hindi ito dapat kapareho ng domain sa server_url.
  • Isang client machine na sasalihan, na nagpapatakbo ng Linux, macOS, Windows, Android, o iOS.

Mag-install ng headscale mula sa opisyal na .deb

Naglalabas ang proyekto ng mga .deb package sa GitHub releases page nito. 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-architecture

Ipinapakita nito ang amd64 sa karaniwang x86 VPS at arm64 sa planong gaya ng Ampere o Graviton. 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 version

Kinakailangan ang ./ bago ang file name. Kung wala ito, hahanapin ng apt ang package na headscale.deb sa iyong repositories at mabibigo.

Lumilikha ang package ng headscale system user, nagsusulat ng default na /etc/headscale/config.yaml, at nag-i-install ng systemd unit. Hindi nito awtomatikong sinisimulan ang service, at ito ang tamang pagkakasunod-sunod. Itinuturo ng configuration na kasama ng package ang server_url sa http://127.0.0.1:8080, na hindi address na maaabot ng alinman sa iyong client. Kaya mali ang service kapag sinimulan agad, kahit maging operational ito. Kapag pinatakbo ang sudo systemctl is-active headscale sa puntong ito, ipinapakita 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 reference 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.yaml

Ang server_url ang address na inilalagay ng headscale sa bawat client registration. Tatawagan ng mga client ang 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 nagbi-bind ang process. Iwanan ito sa loopback. Ang reverse proxy sa parehong server ang nagte-terminate ng TLS (transport layer security) at nagpapasa ng request dito, kaya walang kailangang umabot mula sa labas ng server sa port 8080.

Ang base_domain ang MagicDNS suffix, ang domain kung saan ibinibigay ang mga pangalan ng iyong nodes. Dapat itong isang fully qualified domain name na walang trailing dot, at dapat itong ibang domain kaysa sa nasa server_url, dahil kung hindi ay magko-collide ang dalawang namespace.

Huwag baguhin ang database section. Ang default ay SQLite sa /var/lib/headscale/db.sqlite, sa directory na ginawa at pagmamay-ari ng package, at sapat na ang SQLite para sa tailnet na ganito ang laki.

Simulan ang headscale at patunayang tumatakbo 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/health

Ang is-active ay nagpi-print ng active, at ang curl ay nagpi-print ng 200. Parehong ginagawa ng enable --now ang dalawang bahaging ito: sinisimulan nito ang service at itinatakda itong awtomatikong magsimula pagkatapos ng reboot.

Kung nagpi-print ang is-active ng 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 bina-parse ng headscale ang buong file bago ito magbukas ng socket. Dahil dito, mapipigilan ng maling indentation o unknown key ang process bago ito makinig sa anumang port. Ayusin ang file, pagkatapos ay patakbuhin 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 systemd units, ipinapaliwanag ng pagpapatakbo ng sarili mong service at timer gamit ang systemd ang mga command na ginagamit dito.

Suriin ang state files habang nasa shell ka:

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

Nagsisimula ang dalawang linya sa headscale, ang unprivileged user na ginawa ng package. Ang noise_private.key ang identity ng server para sa mga client nito. Huwag itong burahin. Kapag binura mo ito, gagawa ang headscale ng bagong identity at kailangang mag-register muli ang bawat node.

Ilagay ang TLS sa harap ng headscale

Dapat ma-access ng mga client ang server_url gamit ang HTTPS. Ang Caddy ang pinakamaikling paraan dahil awtomatiko nitong nire-request at nire-renew ang certificate.

sudo apt install -y caddy

Palitan 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 caddy

Ipinapakita ng validate ang adapted config to JSON kapag tama ang syntax ng file. Cosmetic lamang ang warning na 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 nag-iisang check na iyon na gumagana nang magkakasama ang DNS, firewall, certificate, at proxy.

Narito ang detalye sa proxy na madalas kumukuha ng isang gabi para maayos. Ang Tailscale control connection 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;
    }
}

Kapag hindi isinama ang mga linyang iyon, gumagana pa rin ang mga ordinaryong request. Kaya nagbabalik ang /health ng 200 at mukhang maayos ang lahat, habang hindi nabubuo ang long-lived control connection at nagre-register ang mga node ngunit nananatiling offline. Kung nginx ang gagamitin mo, saklaw ng Certbot sa Ubuntu 24.04 gamit ang nginx ang bahagi tungkol sa certificate.

Anong mga port ang bubuksan sa UFW

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

Ang port 443 ang nagdadala ng lahat ng communication ng mga client. Ginagamit lamang ang port 80 para sa ACME (automatic certificate management environment) HTTP challenge at pag-redirect sa HTTPS. Kailangan ito ng Caddy para makakuha ng certificate.

Mananatiling sarado ang port 8080. Ang listen_addr ay 127.0.0.1:8080, kaya naaabot ng proxy ang headscale sa loopback interface at walang kailangang firewall rule. Kapag binuksan ang 8080 sa internet, binibigyan nito ang mga client ng cleartext control channel ngunit walang pakinabang. Tandaan na karamihan sa mga provider ay may pangalawang firewall sa control panel nila, na hiwalay sa UFW. Dahil dito, maaaring bukas ang port sa server ngunit sarado pa rin sa edge. Ipinaliliwanag nang mas detalyado sa Mga pangunahing kaalaman sa UFW firewall sa isang VPS ang syntax ng mga rule.

Gumawa ng user at preauth key

sudo headscale users create alice
sudo headscale users list

Ang 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 sumusunod dito. 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 ang 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 24h

Isang beses lang ipinapakita ang key. Kopyahin ito ngayon. Isang beses lang magagamit ang preauth key at valid ito nang isang oras maliban kung iba ang itatakda mo, kaya mainam na itakda ang --expiration 24h habang nagte-test ka pa. Idagdag ang --reusable para sa key na mag-e-enroll ng maraming 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 i-join:

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

Ang tailscale ip -4 ay nagpi-print ng address na itinalaga ng headscale, gaya ng 100.64.0.1. Bumalik sa server at gamitin ang sudo headscale nodes list upang makita ang node kasama ang ID nito, user nito, at online state nito.

Dapat eksaktong magtugma ang value ng --login-server at server_url, kasama ang scheme at walang trailing slash. String ang paghahambing sa mga ito. Kapag hindi nagtugma, magre-register ang client sa isang address at pagkatapos ay uutusan itong kumonekta sa ibang address.

Kung dati nang naka-sign in sa hosted service ng Tailscale ang isang machine, mananatili ang login na iyon. Patakbuhin muna ang sudo tailscale logout rito, pagkatapos ay patakbuhin ang tailscale up gamit ang --login-server.

Kung hindi mo isasama ang --auth-key, magpi-print ang client ng URL. Buksan ito upang makita ang identifier para sa registration attempt na iyon. I-approve ito sa server:

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

Mas maginhawa ang form na ito para sa sarili mong laptop. Mas mainam ang preauth keys para sa anumang scripted, dahil walang kailangang taong mag-monitor nito. Kapag node na ang VPS mismo, maaari rin nitong ihatid ang internet traffic ng iba mong machine. Ito ang setup ng exit node, ngunit sa halip na sa hosted admin console, ia-approve mo ang advertised route sa server gamit ang command na headscale.

DERP at kung ano ang nagre-relay ng 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 ng strict NAT (network address translation), sa halip ay ipinapadala nila ang mga packet sa pamamagitan ng relay. Walang hawak na key ang relay, kaya hindi nito mababasa ang traffic mo. Nakikita nito kung aling mga node ang nag-uusap at kung gaano karaming data ang dumadaloy.

Linawin kung ano ang ginagawa ng default configuration. Ang Headscale ay naka-configure na gumamit ng https://controlplane.tailscale.com/derpmap/default kasama ang auto_update_enabled: true at update_frequency: 3h, kaya iyo ang control plane ngunit sa Tailscale ang mga relay. Para sa karamihan, katanggap-tanggap na trade-off ito. Kung hindi ito angkop sa iyo, magpatakbo ka 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 binura ang listahan ng derp.urls, inaalis nito ang mga relay ng Tailscale sa map. Kung gagawin mo ito nang walang gumaganang embedded relay, hindi na 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. 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 sa headscale. Iba naman ang sitwasyon ng peer na direct pero mabagal pa rin. Karaniwan, MTU ang problema rito at hindi ang tunnel mismo.

Bakit lumalabas na offline ang isang node?

Ibinabagsak ng proxy ang upgrade. Ito ang pinakakaraniwang dahilan. Makikita ito kapag maayos ang lahat ng iba pa: /health ay nagbabalik ng 200, ipinapakita ng headscale nodes list ang node, at hindi kailanman nagiging online ang node. Ang control connection ay isang POST na nagdadala ng Upgrade: tailscale-control-protocol. Kapag hindi ito ipinasa ng proxy, mawawala ang tanging channel na nag-uulat ng state ng node. Ihambing ang configuration ng nginx sa map block sa itaas, o lumipat sa Caddy upang maalis ang proxy bilang sanhi.

Nagbago ang server_url matapos mag-register ang mga node. Patuloy na dina-dial 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. Nasa mga log na iyon ang mga retry ng client kapag hindi nito ma-resolve o maabot ang iyong domain.

Nag-expire ang key. Tinalakay ito sa susunod na seksyon.

Upang ma-monitor ang server side habang nagte-test ka, patakbuhin ang sudo journalctl -u headscale -f sa VPS at i-restart ang tailscaled sa client. Kapag nakarating ang node sa headscale, agad itong maglalabas ng mga log line. Kapag walang lumabas, hindi dumarating ang request. Suriin muna ang DNS, firewall, at proxy bago ang headscale.

Pag-expire ng key, at ang node na tumitigil gumana pagkalipas ng ilang linggo

May dalawang magkahiwalay na expiry, at nakasasayang ng oras kapag napagpapalit ang mga ito.

Mabilis mag-expire ang preauth keys, ayon sa disenyo. Ang default ay isang oras at isang paggamit. Kung tumanggi ang tailscale up sa key, bumuo ng panibagong key sa server sa halip na mag-edit ng anuman sa client.

Ang node keys ang pangmatagalang bahagi. Itinatakda ng seksyong node ng config.yaml ang expiry: 0, at ang 0 ay nangangahulugang walang default na expiry: mananatiling valid ang isang registered node hanggang sa 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, ngunit unawain ang epekto nito: kakailanganin ng bawat non-tagged node ang sudo tailscale up --login-server https://headscale.example.com --force-reauth ayon sa schedule na iyon, at awtomatikong mawawala sa network ang isang headless server na walang muling nag-a-authenticate dito.

Manu-mano itong gawin kapag may nawalan ng laptop. Ibinibigay ng sudo headscale nodes list ang ID, pagkatapos ay ila-log out ng sudo headscale nodes expire -i 3 ang node na iyon, at tuluyang aalisin ito ng sudo headscale nodes delete -i 3 sa network.

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 ang SQLite at maaaring maging inconsistent ang database kapag kinopya habang may load.

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

Ilipat ang dalawang file sa labas ng server. Naglalaman ang mga ito ng private keys at lahat ng registration, kaya dapat pareho ang pag-iingat sa mga ito at sa server mismo. Sinasaklaw ng mga restic backup mula sa isang VPS ang pag-set up nito ayon sa iskedyul at nang naka-encrypt.

Ulitin sa mga upgrade ang proseso ng pag-install: i-download ang bagong .deb at sudo apt install ./headscale.deb, pagkatapos ay i-restart at muling patakbuhin ang mga pagsusuri sa is-active at /health. Simula noong 0.29, mahigpit ang upgrade path. Hinaharangan ang paglaktaw sa isang minor version, pati ang pag-downgrade sa mas lumang minor version. Mag-upgrade nang tig-iisang minor version, gumawa ng backup bago ang bawat hakbang, at basahin muna ang release notes ng bersyong iyon, dahil binago ng parehong release ang gawi ng ACL policy at inilipat ang ilang configuration key.

FAQ

Bakit hindi nagsisimulang gumana ang headscale agad matapos kong i-install ang .deb?

Ini-install ng package ang unit pero iniiwang nakahinto 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 binabasa ng headscale ang buong file bago ito mag-bind sa isang port.

Kailangan ko pa bang i-install ang normal na Tailscale client sa mga machine ko?

Oo. Pinapalitan ng Headscale ang control server lamang. Ang bawat node ay nagpapatakbo ng official client mula sa Tailscale, at itinuturo mo ito sa server gamit ang sudo tailscale up --login-server https://headscale.example.com. Kasama ang flag na iyon sa 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 ito ng mga key at address, habang direktang dumadaan sa WireGuard ang data path sa pagitan ng mga node mo. Lumulihis lamang ang traffic kapag hindi direktang maabot ng dalawang node ang isa't isa at gumagamit sila ng DERP relay bilang fallback. Sa kasamang configuration, mga public relay ng Tailscale ang ginagamit. Patakbuhin ang tailscale status sa isang node upang makita kung ang isang partikular na peer ay direct o gumagamit ng 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 ito na ipinapadala bilang POST na may header na Upgrade: tailscale-control-protocol, at dini-drop ito ng nginx maliban kung idagdag mo ang map $http_upgrade $connection_upgrade block at ang katugmang proxy_set_header lines. Ipinapasa ito ng Caddy nang walang karagdagang configuration, kaya mabilis itong gamitin upang subukan kung ang proxy ang may problema.

Kailangan ko ba ng domain name at TLS para sa headscale?

Sa praktika, oo. Kumokonekta ang mga client sa string na inilagay mo sa server_url, ini-issue ang mga certificate para sa mga pangalan at hindi para sa mga bare IP address, at nakasaad sa configuration file na kailangan ng DERP ng TLS. Ang domain na may Caddy ay karaniwang limang minuto lang i-configure at nagbibigay ito ng HTTPS endpoint na awtomatikong nagre-renew. Kapag pinatakbo ang control server gamit ang plain HTTP, dumadaan sa internet nang walang encryption ang bawat pakikipag-ugnayan ng client dito.