SSD Nodes Learn RAM 8GB — $66/tahun
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-08-01

Cara Self-Host Tailscale dengan Headscale di VPS

Jalankan server kontrol Tailscale sendiri di VPS. Instal headscale dari .deb resmi, atur server_url sebelum menjalankannya, lalu gabungkan node pertama.

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

Apa itu headscale

Headscale adalah implementasi yang di-host sendiri dari server kontrol Tailscale. Dengan demikian, mesin yang mengoordinasikan jaringan privat Anda adalah VPS milik Anda. Headscale merupakan proyek komunitas dan tidak dikelola oleh Tailscale Inc. Setiap mesin tetap menjalankan klien resmi tailscale, yang diarahkan ke server Anda menggunakan satu flag, --login-server.

Server kontrol adalah komponen yang mengetahui siapa saja yang tergabung dalam jaringan. Server ini memberikan setiap node alamat dari 100.64.0.0/10, mendistribusikan kunci publik, dan memberi tahu node tempat menemukan node lainnya. Tunnel tetap menggunakan WireGuard dan dibangun langsung antarnode. Traffic antara dua mesin Anda tidak melewati server headscale, kecuali jalur langsung tidak dapat dibuat dan node beralih menggunakan relay.

Setiap instance headscale melayani satu tailnet (satu jaringan Tailscale). Menurut dokumentasi proyek, model ini sesuai untuk penggunaan pribadi atau organisasi kecil. Dengan tiga atau empat mesin, VPN WireGuard biasa pada VPS milik Anda memerlukan lebih sedikit perangkat lunak untuk dijalankan dan lebih sedikit komponen yang dapat mengalami masalah. Headscale bermanfaat ketika Anda tidak lagi ingin menulis blok [Peer] secara manual untuk setiap laptop baru. Untuk perbandingan kedua model secara lebih luas, lihat perbedaan antara WireGuard dan Tailscale.

Yang Anda perlukan sebelum menginstal

  • VPS yang menjalankan Ubuntu 24.04 dengan alamat IPv4 publik dan akses sudo. Jika server masih baru, ikuti sepuluh menit pertama pada VPS baru terlebih dahulu.
  • Record DNS A yang mengarah ke alamat tersebut. Panduan ini menggunakan headscale.example.com.
  • Domain atau subdomain kedua untuk MagicDNS. Panduan ini menggunakan tailnet.example.net. Domain ini tidak boleh sama dengan domain dalam server_url.
  • Satu mesin klien untuk bergabung, yang menjalankan Linux, macOS, Windows, Android, atau iOS.

Menginstal headscale dari .deb resmi

Proyek ini menerbitkan paket .deb di halaman rilis GitHub. Per Juli 2026, rilis saat ini adalah 0.29.3. Periksa arsitektur Anda terlebih dahulu karena nama file mencantumkan arsitekturnya.

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

Perintah tersebut menampilkan amd64 pada VPS x86 biasa dan arm64 pada paket bergaya Ampere atau Graviton. Masukkan hasilnya ke dalam variabel di bawah ini.

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

./ di depan nama file wajib digunakan. Tanpanya, apt akan mencari paket bernama headscale.deb di repositori Anda dan gagal.

Paket tersebut membuat pengguna sistem headscale, menulis /etc/headscale/config.yaml default, dan menginstal unit systemd. Paket ini tidak memulai layanan, dan urutan tersebut sudah benar. Konfigurasi yang disertakan mengarahkan server_url ke http://127.0.0.1:8080, yang bukan alamat yang dapat dijangkau klien mana pun milik Anda. Karena itu, layanan yang dimulai sekarang akan memiliki konfigurasi yang salah meskipun berhasil berjalan. Menjalankan sudo systemctl is-active headscale pada tahap ini menampilkan inactive. Hal tersebut memang diharapkan, bukan kesalahan.

Konfigurasikan server_url sebelum memulai layanan

Edit /etc/headscale/config.yaml dengan sudo nano /etc/headscale/config.yaml, atau terapkan tiga perubahan yang sama dengan sed. Simpan salinan asli karena file ini panjang dan memiliki banyak komentar. File tersebut merupakan referensi terbaik untuk pengaturan lainnya.

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 adalah alamat yang ditulis headscale ke setiap pendaftaran klien. Setelah itu, klien akan selalu terhubung ke string yang persis sama. Karena itu, alamat tersebut harus berupa nama publik dengan https:// di depannya, bukan 127.0.0.1.

listen_addr adalah alamat tempat proses melakukan bind. Biarkan pada loopback. Reverse proxy pada server yang sama menangani TLS (transport layer security) dan meneruskan koneksi ke proses tersebut. Dengan demikian, tidak ada koneksi dari luar server yang perlu mencapai port 8080.

base_domain adalah sufiks MagicDNS, yaitu domain yang digunakan node untuk mendapatkan nama. Sufiks ini harus berupa fully qualified domain name tanpa titik di akhir, dan harus berbeda dari domain dalam server_url. Jika tidak, kedua ruang nama tersebut akan saling bertabrakan.

Biarkan bagian database apa adanya. Konfigurasi default menggunakan SQLite di /var/lib/headscale/db.sqlite, dalam direktori yang dibuat dan dimiliki oleh paket. SQLite sudah memadai untuk tailnet sebesar ini.

Memulai headscale dan membuktikan bahwa layanan berjalan

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 menampilkan active, sedangkan curl menampilkan 200. enable --now melakukan kedua tugas tersebut: memulai layanan dan menandainya agar dimulai setelah reboot.

Jika is-active menampilkan failed, baca journal dengan sudo journalctl -u headscale -n 50 --no-pager. Kegagalan pada tahap ini hampir selalu disebabkan oleh file konfigurasi, karena headscale mengurai seluruh file sebelum membuka socket. Indentasi yang salah atau kunci yang tidak dikenal akan menghentikan proses sebelum apa pun mulai mendengarkan. Perbaiki file tersebut, lalu jalankan sudo systemctl restart headscale. Setiap perubahan konfigurasi berikutnya memerlukan restart yang sama. Setelah itu, klien akan tersambung kembali secara otomatis. Jika unit systemd masih baru bagi Anda, menjalankan layanan dan timer sendiri dengan systemd membahas perintah yang digunakan di sini.

Periksa file status saat Anda masih berada di shell:

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

Kedua baris dimulai dengan headscale, yaitu pengguna tanpa hak istimewa yang dibuat oleh paket tersebut. noise_private.key adalah identitas server bagi kliennya. Jangan menghapusnya. Jika Anda menghapusnya, headscale akan membuat identitas baru dan setiap node harus mendaftar lagi.

Menempatkan TLS di depan headscale

Klien harus mengakses server_url melalui HTTPS. Caddy adalah cara paling singkat karena Caddy meminta dan memperbarui sertifikat secara otomatis.

sudo apt install -y caddy

Ganti /etc/caddy/Caddyfile dengan blok dari dokumentasi 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

validate menampilkan adapted config to JSON jika berkas berhasil diproses. Peringatan bahwa berkas belum diformat hanya bersifat kosmetik. Dari laptop Anda, curl -sS -o /dev/null -w '%{http_code}\\n' https://headscale.example.com/health juga seharusnya menampilkan 200. Pemeriksaan tunggal ini membuktikan bahwa DNS, firewall, sertifikat, dan proxy bekerja bersama.

Berikut detail proxy yang sering menghabiskan waktu. Koneksi kontrol Tailscale adalah HTTP upgrade, dimulai dengan POST, bukan GET, dan nilai header Upgrade adalah tailscale-control-protocol. Caddy meneruskan koneksi tersebut tanpa konfigurasi tambahan. nginx tidak melakukannya, sehingga front end nginx memerlukan 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;
    }
}

Jika baris tersebut dihilangkan, permintaan biasa tetap berhasil. Karena itu, /health mengembalikan 200 dan semuanya tampak normal, tetapi koneksi kontrol yang berlangsung lama tidak pernah terbentuk. Akibatnya, node mendaftar lalu tetap offline. Jika Anda memilih nginx, Certbot di Ubuntu 24.04 dengan nginx membahas bagian sertifikat.

Port yang harus dibuka di UFW

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

Port 443 membawa semua komunikasi klien. Port 80 hanya digunakan untuk challenge HTTP ACME (automatic certificate management environment) dan pengalihan ke HTTPS. Caddy juga memerlukannya untuk memperoleh sertifikat.

Port 8080 tetap ditutup. listen_addr adalah 127.0.0.1:8080, sehingga proxy mengakses headscale melalui antarmuka loopback dan tidak memerlukan aturan firewall. Membuka port 8080 ke internet akan memberikan klien saluran kontrol cleartext tanpa manfaat. Perhatikan bahwa sebagian besar penyedia menjalankan firewall kedua di panel kontrol mereka, terpisah dari UFW. Karena itu, port dapat terbuka pada server, tetapi tetap tertutup di sisi edge. Dasar-dasar firewall UFW pada VPS menjelaskan sintaks aturan secara lebih terperinci.

Buat pengguna dan kunci preauth

sudo headscale users create alice
sudo headscale users list

Perintah headscale adalah klien. Perintah ini berkomunikasi dengan daemon yang sedang berjalan melalui unix socket di /var/run/headscale/headscale.sock, yang memiliki mode 0770 dan dimiliki oleh grup headscale. Ada dua hal yang perlu diperhatikan. Perintah ini gagal saat layanan berhenti, yang menjadi alasan lain mengapa urutan dalam panduan ini penting. Perintah ini juga memerlukan sudo, kecuali Anda menambahkan akun sendiri ke grup headscale.

users list menampilkan ID di sebelah setiap nama. Anda memerlukan nomor tersebut karena perintah key menerima ID pengguna numerik, bukan nama.

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

Kunci hanya ditampilkan satu kali. Salin sekarang. Kunci preauth hanya dapat digunakan satu kali dan berlaku selama satu jam, kecuali Anda menentukan lain. Karena itu, --expiration 24h sebaiknya ditetapkan saat Anda masih melakukan pengujian. Tambahkan --reusable untuk kunci yang mendaftarkan beberapa mesin. Perlakukan kunci tersebut seperti kata sandi karena siapa pun yang memilikinya dapat bergabung ke jaringan Anda.

Hubungkan klien pertama dengan --login-server

Pada mesin yang ingin Anda gabungkan:

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 menampilkan alamat yang ditetapkan headscale, misalnya 100.64.0.1. Kembali ke server, sudo headscale nodes list menampilkan node beserta ID, pengguna, dan status online-nya.

Nilai --login-server harus sama persis dengan server_url, termasuk skema dan tanpa garis miring di akhir. Keduanya dibandingkan sebagai string. Ketidaksesuaian membuat klien mendaftar ke satu alamat, lalu diarahkan untuk berkomunikasi dengan alamat lain.

Mesin yang sebelumnya masuk ke layanan terkelola Tailscale akan tetap menggunakan login tersebut. Jalankan sudo tailscale logout terlebih dahulu, lalu jalankan tailscale up dengan --login-server.

Jika --auth-key tidak disertakan, klien akan menampilkan URL. Buka URL tersebut. Halamannya menampilkan identifier untuk percobaan pendaftaran itu, yang kemudian Anda setujui di server:

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

Formulir tersebut lebih praktis untuk laptop pribadi. Preauth keys lebih baik untuk segala sesuatu yang dijalankan melalui skrip karena tidak memerlukan orang untuk memantaunya.

DERP dan pihak yang meneruskan traffic saat jalur langsung gagal

DERP (designated encrypted relay for packets) adalah jalur fallback. Jika dua node tidak dapat membuka koneksi WireGuard langsung, biasanya karena keduanya berada di balik NAT (network address translation) yang ketat, keduanya mengirim paket melalui relay. Relay tidak menyimpan key, sehingga tidak dapat membaca traffic Anda. Namun, relay dapat melihat node mana yang berkomunikasi dan jumlah data yang berpindah.

Pahami fungsi konfigurasi default. Headscale dirilis dengan konfigurasi yang menunjuk ke https://controlplane.tailscale.com/derpmap/default menggunakan auto_update_enabled: true dan update_frequency: 3h. Dengan demikian, control plane berada di bawah kendali Anda, sedangkan relay menggunakan milik Tailscale. Bagi sebagian besar pengguna, ini merupakan kompromi yang wajar. Jika tidak, jalankan relay sendiri.

Untuk menjalankan relay sendiri, tetapkan enabled: true di bawah derp.server dalam config.yaml, mulai ulang headscale, lalu buka port STUN (session traversal utilities for NAT) dengan sudo ufw allow 3478/udp. File konfigurasi menyatakan persyaratan ini secara jelas: server_url harus menggunakan https karena DERP memerlukan TLS. Mengosongkan daftar derp.urls akan menghapus relay milik Tailscale dari peta. Jika Anda melakukan ini tanpa relay tertanam yang berfungsi, setiap pasangan node yang tidak dapat terhubung secara langsung sama sekali tidak dapat terhubung.

Dari sisi client, tailscale netcheck menampilkan latensi ke setiap region relay yang dikenalnya, sedangkan tailscale status menandai setiap peer sebagai direct dengan alamat atau relay dengan kode region. Peer yang tetap berada pada relay menunjukkan masalah NAT, bukan masalah headscale.

Mengapa node ditampilkan sebagai offline?

Proxy menjatuhkan proses upgrade. Ini penyebab yang umum. Tandanya, semua hal lain terlihat normal: /health mengembalikan 200, headscale nodes list menampilkan node, tetapi node tidak pernah online. Koneksi kontrol menggunakan POST yang membawa Upgrade: tailscale-control-protocol. Proxy yang tidak meneruskannya akan memutus satu-satunya kanal untuk melaporkan status node. Bandingkan konfigurasi nginx Anda dengan blok map di atas, atau beralih ke Caddy untuk memastikan proxy bukan penyebabnya.

server_url berubah setelah node terdaftar. Node terus mencoba terhubung menggunakan nilai yang diberikan saat pendaftaran. Jika Anda mengubahnya, jalankan sudo tailscale up --login-server https://headscale.example.com --force-reauth pada setiap node.

Client tidak berjalan. Pada node, jalankan sudo systemctl is-active tailscaled dan sudo journalctl -u tailscaled -n 50 --no-pager. Client yang tidak dapat me-resolve atau menjangkau domain Anda akan mencatat percobaan ulang di sana.

Key kedaluwarsa. Dibahas di bagian berikutnya.

Untuk memantau sisi server saat melakukan pengujian, jalankan sudo journalctl -u headscale -f di VPS dan mulai ulang tailscaled pada client. Node yang berhasil menjangkau headscale akan segera menghasilkan baris log. Jika tidak ada keluaran, berarti permintaan tidak sampai. Periksa DNS, firewall, dan proxy sebelum memeriksa headscale.

Masa berlaku key dan node yang berhenti bekerja beberapa minggu kemudian

Ada dua masa berlaku yang terpisah. Kesalahan membedakan keduanya dapat membuang waktu.

Preauth key sengaja memiliki masa berlaku singkat. Nilai default-nya adalah satu jam dan satu kali penggunaan. Jika tailscale up menolak key tersebut, buat key baru di server, bukan dengan mengedit apa pun di client.

Node key adalah bagian yang berlaku lebih lama. Bagian node dalam config.yaml menetapkan expiry: 0, sedangkan 0 berarti tidak ada masa berlaku default: node yang telah didaftarkan tetap valid sampai Anda membuatnya kedaluwarsa. Node bertag tidak pernah kedaluwarsa. Tetapkan expiry: 180d jika Anda ingin masa berlaku registrasi berakhir secara otomatis, dan pahami konsekuensinya: setiap node tanpa tag kemudian memerlukan sudo tailscale up --login-server https://headscale.example.com --force-reauth sesuai jadwal tersebut, dan server tanpa antarmuka grafis yang tidak diautentikasi ulang akan terputus dari jaringan dengan sendirinya.

Lakukan secara manual jika seseorang kehilangan laptop. sudo headscale nodes list menampilkan ID node tersebut, kemudian sudo headscale nodes expire -i 3 mengeluarkan node itu dari sesi, dan sudo headscale nodes delete -i 3 menghapusnya sepenuhnya dari jaringan.

Cadangan dan pemutakhiran

/var/lib/headscale dan /etc/headscale secara bersama-sama membentuk seluruh server. Hentikan layanan sebelum menyalinnya, karena SQLite mungkin masih memproses operasi tulis dan database yang disalin saat sedang digunakan dapat menjadi tidak konsisten.

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

Pindahkan kedua file tersebut keluar dari server. File-file itu berisi kunci privat dan semua registrasi, sehingga harus dilindungi dengan tingkat kehati-hatian yang sama seperti server itu sendiri. cadangan restic dari VPS membahas cara menjadwalkan proses ini dan mengenkripsinya.

Pemutakhiran mengulangi proses instalasi: unduh .deb, sudo apt install ./headscale.deb yang baru, lalu mulai ulang layanan dan jalankan kembali pemeriksaan is-active dan /health. Sejak 0.29, jalur pemutakhiran bersifat ketat. Melewati versi minor akan diblokir, begitu juga menurunkan versi ke versi minor yang lebih lama. Lakukan pemutakhiran satu versi minor setiap kali, buat cadangan sebelum setiap langkah, dan baca catatan rilis versi tersebut terlebih dahulu, karena rilis yang sama mengubah perilaku kebijakan ACL dan memindahkan beberapa kunci konfigurasi.

FAQ

Mengapa headscale gagal memulai tepat setelah saya menginstal .deb?

Paket menginstal unit, tetapi membiarkan layanan tetap berhenti, dan /etc/headscale/config.yaml bawaan hanyalah templat, bukan konfigurasi yang dapat digunakan. Edit server_url, listen_addr, dan base_domain terlebih dahulu, lalu jalankan sudo systemctl enable --now headscale dan lakukan konfirmasi dengan sudo systemctl is-active headscale. Jika masih gagal, sudo journalctl -u headscale -n 50 --no-pager akan menunjukkan masalahnya. Pada tahap ini, penyebabnya hampir selalu kesalahan YAML karena headscale mengurai seluruh berkas sebelum mengikat port.

Apakah saya tetap perlu menginstal klien Tailscale biasa di mesin saya?

Ya. Headscale hanya menggantikan server kontrol. Setiap node menjalankan klien resmi dari Tailscale, lalu Anda mengarahkannya ke server dengan sudo tailscale up --login-server https://headscale.example.com. Flag tersebut tersedia di klien standar, sehingga tidak ada yang perlu ditambal atau dibangun ulang.

Apakah traffic saya melewati server headscale?

Biasanya tidak. Headscale mengoordinasikan jaringan serta membagikan key dan alamat, sedangkan jalur data menggunakan WireGuard secara langsung antar-node Anda. Traffic hanya mengambil jalur memutar ketika dua node tidak dapat saling menjangkau secara langsung dan beralih ke relay DERP. Dengan konfigurasi yang disertakan, relay tersebut adalah relay publik milik Tailscale. Jalankan tailscale status pada sebuah node untuk melihat apakah peer tertentu berstatus direct atau menggunakan relay.

Mengapa node saya tetap offline setelah melakukan registrasi?

Node yang muncul di headscale nodes list tetapi tidak pernah online biasanya kehilangan koneksi kontrolnya pada reverse proxy. Koneksi tersebut adalah HTTP upgrade yang dikirim sebagai POST dengan header Upgrade: tailscale-control-protocol. nginx akan menghapusnya kecuali Anda menambahkan blok map $http_upgrade $connection_upgrade dan baris proxy_set_header yang sesuai. Caddy meneruskannya tanpa konfigurasi tambahan, sehingga dapat digunakan dengan cepat untuk menguji apakah proxy menjadi penyebabnya.

Apakah saya memerlukan nama domain dan TLS untuk headscale?

Dalam praktiknya, ya. Klien terhubung ke string apa pun yang Anda masukkan ke server_url, sertifikat diterbitkan untuk nama dan bukan untuk alamat IP tanpa nama domain, dan berkas konfigurasi menyatakan bahwa DERP memerlukan TLS. Domain dan Caddy dapat disiapkan dalam waktu sekitar lima menit, serta menyediakan endpoint HTTPS yang memperbarui sertifikatnya sendiri. Menjalankan server kontrol melalui HTTP biasa berarti setiap komunikasi klien dengannya melintasi internet tanpa enkripsi.