SSD Nodes Learn RAM 8GB — $66/ปี
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-08-01

วิธีติดตั้ง Headscale ทำ Tailscale Server ส่วนตัวบน VPS

เรียนรู้วิธีติดตั้ง Headscale เพื่อรัน Tailscale Control Server บน VPS ของคุณเอง พร้อมขั้นตอนการตั้งค่า server_url และการเชื่อมต่อโหนดแรกเข้าสู่เครือข่ายอย่างถูกต้อง

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

headscale คืออะไร

headscale คือการนำเซิร์ฟเวอร์ควบคุมของ Tailscale มาติดตั้งใช้งานด้วยตนเอง ดังนั้นเครื่องที่ทำหน้าที่ประสานงานเครือข่ายส่วนตัวของคุณจึงเป็น VPS ที่คุณเป็นเจ้าของ นี่เป็นโครงการชุมชนและไม่ได้ดำเนินการโดย Tailscale Inc. โดยทุกเครื่องยังคงรันไคลเอนต์ tailscale อย่างเป็นทางการ ซึ่งชี้ไปยังเซิร์ฟเวอร์ของคุณด้วยแฟล็ก --login-server เพียงตัวเดียว

เซิร์ฟเวอร์ควบคุมคือส่วนที่ทำหน้าที่ระบุว่าใครเป็นสมาชิกของเครือข่าย โดยจะกำหนดที่อยู่จาก 100.64.0.0/10 ให้กับแต่ละโหนด แจกจ่ายกุญแจสาธารณะ และแจ้งให้โหนดทราบว่าต้องเชื่อมต่อหากันอย่างไร อุโมงค์เชื่อมต่อยังคงเป็น WireGuard ซึ่งสร้างขึ้นแบบโหนดต่อโหนด ทราฟฟิกระหว่างเครื่องสองเครื่องของคุณจะไม่ผ่านกล่อง headscale เว้นแต่ว่าจะไม่สามารถสร้างเส้นทางตรงได้และโหนดต้องเปลี่ยนไปใช้รีเลย์แทน

headscale รองรับหนึ่ง tailnet (เครือข่าย Tailscale หนึ่งเครือข่าย) ต่อหนึ่งอินสแตนซ์ ซึ่งโครงการระบุว่าเหมาะสมสำหรับการใช้งานส่วนตัวหรือองค์กรขนาดเล็ก หากมีเครื่องเพียง 3 หรือ 4 เครื่อง การใช้ VPN แบบ WireGuard บน VPS ที่คุณเป็นเจ้าของ จะเป็นซอฟต์แวร์ที่ต้องดูแลรักษาน้อยกว่าและมีโอกาสเกิดข้อผิดพลาดน้อยกว่า headscale จะคุ้มค่าเมื่อคุณไม่ต้องการเขียนบล็อก [Peer] ด้วยตนเองสำหรับแล็ปท็อปเครื่องใหม่ทุกเครื่อง สำหรับการเปรียบเทียบทั้งสองรูปแบบในวงกว้าง โปรดดูที่ ความแตกต่างระหว่าง WireGuard และ Tailscale

สิ่งที่ต้องเตรียมก่อนการติดตั้ง

  • VPS ที่รัน Ubuntu 24.04 พร้อมที่อยู่ IPv4 สาธารณะและสิทธิ์ sudo หากเป็นเซิร์ฟเวอร์ใหม่ ให้ดำเนินการตาม สิบนาทีแรกบน VPS ใหม่ ก่อน
  • ระเบียน DNS A ที่ชี้ไปยังที่อยู่นั้น คู่มือนี้ใช้ headscale.example.com
  • โดเมนที่สองหรือโดเมนย่อยสำหรับ MagicDNS คู่มือนี้ใช้ tailnet.example.net โดยต้องไม่เป็นโดเมนเดียวกับที่ใช้ใน server_url
  • เครื่องลูกข่ายหนึ่งเครื่องเพื่อเข้าร่วมเครือข่าย โดยรันระบบปฏิบัติการ Linux, macOS, Windows, Android หรือ iOS

การติดตั้ง headscale จากไฟล์ .deb อย่างเป็นทางการ

โครงการนี้เผยแพร่แพ็กเกจ .deb ไว้บนหน้า GitHub releases ณ เดือนกรกฎาคม 2026 เวอร์ชันปัจจุบันคือ 0.29.3 ให้ตรวจสอบสถาปัตยกรรมของระบบคุณก่อน เนื่องจากชื่อไฟล์จะระบุสถาปัตยกรรมไว้

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

คำสั่งดังกล่าวจะแสดงผล amd64 บน VPS x86 ทั่วไป และแสดงผล arm64 บนแผนบริการประเภท Ampere หรือ Graviton ให้นำค่าที่ได้ไปใส่ในตัวแปรด้านล่างนี้

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

จำเป็นต้องใส่ ./ ไว้หน้าชื่อไฟล์ หากไม่มี apt จะค้นหาแพ็กเกจที่ชื่อ headscale.deb ในคลังซอฟต์แวร์ของคุณและจะเกิดข้อผิดพลาด

แพ็กเกจนี้จะสร้างผู้ใช้ระบบ headscale, เขียนไฟล์ /etc/headscale/config.yaml เริ่มต้น และติดตั้ง systemd unit โดยจะ ไม่ เริ่มการทำงานของเซอร์วิสโดยอัตโนมัติ ซึ่งเป็นลำดับขั้นตอนที่ถูกต้องแล้ว การตั้งค่าที่มาพร้อมกับซอฟต์แวร์จะชี้ server_url ไปที่ http://127.0.0.1:8080 ซึ่งไม่ใช่ที่อยู่ที่ไคลเอนต์ของคุณจะเข้าถึงได้ ดังนั้นหากเริ่มเซอร์วิสในตอนนี้ก็จะทำงานไม่ถูกต้องแม้ว่าจะเปิดใช้งานได้ก็ตาม การรันคำสั่ง sudo systemctl is-active headscale ในขั้นตอนนี้จะแสดงผล inactive ซึ่งเป็นสิ่งที่คาดการณ์ไว้ ไม่ใช่ข้อผิดพลาดแต่อย่างใด

กำหนดค่า server_url ก่อนเริ่มใช้งานบริการ

แก้ไขไฟล์ /etc/headscale/config.yaml ด้วย sudo nano /etc/headscale/config.yaml หรือใช้คำสั่ง sed เพื่อปรับเปลี่ยนการตั้งค่าทั้ง 3 จุด ให้สำรองไฟล์ต้นฉบับไว้เสมอ เนื่องจากไฟล์ดังกล่าวมีความยาวและมีคำอธิบายประกอบจำนวนมาก ซึ่งถือเป็นข้อมูลอ้างอิงที่ดีที่สุดสำหรับการตั้งค่าส่วนที่เหลือ

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 คือที่อยู่ที่ headscale จะเขียนลงในการลงทะเบียนของไคลเอนต์ทุกเครื่อง ไคลเอนต์จะเชื่อมต่อไปยังสตริงดังกล่าวตลอดไป ดังนั้นค่านี้จะต้องเป็นชื่อสาธารณะที่ขึ้นต้นด้วย https:// เท่านั้น ห้ามใช้ 127.0.0.1

listen_addr คือตำแหน่งที่โพรเซสจะทำการ bind ให้คงค่าไว้ที่ loopback เนื่องจาก reverse proxy ที่อยู่บนเครื่องเดียวกันจะทำหน้าที่จัดการ TLS (transport layer security) และส่งต่อข้อมูลเข้ามา ดังนั้นจึงไม่มีความจำเป็นที่บริการภายนอกจะต้องเข้าถึงพอร์ต 8080 โดยตรง

base_domain คือส่วนต่อท้ายของ MagicDNS ซึ่งเป็นโดเมนที่โหนดของคุณจะได้รับชื่อภายใต้โดเมนนั้น ค่านี้จะต้องเป็นชื่อโดเมนแบบสมบูรณ์ (FQDN) โดยไม่มีจุดปิดท้าย และจะต้องเป็นโดเมนที่แตกต่างจากที่ระบุไว้ใน server_url มิฉะนั้นพื้นที่ชื่อทั้งสองส่วนจะเกิดการทับซ้อนกัน

ไม่ต้องแก้ไขส่วนของฐานข้อมูล ค่าเริ่มต้นคือ SQLite ที่ตำแหน่ง /var/lib/headscale/db.sqlite ซึ่งอยู่ในไดเรกทอรีที่แพ็กเกจสร้างขึ้นและเป็นเจ้าของอยู่แล้ว โดย SQLite เพียงพอสำหรับการใช้งาน tailnet ในขนาดเท่านี้

เริ่มการทำงานของ headscale และตรวจสอบสถานะ

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 จะแสดงผล active และ curl จะแสดงผล 200 ส่วน enable --now จะทำหน้าที่ทั้งสองอย่าง คือการเริ่มบริการและกำหนดให้เริ่มทำงานโดยอัตโนมัติหลังจากรีบูตเครื่อง

หาก is-active แสดงผล failed ให้ตรวจสอบบันทึกการทำงานด้วย sudo journalctl -u headscale -n 50 --no-pager ความผิดพลาดในขั้นตอนนี้มักเกิดจากไฟล์การตั้งค่าเสมอ เนื่องจาก headscale จะตรวจสอบไฟล์ทั้งหมดก่อนที่จะเปิด socket ดังนั้นการย่อหน้าผิดหรือการใช้คีย์ที่ไม่รู้จักจะทำให้กระบวนการหยุดทำงานก่อนที่จะมีการเปิดพอร์ตใดๆ ให้แก้ไขไฟล์แล้วจึงใช้ sudo systemctl restart headscale การเปลี่ยนแปลงการตั้งค่าในภายหลังทั้งหมดจำเป็นต้องรีสตาร์ทด้วยวิธีเดียวกันนี้ ไคลเอนต์จะเชื่อมต่อใหม่โดยอัตโนมัติหลังจากนั้น หากคุณยังไม่คุ้นเคยกับ systemd สามารถศึกษา การรันบริการและตัวตั้งเวลาด้วยตนเองโดยใช้ systemd ซึ่งครอบคลุมคำสั่งที่ใช้ในที่นี้

ตรวจสอบไฟล์สถานะในขณะที่คุณอยู่ในเชลล์:

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

ทั้งสองบรรทัดจะขึ้นต้นด้วย headscale ซึ่งเป็นผู้ใช้ที่ไม่มีสิทธิ์พิเศษที่แพ็กเกจสร้างขึ้น noise_private.key คืออัตลักษณ์ของเซิร์ฟเวอร์ที่ใช้สื่อสารกับไคลเอนต์ โปรดเก็บไฟล์นี้ไว้ หากคุณลบไฟล์ดังกล่าว headscale จะสร้างไฟล์ใหม่ขึ้นมา และโหนดทุกโหนดจะต้องลงทะเบียนใหม่อีกครั้ง

การติดตั้ง TLS หน้า headscale

ไคลเอนต์ต้องเข้าถึง server_url ผ่าน HTTPS โดย Caddy เป็นเส้นทางที่สั้นที่สุด เนื่องจาก Caddy จะร้องขอและต่ออายุใบรับรองด้วยตัวเอง

sudo apt install -y caddy

แทนที่ /etc/caddy/Caddyfile ด้วยบล็อกการตั้งค่าจากเอกสารประกอบของ 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 จะแสดงผล adapted config to JSON เมื่อไฟล์ถูกประมวลผล คำเตือนเรื่องรูปแบบไฟล์ไม่ถูกต้องเป็นเพียงเรื่องความสวยงามเท่านั้น จากแล็ปท็อปของคุณ คำสั่ง curl -sS -o /dev/null -w '%{http_code}\\n' https://headscale.example.com/health ควรแสดงผล 200 เช่นกัน การตรวจสอบเพียงขั้นตอนเดียวนี้เป็นการยืนยันว่า DNS, ไฟร์วอลล์, ใบรับรอง และพร็อกซีทำงานร่วมกันได้อย่างถูกต้อง

นี่คือรายละเอียดของพร็อกซีที่มักทำให้เสียเวลาในการแก้ไขปัญหา การเชื่อมต่อควบคุมของ Tailscale เป็นการอัปเกรด HTTP ซึ่งเริ่มต้นด้วย POST แทนที่จะเป็น GET และค่าของส่วนหัว Upgrade คือ tailscale-control-protocol โดย Caddy จะส่งผ่านค่านี้ไปโดยไม่ต้องตั้งค่าเพิ่มเติม แต่ nginx ไม่ได้ทำเช่นนั้น ดังนั้น nginx ที่ทำหน้าที่เป็น front end จึงจำเป็นต้องมีแผนผังการอัปเกรด:

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;
    }
}

หากละเว้นบรรทัดเหล่านั้น คำขอทั่วไปจะยังคงสำเร็จ ซึ่งเป็นเหตุผลว่าทำไม /health ถึงคืนค่า 200 และทุกอย่างดูเหมือนปกติ ในขณะที่การเชื่อมต่อควบคุมแบบคงอยู่ยาวนานไม่สามารถสร้างขึ้นได้ ส่งผลให้โหนดของคุณลงทะเบียนแล้วอยู่ในสถานะออฟไลน์ หากคุณเลือกใช้ nginx คุณสามารถดู Certbot บน Ubuntu 24.04 ร่วมกับ nginx สำหรับขั้นตอนการจัดการใบรับรอง

พอร์ตที่ต้องเปิดใน UFW

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

พอร์ต 443 ใช้สำหรับการสื่อสารกับไคลเอนต์ทั้งหมด ส่วนพอร์ต 80 มีไว้สำหรับการทำ ACME (automatic certificate management environment) HTTP challenge และการเปลี่ยนเส้นทางไปยัง HTTPS เท่านั้น โดย Caddy จำเป็นต้องใช้พอร์ตนี้ในการขอใบรับรอง

พอร์ต 8080 ควรปิดไว้ listen_addr คือ 127.0.0.1:8080 ดังนั้นพร็อกซีจะเข้าถึง headscale ผ่านอินเทอร์เฟซ loopback โดยไม่ต้องผ่านกฎของไฟร์วอลล์ การเปิดพอร์ต 8080 สู่สาธารณะจะทำให้ไคลเอนต์ใช้ช่องทางควบคุมแบบข้อความธรรมดา (cleartext) ซึ่งไม่มีประโยชน์ใดๆ โปรดทราบว่าผู้ให้บริการส่วนใหญ่จะมีไฟร์วอลล์อีกชั้นหนึ่งในแผงควบคุม ซึ่งแยกต่างหากจาก UFW ดังนั้นพอร์ตอาจเปิดอยู่บนเซิร์ฟเวอร์แต่ยังคงถูกปิดอยู่ที่ระดับเครือข่ายภายนอก พื้นฐานการใช้งาน UFW บน VPS อธิบายไวยากรณ์ของกฎต่างๆ ไว้โดยละเอียด

การสร้างผู้ใช้และคีย์ preauth

sudo headscale users create alice
sudo headscale users list

คำสั่ง headscale ทำหน้าที่เป็นไคลเอนต์ โดยจะสื่อสารกับ daemon ที่กำลังทำงานอยู่ผ่าน unix socket ที่ /var/run/headscale/headscale.sock ซึ่งกำหนดสิทธิ์ไว้ที่ 0770 และเป็นกรรมสิทธิ์ของกลุ่ม headscale สิ่งนี้ส่งผลให้เกิดสองประเด็นคือ คำสั่งจะทำงานล้มเหลวหากบริการหยุดทำงาน ซึ่งเป็นเหตุผลว่าทำไมลำดับขั้นตอนในคู่มือนี้จึงมีความสำคัญ และจำเป็นต้องใช้ sudo เว้นแต่คุณจะเพิ่มบัญชีผู้ใช้ของคุณเข้าไปในกลุ่ม headscale

users list จะแสดง ID กำกับไว้ข้างชื่อแต่ละรายการ คุณจำเป็นต้องใช้ตัวเลขนั้นเนื่องจากคำสั่ง key ต้องการระบุเป็นตัวเลข ID ของผู้ใช้ ไม่ใช่ชื่อ

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

คีย์จะถูกแสดงออกมาเพียงครั้งเดียว ให้คัดลอกไว้ทันที คีย์ preauth สามารถใช้งานได้เพียงครั้งเดียวและมีอายุการใช้งานหนึ่งชั่วโมงเว้นแต่คุณจะกำหนดเป็นอย่างอื่น ดังนั้นการใช้ --expiration 24h จึงเป็นสิ่งที่ควรทำในระหว่างที่คุณยังอยู่ในขั้นตอนการทดสอบ หากต้องการคีย์ที่สามารถใช้ลงทะเบียนเครื่องได้หลายเครื่อง ให้เพิ่ม --reusable และควรเก็บรักษาคีย์นั้นไว้เหมือนรหัสผ่าน เนื่องจากใครก็ตามที่ถือคีย์นี้สามารถเข้าร่วมเครือข่ายของคุณได้

เชื่อมต่อไคลเอนต์เครื่องแรกของคุณด้วย --login-server

บนเครื่องที่คุณต้องการเชื่อมต่อ:

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 จะแสดงที่อยู่ที่ headscale กำหนดให้ ซึ่งจะมีลักษณะคล้ายกับ 100.64.0.1 กลับไปที่ฝั่งเซิร์ฟเวอร์ sudo headscale nodes list จะแสดงโหนดพร้อมกับ ID, ผู้ใช้งาน และสถานะออนไลน์

ค่าของ --login-server จะต้องตรงกับ server_url ทุกประการ รวมถึงรูปแบบ (scheme) และต้องไม่มีเครื่องหมายทับ (slash) ต่อท้าย ทั้งสองค่าจะถูกเปรียบเทียบในรูปแบบสตริง หากไม่ตรงกันจะทำให้ไคลเอนต์ลงทะเบียนกับที่อยู่หนึ่ง แต่ได้รับคำสั่งให้ไปติดต่อกับอีกที่อยู่หนึ่งแทน

เครื่องที่เคยลงชื่อเข้าใช้บริการโฮสต์ของ Tailscale มาก่อนจะยังคงสถานะการลงชื่อเข้าใช้นั้นไว้ ให้รันคำสั่ง sudo tailscale logout บนเครื่องนั้นก่อน จากนั้นจึงรัน tailscale up ด้วย --login-server

หากคุณละเว้น --auth-key ไว้ ไคลเอนต์จะแสดง URL ออกมาแทน ให้เปิด URL ดังกล่าวแล้วหน้าเว็บจะแสดงตัวระบุสำหรับการลงทะเบียนนั้น ซึ่งคุณจะต้องอนุมัติบนเซิร์ฟเวอร์:

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

รูปแบบดังกล่าวสะดวกกว่าสำหรับแล็ปท็อปส่วนตัวของคุณ ส่วน Preauth keys จะเหมาะสมกว่าสำหรับงานที่ทำผ่านสคริปต์ เนื่องจากไม่จำเป็นต้องมีผู้ใช้งานคอยเฝ้าดูอยู่ตลอดเวลา

DERP และสิ่งที่ทำหน้าที่ส่งต่อทราฟฟิกเมื่อเส้นทางตรงล้มเหลว

DERP (designated encrypted relay for packets) คือเส้นทางสำรอง เมื่อโหนดสองโหนดไม่สามารถเปิดการเชื่อมต่อ WireGuard แบบตรงได้ ซึ่งมักเกิดจากทั้งสองโหนดอยู่หลัง NAT (network address translation) ที่เข้มงวด โหนดเหล่านั้นจะส่งแพ็กเก็ตผ่านรีเลย์แทน รีเลย์ไม่มีกุญแจเข้ารหัส จึงไม่สามารถอ่านทราฟฟิกของคุณได้ แต่รีเลย์จะเห็นว่าโหนดใดกำลังสื่อสารกันและมีปริมาณข้อมูลเคลื่อนย้ายเท่าใด

โปรดทำความเข้าใจว่าการตั้งค่าเริ่มต้นทำงานอย่างไร Headscale ถูกปล่อยออกมาโดยชี้ไปที่ https://controlplane.tailscale.com/derpmap/default พร้อมด้วย auto_update_enabled: true และ update_frequency: 3h ดังนั้นในขณะที่ระนาบควบคุม (control plane) เป็นของคุณ แต่รีเลย์ยังคงเป็นของ Tailscale สำหรับคนส่วนใหญ่ถือเป็นการแลกเปลี่ยนที่ยอมรับได้ หากคุณไม่ต้องการเช่นนั้น คุณสามารถรันรีเลย์ของคุณเองได้

ในการรันรีเลย์ของคุณเอง ให้ตั้งค่า enabled: true ภายใต้ derp.server ใน config.yaml จากนั้นรีสตาร์ท headscale และเปิดพอร์ต STUN (session traversal utilities for NAT) ด้วย sudo ufw allow 3478/udp ไฟล์การตั้งค่าระบุข้อกำหนดไว้อย่างชัดเจนว่า server_url ต้องใช้ https เนื่องจาก DERP จำเป็นต้องใช้ TLS การล้างรายการ derp.urls จะเป็นการนำรีเลย์ของ Tailscale ออกจากแผนผัง และหากคุณทำเช่นนั้นโดยไม่มีรีเลย์แบบฝังตัวที่ทำงานได้ โหนดคู่ใดก็ตามที่ไม่สามารถเชื่อมต่อกันโดยตรงได้ จะไม่สามารถเชื่อมต่อกันได้เลย

จากฝั่งไคลเอนต์ tailscale netcheck จะแสดงค่าความหน่วง (latency) ไปยังแต่ละภูมิภาคของรีเลย์ที่ไคลเอนต์รู้จัก และ tailscale status จะระบุเพียร์แต่ละตัวว่าอยู่ในสถานะ direct พร้อมที่อยู่ หรือ relay พร้อมรหัสภูมิภาค หากเพียร์ติดอยู่ที่สถานะ relay นั่นคือปัญหาของ NAT ไม่ใช่ปัญหาของ headscale

ทำไมโหนดถึงแสดงสถานะเป็นออฟไลน์?

พร็อกซีปฏิเสธการอัปเกรด นี่เป็นสาเหตุที่พบบ่อยที่สุด โดยมีลักษณะคือทุกอย่างดูปกติ: /health ส่งค่ากลับเป็น 200, headscale nodes list แสดงโหนดดังกล่าว แต่โหนดไม่เคยออนไลน์ การเชื่อมต่อควบคุมคือ POST ที่ส่ง Upgrade: tailscale-control-protocol หากพร็อกซีไม่ส่งต่อข้อมูลนี้ จะทำให้ช่องทางเดียวที่ใช้รายงานสถานะโหนดถูกตัดขาด ให้ตรวจสอบการตั้งค่า nginx ของคุณเทียบกับบล็อก map ด้านบน หรือเปลี่ยนไปใช้ Caddy เพื่อตัดปัญหาเรื่องพร็อกซีออกไป

server_url เปลี่ยนแปลงหลังจากโหนดลงทะเบียนแล้ว โหนดจะพยายามเชื่อมต่อด้วยค่าที่ได้รับตอนลงทะเบียนเสมอ หากคุณแก้ไขค่านี้ ให้รัน sudo tailscale up --login-server https://headscale.example.com --force-reauth บนโหนดแต่ละตัว

ไคลเอนต์ไม่ได้ทำงาน บนโหนด ให้ตรวจสอบ sudo systemctl is-active tailscaled และ sudo journalctl -u tailscaled -n 50 --no-pager ไคลเอนต์ที่ไม่สามารถแก้ไขชื่อโดเมนหรือเข้าถึงโดเมนของคุณได้จะบันทึกการพยายามเชื่อมต่อซ้ำไว้ที่นั่น

คีย์หมดอายุ รายละเอียดอยู่ในหัวข้อถัดไป

หากต้องการตรวจสอบฝั่งเซิร์ฟเวอร์ในระหว่างการทดสอบ ให้รัน sudo journalctl -u headscale -f บน VPS และรีสตาร์ท tailscaled บนไคลเอนต์ โหนดที่เข้าถึง headscale ได้จะสร้างบรรทัดบันทึกข้อมูลขึ้นมาทันที หากไม่มีข้อมูลปรากฏ แสดงว่าคำขอส่งมาไม่ถึง ให้ตรวจสอบ DNS, ไฟร์วอลล์ และพร็อกซีก่อนที่จะตรวจสอบ headscale

การหมดอายุของคีย์ และโหนดที่หยุดทำงานในอีกหลายสัปดาห์ต่อมา

มีการหมดอายุอยู่สองประเภทที่แยกจากกัน และการสับสนระหว่างสองสิ่งนี้ทำให้เสียเวลา

คีย์ประเภท Preauth ถูกออกแบบมาให้หมดอายุอย่างรวดเร็ว โดยค่าเริ่มต้นคือหนึ่งชั่วโมงและใช้งานได้เพียงครั้งเดียว หาก tailscale up ปฏิเสธคีย์ดังกล่าว ให้สร้างคีย์ใหม่บนเซิร์ฟเวอร์แทนการแก้ไขค่าใดๆ บนไคลเอนต์

คีย์ของโหนดเป็นส่วนที่มีอายุการใช้งานยาวนาน ส่วน node ของ config.yaml จะกำหนดค่า expiry: 0 และการตั้งค่า 0 หมายความว่าจะไม่มีการหมดอายุโดยอัตโนมัติ โหนดที่ลงทะเบียนไว้จะมีสถานะใช้งานได้จนกว่าคุณจะสั่งให้หมดอายุ ส่วนโหนดที่ติดแท็ก (Tagged nodes) จะไม่มีวันหมดอายุไม่ว่ากรณีใดๆ ให้ตั้งค่า expiry: 180d หากคุณต้องการให้การลงทะเบียนมีวันหมดอายุ และต้องเข้าใจผลลัพธ์ที่จะเกิดขึ้น: โหนดที่ไม่ได้ติดแท็กทุกโหนดจะต้องทำการ sudo tailscale up --login-server https://headscale.example.com --force-reauth ตามกำหนดเวลานั้น และเซิร์ฟเวอร์แบบ headless ที่ไม่มีใครทำการยืนยันตัวตนใหม่จะหลุดออกจากเครือข่ายโดยอัตโนมัติ

ให้ดำเนินการด้วยตนเองเมื่อมีคนทำแล็ปท็อปสูญหาย คำสั่ง sudo headscale nodes list จะแสดง ID ให้คุณทราบ จากนั้น sudo headscale nodes expire -i 3 จะทำการออกจากระบบของโหนดนั้น และ sudo headscale nodes delete -i 3 จะลบโหนดนั้นออกจากเครือข่ายโดยสมบูรณ์

การสำรองข้อมูลและการอัปเกรด

/var/lib/headscale และ /etc/headscale รวมกันถือเป็นข้อมูลทั้งหมดของเซิร์ฟเวอร์ ให้หยุดการทำงานของเซอร์วิสก่อนทำการคัดลอกไฟล์เหล่านี้ เนื่องจาก SQLite อาจมีการเขียนข้อมูลค้างอยู่ และฐานข้อมูลที่ถูกคัดลอกในขณะที่มีการใช้งานอาจมีความไม่สอดคล้องกันของข้อมูล

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

ให้ย้ายไฟล์ทั้งสองออกจากเครื่องเซิร์ฟเวอร์ ไฟล์เหล่านี้ประกอบด้วยคีย์ส่วนตัวและการลงทะเบียนทั้งหมด จึงควรได้รับการดูแลเช่นเดียวกับตัวเซิร์ฟเวอร์เอง บทความ การสำรองข้อมูลด้วย restic จาก VPS ครอบคลุมวิธีการดำเนินการดังกล่าวตามกำหนดเวลาและมีการเข้ารหัสข้อมูล

การอัปเกรดให้ทำซ้ำขั้นตอนการติดตั้ง: ดาวน์โหลด .deb และ sudo apt install ./headscale.deb เวอร์ชันใหม่ จากนั้นรีสตาร์ทและรันการตรวจสอบ is-active และ /health อีกครั้ง ตั้งแต่เวอร์ชัน 0.29 เป็นต้นมา เส้นทางการอัปเกรดมีความเข้มงวด ระบบจะบล็อกการข้ามเวอร์ชันย่อย รวมถึงการดาวน์เกรดไปยังเวอร์ชันย่อยที่เก่ากว่า ให้ดำเนินการทีละเวอร์ชันย่อย สำรองข้อมูลก่อนเริ่มแต่ละขั้นตอน และอ่านบันทึกประจำรุ่น (release notes) ของเวอร์ชันนั้นๆ ก่อนเสมอ เนื่องจากบางรุ่นมีการเปลี่ยนแปลงพฤติกรรมของนโยบาย ACL และมีการย้ายคีย์การตั้งค่าหลายรายการ

FAQ

ทำไม headscale ถึงไม่เริ่มทำงานทันทีหลังจากที่ผมติดตั้งไฟล์ .deb?

แพ็กเกจจะติดตั้ง unit ไว้แต่จะปล่อยให้ service หยุดทำงานอยู่ และค่าเริ่มต้นใน /etc/headscale/config.yaml เป็นเพียงเทมเพลตไม่ใช่การตั้งค่าที่ใช้งานได้จริง ให้แก้ไขไฟล์ server_url, listen_addr และ base_domain ก่อน จากนั้นรันคำสั่ง sudo systemctl enable --now headscale และตรวจสอบสถานะด้วย sudo systemctl is-active headscale หากยังคงล้มเหลว sudo journalctl -u headscale -n 50 --no-pager จะระบุสาเหตุของปัญหา ซึ่งในขั้นตอนนี้มักเกิดจากข้อผิดพลาดในไฟล์ YAML เนื่องจาก headscale จะอ่านไฟล์ทั้งหมดก่อนที่จะทำการ bind พอร์ต

ผมยังต้องติดตั้ง Tailscale client แบบปกติบนเครื่องของผมหรือไม่?

ใช่ครับ Headscale จะทำหน้าที่แทนเฉพาะ control server เท่านั้น ทุกโหนดจะรัน client อย่างเป็นทางการจาก Tailscale และคุณต้องชี้ client ไปยังเซิร์ฟเวอร์ของคุณด้วย sudo tailscale up --login-server https://headscale.example.com แฟล็กดังกล่าวมีอยู่ใน client มาตรฐานอยู่แล้ว จึงไม่จำเป็นต้องแก้ไขหรือคอมไพล์โปรแกรมใหม่

ทราฟฟิกของผมจะวิ่งผ่านเซิร์ฟเวอร์ headscale หรือไม่?

โดยปกติแล้วจะไม่ใช่ครับ Headscale ทำหน้าที่ประสานงานเครือข่ายและแจกจ่ายคีย์รวมถึงที่อยู่ IP ในขณะที่เส้นทางข้อมูลจะเป็น WireGuard ระหว่างโหนดของคุณโดยตรง ทราฟฟิกจะอ้อมผ่านเซิร์ฟเวอร์ก็ต่อเมื่อโหนดสองโหนดไม่สามารถเชื่อมต่อกันโดยตรงได้และต้องเปลี่ยนไปใช้ DERP relay แทน ซึ่งในการตั้งค่าที่ให้มา relay เหล่านี้จะเป็น relay สาธารณะของ Tailscale ให้รันคำสั่ง tailscale status บนโหนดเพื่อตรวจสอบว่า peer ที่กำหนดนั้นเชื่อมต่อแบบ direct หรือผ่าน relay

ทำไมโหนดของผมถึงยังคงสถานะออฟไลน์หลังจากลงทะเบียนแล้ว?

โหนดที่ปรากฏใน headscale nodes list แต่ไม่เคยออนไลน์ มักเกิดจากการสูญเสียการเชื่อมต่อ control connection ที่ reverse proxy การเชื่อมต่อดังกล่าวคือ HTTP upgrade ที่ส่งผ่าน POST พร้อมเฮดเดอร์ Upgrade: tailscale-control-protocol ซึ่ง nginx จะตัดการเชื่อมต่อนี้ทิ้งหากคุณไม่ได้เพิ่มบล็อก map $http_upgrade $connection_upgrade และบรรทัด proxy_set_header ที่สอดคล้องกัน ส่วน Caddy จะส่งต่อการเชื่อมต่อนี้โดยไม่ต้องตั้งค่าเพิ่มเติม ซึ่งทำให้เป็นวิธีที่รวดเร็วในการทดสอบว่าปัญหาเกิดจาก proxy หรือไม่

ผมจำเป็นต้องมีชื่อโดเมนและ TLS สำหรับ headscale หรือไม่?

ในทางปฏิบัติแล้วจำเป็นครับ Client จะเชื่อมต่อไปยังสตริงที่คุณระบุไว้ใน server_url โดยใบรับรองจะออกให้สำหรับชื่อโดเมนไม่ใช่สำหรับ IP address เปล่าๆ และไฟล์การตั้งค่าระบุว่า DERP จำเป็นต้องใช้ TLS การใช้โดเมนร่วมกับ Caddy ใช้เวลาเพียงประมาณห้านาทีและจะทำให้คุณได้ HTTPS endpoint ที่ต่ออายุใบรับรองอัตโนมัติ การรัน control server ผ่าน HTTP ธรรมดาจะทำให้การสื่อสารทั้งหมดระหว่าง client กับเซิร์ฟเวอร์ถูกส่งผ่านอินเทอร์เน็ตโดยไม่มีการเข้ารหัส