Vấn đề với Tailscale mặc định
Tailscale rất tiện — cài vào, đăng nhập, các máy kết nối với nhau ngay mà không cần đụng firewall. Nhưng sau một thời gian dùng cho hệ thống production, mình bắt đầu để ý ra điểm gai mắt: toàn bộ control plane đang nằm trên server của Tailscale Inc. Mọi thông tin về thiết bị, IP, routing policy đều phụ thuộc vào một bên thứ ba mà mình không kiểm soát được.
Cá nhân dùng thì không ảnh hưởng gì. Nhưng với công ty phải tuân thủ data residency — GDPR, PDPA hay internal security policy — thì đây là vấn đề nghiêm túc cần giải quyết, không phải lo lắng thừa. Chưa kể free plan giới hạn 100 thiết bị — nghe nhiều nhưng khi scale lên với IoT hay container thì hết nhanh lắm.
Đó là lý do mình chuyển sang Headscale — implementation mã nguồn mở của Tailscale control plane, tự host trên VPS của mình.
Headscale là gì và nó hoạt động thế nào
Tailscale hoạt động theo mô hình split architecture với hai phần tách biệt hoàn toàn.
- Control plane: quản lý danh sách thiết bị, phân phối key, xác thực, ACL policy. Mặc định nằm trên
controlplane.tailscale.com. - Data plane: traffic thực sự đi trực tiếp giữa các peer qua WireGuard (peer-to-peer), control plane không chạm vào data.
Headscale thay thế phần control plane đó. Bạn tự host nó trên server của mình, còn Tailscale client trên các thiết bị dùng y như cũ — chỉ cần trỏ về Headscale server thay vì server của Tailscale.
Cụ thể hơn: traffic thực sự không chạm qua Headscale. Dữ liệu vẫn đi thẳng peer-to-peer qua WireGuard — Headscale chỉ làm broker: phân phối public key, duy trì danh sách node, xử lý auth. Nhẹ đến mức VPS 1 CPU 1GB RAM chạy thoải mái cho vài chục node, CPU thường xuyên dưới 5%.
Cài đặt Headscale trên VPS
Yêu cầu môi trường
- VPS chạy Ubuntu 22.04 hoặc Debian 12 (mình dùng Ubuntu 22.04)
- Domain trỏ về IP của VPS (ví dụ:
hs.example.com) - Port 443 và 80 mở trên firewall
- Nginx làm reverse proxy (khuyến nghị)
Bước 1: Tải và cài Headscale
# Tải binary mới nhất (kiểm tra version tại GitHub releases)
wget https://github.com/juanfont/headscale/releases/download/v0.23.0/headscale_0.23.0_linux_amd64.deb
# Cài đặt
sudo dpkg -i headscale_0.23.0_linux_amd64.deb
# Kiểm tra
headscale version
Bước 2: Cấu hình Headscale
File config nằm tại /etc/headscale/config.yaml. Mở ra và chỉnh các phần quan trọng:
server_url: https://hs.example.com
listen_addr: 0.0.0.0:8080
metrics_listen_addr: 127.0.0.1:9090
grpc_listen_addr: 0.0.0.0:50443
grpc_allow_insecure: false
private_key_path: /var/lib/headscale/private.key
noise:
private_key_path: /var/lib/headscale/noise_private.key
ip_prefixes:
- 100.64.0.0/10
derp:
server:
enabled: false # Dùng DERP server của Tailscale (hoặc tự host)
urls:
- https://controlplane.tailscale.com/derpmap/default
dns_config:
nameservers:
- 1.1.1.1
domains: []
magic_dns: true
base_domain: mesh.example.com
db_type: sqlite3
db_path: /var/lib/headscale/db.sqlite
log:
level: info
acme_url: https://acme-v02.api.letsencrypt.org/directory
acme_email: [email protected]
tls_letsencrypt_hostname: ""
tls_letsencrypt_cache_dir: /var/lib/headscale/cache
tls_letsencrypt_challenge_type: HTTP-01
Chú ý kỹ server_url — phải khớp chính xác với domain bạn dùng. Sai chỗ này là client không kết nối được, không có error message rõ ràng, debug mất cả buổi.
Bước 3: Cấu hình Nginx reverse proxy
sudo apt install nginx certbot python3-certbot-nginx -y
# Tạo SSL certificate
sudo certbot --nginx -d hs.example.com
Tạo config Nginx tại /etc/nginx/sites-available/headscale:
server {
listen 80;
server_name hs.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name hs.example.com;
ssl_certificate /etc/letsencrypt/live/hs.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/hs.example.com/privkey.pem;
location / {
proxy_pass http://localhost:8080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header 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_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
}
}
sudo ln -s /etc/nginx/sites-available/headscale /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx
Bước 4: Khởi động Headscale
sudo systemctl enable headscale
sudo systemctl start headscale
sudo systemctl status headscale
Thêm thiết bị vào mạng Headscale
Tạo user (namespace)
Headscale dùng khái niệm “user” để nhóm thiết bị:
# Tạo user mới
headscale users create myteam
# Liệt kê users
headscale users list
Đăng ký thiết bị Linux/macOS
Trên máy cần kết nối (cài Tailscale client bình thường):
# Trỏ về Headscale server của bạn thay vì server Tailscale
tailscale up --login-server https://hs.example.com
Lệnh này sẽ in ra một URL để xác thực. Trên server Headscale, approve bằng:
# Lấy danh sách node chờ approve
headscale nodes list --user myteam
# Approve node (thay NODE_KEY bằng key từ output ở trên)
headscale nodes register --user myteam --key NODE_KEY
Approve thủ công ổn cho vài máy, nhưng khi deploy hàng loạt thì dùng auth key cho tiện hơn nhiều:
# Tạo reusable auth key
headscale preauthkeys create --user myteam --reusable --expiration 24h
# Trên client dùng auth key
tailscale up --login-server https://hs.example.com --authkey tskey-auth-XXXX
Kiểm tra kết nối
# Trên server Headscale
headscale nodes list
# Output mẫu:
# ID | Hostname | Name | MachineKey | NodeKey | User | IP addresses | Ephemeral | Last seen
# 1 | web-server | web-server | ... | ... | myteam | 100.64.0.1 | false | 2026-04-25 10:30
# 2 | dev-laptop | dev-laptop | ... | ... | myteam | 100.64.0.2 | false | 2026-04-25 10:31
# Trên client kiểm tra kết nối
tailscale status
tailscale ping 100.64.0.1
Câu chuyện debug thực tế
Sau khoảng 3 tháng chạy production, mình gặp một vấn đề khó chịu: intermittent packet loss chỉ xảy ra vào giờ cao điểm — buổi sáng từ 9–11h và chiều 14–16h. Các connection vẫn “up” theo Tailscale status nhưng latency tăng vọt từ 5ms lên 200ms+, rồi drop packet.
Ban đầu mình nghĩ là vấn đề mạng ISP. Nhưng dùng tailscale ping --verbose mới thấy: traffic không đi direct (peer-to-peer) mà đang relay qua DERP server. Lý do là NAT traversal thất bại, hai peer không punch-through được với nhau.
Fix là thêm một DERP server tự host gần hơn về mặt địa lý, và cấu hình ACL để force direct connection khi có thể. Từ đó stable hẳn.
Bài học rút ra: Tailscale/Headscale trông đơn giản nhưng phía dưới là WireGuard + DERP + NAT traversal. Khi có sự cố cần biết layer nào đang fail, không thể chỉ nhìn vào tailscale status là xong.
Quản lý ACL Policy
Headscale hỗ trợ HuJSON policy (tương tự Tailscale ACL). Tạo file /etc/headscale/acl.yaml:
acls:
# Dev team truy cập tất cả
- action: accept
src: ["myteam:*"]
dst: ["myteam:*:*"]
# Production servers chỉ nhận kết nối từ specific nodes
- action: accept
src: ["myteam:dev-laptop"]
dst: ["myteam:prod-server:22,80,443"]
# Load policy
headscale policy set --path /etc/headscale/acl.yaml
Kết luận
Sau 6 tháng chạy Headscale trên production với ~30 node, nhận xét thẳng thắn: nó hoạt động tốt và đáng tin cậy. Nếu bạn đã quen với Tailscale và cần tự host — vì privacy, compliance, hay đơn giản không muốn bị giới hạn 100 thiết bị — Headscale là lựa chọn trưởng thành nhất hiện có.
Cái hay nhất của Headscale: toàn bộ Tailscale client trên các node không cần đổi gì — chỉ thêm --login-server là xong. Một vài tính năng của Tailscale cloud chưa được port sang, như Tailscale SSH cert hay một số ACL tag nâng cao. Nhưng với nhu cầu kết nối máy chủ, dev team, homelab — hoàn toàn không thiếu gì.
Chi phí vận hành thực tế: VPS $5/tháng chạy Headscale cho 30 node, so với Tailscale Personal Pro $6/user/tháng — với team nhỏ thì break-even sau 1–2 tháng, chưa kể lợi ích về data sovereignty mà không có con số nào định giá được.

