Tailscale serve hay funnel: nên dùng cái nào?
Tailscale serve chỉ mở HTTPS trong tailnet, còn funnel đưa port ra Internet. Xem lỗi policy chặn funnel và cách chọn đúng command cho VPS.
tailscale serve và funnel: ai có thể truy cập URL
Điểm khác nhau giữa tailscale serve và tailscale funnel là đối tượng truy cập, ngoài ra không có gì khác. serve đặt một frontend HTTPS (hypertext transfer protocol secure) trước một port local và chỉ publish port đó trong tailnet của bạn. funnel publish chính port local đó ra toàn bộ public Internet thông qua các relay server do Tailscale vận hành. Hai command nhận cùng các flag và cùng target. Chỉ một từ quyết định dashboard là private hay có thể truy cập từ toàn thế giới.
Cả hai đều cấp cho bạn một certificate mà các browser đã trust, trên một hostname kết thúc bằng ts.net. Cả hai cũng không cần mở inbound port trên firewall của VPS. Daemon tailscaled của bạn đã duy trì một connection đi ra tailnet, nên traffic sẽ đi qua connection đó. Đưa một server vào tailnet là một việc riêng; chạy VPS làm Tailscale exit node hoặc quảng bá subnet router cho một private network đã bao quát phần đó. Publish một service đã nằm trong tailnet là nội dung của phần này.
Điều kiện cần có trước khi chạy một trong hai lệnh
- Tailscale 1.38.3 trở lên trên VPS và đã đăng nhập vào tailnet. Kiểm tra bằng
tailscale versionvàtailscale status. - Đã bật MagicDNS. MagicDNS là DNS (domain name system) tích hợp sẵn của Tailscale. Tính năng này cấp cho máy một tên như
blog-vps.your-tailnet.ts.netthay vì chỉ có địa chỉ100.x. - Đã bật chứng chỉ HTTPS cho tailnet trên trang DNS của admin console. Nếu chưa bật, bạn sẽ không có chứng chỉ để đặt trước port của mình.
- Chỉ với
funnel, cần có node attributefunneltrong file policy của tailnet. Đây là chỗ khiến phần lớn lần thử đầu tiên dừng lại. Nội dung này được trình bày bên dưới.
Mọi lệnh trong phần này đều bắt đầu bằng sudo, vì CLI giao tiếp với tailscaled thông qua một socket mà chỉ root mới có quyền ghi. Cấp cho một user quyền bỏ qua yêu cầu đó:
sudo tailscale set --operator=$USERPublish lên tailnet bằng tailscale serve
Trỏ serve đến một port local và tailscale serve sẽ xử lý phần còn lại.
sudo tailscale serve 3000Available within your tailnet:
https://amelie-workstation.pango-lin.ts.net
|-- / proxy http://127.0.0.1:3000
Press Ctrl+C to exit.3000 dạng rút gọn tương đương với http://127.0.0.1:3000. Tailscale lắng nghe trên port 443 của địa chỉ tailnet trên máy, thực hiện TLS termination bằng certificate ts.net, rồi chuyển tiếp HTTP thuần đến port local. Ứng dụng của bạn không cần biết certificate tồn tại. Đây là lý do chính để dùng cách này trước một admin panel mà nếu không sẽ phải để chạy bằng HTTP thuần. Web UI chỉ bind vào 127.0.0.1 là ứng viên rõ ràng. Web UI của dsh trên port 3080 là một ví dụ phù hợp: thay vì mỗi lần cần dùng lại phải mở SSH tunnel, chỉ cần trỏ serve đến 3080 một lần rồi truy cập từ bất kỳ thiết bị nào trong tailnet.
Bây giờ hãy đọc dòng cuối: Press Ctrl+C to exit. Lệnh chạy ở foreground và mapping nằm bên trong process đó. Đóng terminal thì URL sẽ ngừng hoạt động vì không có gì được ghi xuống disk. Thêm --bg để lưu mapping vào serve config của node. Cấu hình này vẫn tồn tại sau khi đóng terminal và sau reboot.
sudo tailscale serve --bg 3000Serve không chỉ nhận một port number. --set-path mount một service dưới một subpath, để nhiều app có thể dùng chung một hostname:
sudo tailscale serve --bg --set-path=/grafana 3000
sudo tailscale serve --bg --set-path=/metrics 9090Target cũng có thể là một thư mục chứa static file hoặc một backend đã hỗ trợ TLS bằng certificate mà bạn không muốn kiểm tra:
sudo tailscale serve --bg /srv/reports
sudo tailscale serve --bg https+insecure://localhost:8443Tính năng này không chỉ dành cho HTTP. --tcp=<port> chuyển tiếp một raw TCP (transmission control protocol) stream, còn --tls-terminated-tcp=<port> thực hiện TLS termination tại node rồi chuyển tiếp dữ liệu plaintext. Nhờ đó, bạn có thể đặt một certificate đáng tin cậy trước một service hoàn toàn không hỗ trợ HTTP:
sudo tailscale serve --bg --tls-terminated-tcp=443 tcp://127.0.0.1:9899Vì sao Funnel báo node chưa được đặt attribute?
Funnel mặc định bị tắt cho toàn bộ tailnet. Lần chạy đầu tiên sẽ in thông báo này rồi dừng:
Funnel not available; "funnel" node attribute not set. See https://tailscale.com/kb/1223/tailscale-funnel/.Lệnh không sai. Tailnet policy chưa cấp quyền publish cho node này, nên client từ chối trước khi kết nối đến relay. Hãy chỉnh sửa file tailnet policy trong admin console, tại Access Controls, rồi thêm attribute:
"nodeAttrs": [
{
"target": ["autogroup:member"],
"attr": ["funnel"],
},
],autogroup:member cấp quyền này cho mọi member trong tailnet. Nếu chỉ một máy được phép publish, hãy gắn tag cho máy đó rồi dùng tag làm đối tượng, ví dụ tag:public. Lưu policy, sau đó chạy lại lệnh Funnel.
Nếu account của bạn là tailnet admin, các client gần đây có shortcut: CLI in một consent URL trên login.tailscale.com. Mở URL đó sẽ bật HTTPS certificates và tự thêm attribute cho bạn. Nếu bạn không phải admin, URL đó sẽ không có tác dụng. Người có quyền truy cập policy phải thực hiện thay đổi này.
Công khai dịch vụ trên Internet bằng Tailscale Funnel
Sau khi đặt attribute, command giống command bạn đã biết, chỉ khác verb.
sudo tailscale funnel --bg 3000Available on the internet:
https://amelie-workstation.pango-lin.ts.net
|-- / proxy http://127.0.0.1:3000
Press Ctrl+C to exit.Luôn đọc dòng đầu tiên đó. Available within your tailnet và Available on the internet là khác biệt duy nhất có thể thấy giữa một service private và một service public. Các command tạo ra chúng chỉ khác nhau một từ.
Tính đến tháng 8 năm 2026, Funnel chỉ listen trên port 443, 8443 hoặc 10000, không listen trên port nào khác. Mặc định là port 443; --https=8443 hoặc --https=10000 là các lựa chọn thay thế. Các port khác đều bị từ chối vì các relay của Funnel chỉ chấp nhận connection trên những port này. Vì vậy, URL của Funnel luôn là hostname thuần, hoặc hostname có thêm :8443 ở cuối.
Xem những gì đang được publish ngay lúc này như thế nào?
Đoán là cách để một dashboard bị public trong cả tháng. Hãy hỏi node thay vì đoán.
tailscale serve status
tailscale funnel status
tailscale serve status --jsonCả hai lệnh status đều đọc cùng một config, nên dùng lệnh nào cũng xem được toàn bộ trạng thái. Dùng dạng --json trong script hoặc một lần kiểm tra theo lịch, vì output thông thường được viết cho người đọc. Khi chưa cấu hình gì, bạn sẽ nhận được một dòng:
No serve configNếu thấy dòng này sau khi setup đã hoạt động, mapping đã được tạo ở foreground nhưng process hiện đã thoát. Hãy tạo lại bằng --bg.
Để xóa một mapping, lặp lại lệnh đã tạo mapping đó và thêm off ở cuối. Để xóa toàn bộ mapping serve và funnel trên node, dùng reset.
sudo tailscale funnel --https=443 3000 off
sudo tailscale serve resetChạy lại tailscale serve status sau một trong hai thao tác trên và đọc phần còn lại, thay vì giả định rằng lệnh đã thực hiện đúng điều bạn muốn.
Điều bạn nhận được và những gì phải đánh đổi
Các lợi ích là có thật và đó là lý do người dùng chọn cách này thay vì reverse proxy.
- Chứng chỉ được các trình duyệt tin cậy và được tự động gia hạn. Bạn không cần cài ACME (automatic certificate management environment) client, cũng không phải nhớ cấu hình job gia hạn.
- Không cần mở cổng inbound trên firewall của VPS.
tailscaledchủ động kết nối ra ngoài, nên firewall ufw mặc định từ chối mọi kết nối trên VPS có thể giữ nguyên mức hạn chế như trước. - Không cần mua, trỏ hoặc chờ DNS record.
- Không cần port forwarding. Đây là toàn bộ vấn đề khi máy nằm sau NAT (network address translation), thay vì là VPS có public IP.
Các chi phí cũng có thật và funnel phải chịu toàn bộ những hạn chế này.
- Tên miền không thuộc về bạn. Người truy cập bên ngoài sẽ thấy
host.your-tailnet.ts.net. Funnel không hỗ trợ custom domain, nên bạn không thể đặtapp.example.comphía trước. - Đường dẫn không thuộc về bạn. Traffic trước tiên đi đến một Tailscale relay, rồi relay proxy stream đến node của bạn qua tailnet. Tailscale cho biết traffic của funnel chịu các giới hạn băng thông không được công bố và không thể cấu hình, vì vậy hãy tự đo throughput trước khi phụ thuộc vào một con số cụ thể.
- Bạn không có các cơ chế kiểm soát. Reverse proxy do bạn vận hành cung cấp access log, rate limit, giới hạn kích thước request và một nơi để triển khai authentication. Funnel chỉ cung cấp cho bạn một URL. Mọi thứ khác phải nằm bên trong ứng dụng.
- Danh sách port là cố định như đã nêu ở trên.
Cả hai tính năng đều phụ thuộc một phần vào hạ tầng do Tailscale vận hành: việc cấp certificate cho tên ts.net và chính các relay funnel. Việc dependency này có đáng lo hay không phụ thuộc vào những gì các server đó có thể và không thể làm với traffic của bạn. Đây chính là nội dung được trình bày trong mô hình trust của Tailscale. Cả hai tính năng đều không mất phí sử dụng, vì gói miễn phí hỗ trợ tối đa 6 user, mỗi user có số lượng device không giới hạn, và chính số lượng seat, không phải danh sách tính năng, mới là yếu tố cuối cùng khiến một tailnet phải chuyển sang gói trả phí. Sau ngưỡng đó, hóa đơn được tính theo user thay vì machine. Vì vậy, bạn nên kiểm tra tailnet thực sự tốn bao nhiêu trong gói trả phí trước khi serve hoặc funnel trở thành thành phần không thể thiếu. Nếu bạn đang cân nhắc một control server Headscale tự host, đừng giả định rằng bạn có thể mang cả hai tính năng này sang đó. Hãy kiểm tra release notes của phiên bản Headscale bạn định chạy.
Nên dùng cách nào?
Quy tắc này rất ngắn.
Dùng serve cho mọi thứ mang tính nội bộ: giao diện quản trị, dashboard, giao diện metrics mà bạn không muốn công cụ tìm kiếm index, hoặc bản staging của một website. Việc thiết bị thuộc tailnet chính là cơ chế kiểm soát truy cập, và đây là một cơ chế tốt. Thiết bị không thuộc tailnet thậm chí không thể phân giải tên.
Dùng funnel cho link demo, webhook receiver mà bên thứ ba phải gửi POST đến, hoặc OAuth callback trong quá trình phát triển. Đây là cách nhanh nhất để có một URL HTTPS public, và một lệnh off có thể dừng nó. Tuy nhiên, public nghĩa là public: hostname không phải bí mật, và funnel đặt trước một app không có cơ chế đăng nhập sẽ trở thành một service mở. Bất cứ thứ gì nằm phía sau nó cũng phải tự xác thực request, với mức cẩn trọng tương tự như một Ollama API endpoint bị expose.
Dùng reverse proxy thực thụ cho mọi thứ mà bạn xem là production. Bạn kiểm soát domain, certificate, log và rate limit của mình, đồng thời không có bên nào khác nằm trên đường đi của request. So sánh nginx, Caddy và Traefik khi dùng làm reverse proxy trình bày cách chọn một công cụ.
Các trường hợp lỗi và chuỗi bạn sẽ thấy
Funnel không khởi động. Funnel not available; "funnel" node attribute not set. là vấn đề về policy, không phải vấn đề về command. Thêm attribute funnel vào file policy của tailnet, lưu lại rồi thử lại.
Đã chạy được, nhưng giờ tailscale serve status báo No serve config. Mapping được tạo ở foreground và process đó đã kết thúc. Chạy lại command tương tự với --bg.
Tên phân giải được nhưng không có phản hồi. Serve proxy đến target bạn chỉ định. Nếu không có process nào listening tại đó thì không có gì để proxy. Chạy ss -ltnp | grep 3000 trên cùng máy đang chạy tailscaled để xác nhận. Nguyên nhân thường gặp là container publish port trên địa chỉ Docker bridge thay vì 127.0.0.1. Khi đó host không thấy listener tại vị trí bạn dự kiến. Cách networking của Docker Compose hoạt động giải thích port được publish thực sự nằm ở đâu.
Lỗi certificate trên tên ts.net. Nhiều khả năng HTTPS certificate chưa được bật cho tailnet. Bật chúng trong admin console, sau đó chạy riêng bước tạo certificate để lỗi không bị trộn với output của serve:
sudo tailscale cert your-host.your-tailnet.ts.netFunnel tải được khi dùng mobile data nhưng hoạt động khác với laptop. Laptop của bạn đang ở trên tailnet, nên MagicDNS phân giải tên thành địa chỉ 100.x và bạn truy cập service trực tiếp mà không đi qua relay. Đây là hành vi đúng. Điều đó cũng có nghĩa laptop không thể kiểm tra khả năng truy cập public. Dùng curl trên một máy không ở trên tailnet.
FAQ
Điểm khác nhau giữa tailscale serve và tailscale funnel là gì?
Ai có thể truy cập kết quả. tailscale serve publish một port cục bộ tại URL HTTPS mà chỉ các thiết bị trong tailnet của bạn mới truy cập được. tailscale funnel publish cùng port đó tại một URL mà bất kỳ ai trên Internet cũng truy cập được, với traffic được định tuyến qua các relay server do Tailscale vận hành. Các flag và target được dùng chung giữa hai lệnh. Dòng output đầu tiên cho biết bạn đã dùng loại nào: Available within your tailnet hoặc Available on the internet.
Vì sao tailscale funnel báo node attribute chưa được thiết lập?
Vì funnel bị vô hiệu hóa cho một tailnet cho đến khi có người bật tính năng này. Thông báo là Funnel not available; "funnel" node attribute not set. và xuất phát từ chính client của bạn, trước khi client liên hệ với bất kỳ relay nào. Thêm một entry nodeAttrs cấp attribute funnel cho autogroup:member, hoặc cấp cho một tag nếu chỉ một máy được phép publish, vào file policy của tailnet trong phần Access Controls. Tailnet admin cũng có thể mở consent URL mà CLI in ra.
Tailscale Funnel có thể dùng những port nào?
Chỉ dùng được 443, 8443 và 10000. Mặc định là 443; bạn chọn port khác bằng --https=8443 hoặc --https=10000. Đây là giới hạn của funnel relay, không phải của server, nên không thể gỡ giới hạn này bằng cách thay đổi firewall hoặc cấu hình trên VPS. tailscale serve không có giới hạn này vì traffic không bao giờ rời khỏi tailnet của bạn.
URL của serve hoặc funnel có tồn tại sau khi reboot không?
Chỉ khi bạn dùng --bg. Nếu không có flag này, command chạy ở foreground, in ra Press Ctrl+C to exit., và mapping biến mất cùng process. Với --bg, mapping được ghi vào serve config của node và xuất hiện lại cùng tailscaled sau khi reboot. Kiểm tra bằng tailscale serve status; khi chưa thiết lập gì, command này in ra No serve config.
Để funnel chạy có an toàn không?
An toàn xét về transport: connection dùng HTTPS và không mở port nào trên firewall của bạn. Nhưng theo nghĩa mà người dùng thường muốn hỏi thì không an toàn, vì URL là public, nên application phía sau cũng public. Chỉ để funnel chạy trước một application tự xác thực các request của nó, và tắt funnel khi demo hoặc webhook test kết thúc bằng command đã tạo funnel, thêm off ở cuối.
Nguồn về cách các command trên hoạt động: tài liệu Tailscale Serve và Funnel cùng tài liệu tham khảo CLI tại tailscale.com/docs.