Tailscale Features
MagicDNS, ACLs, subnet routers, and the full feature set
MagicDNS
Every device on a tailnet gets a stable DNS name automatically. No manual DNS records, no editing /etc/hosts, no running your own DNS server. This is one of Tailscale's most practically useful features for day-to-day operations.
DNS Name Format
Each device is reachable at: <hostname>.<tailnet-name>.ts.net
# Full qualified name
workstation.tail1234.ts.net
# Short name (within the same tailnet, MagicDNS resolves this)
workstation100.x.y.z Addressing
CGNAT Address Space (100.64.0.0/10)
Tailscale assigns each device an IP from the 100.64.0.0/10 range (RFC 6598 — Shared Address Space, originally allocated for CGNAT). This is a deliberate choice:
- Won't conflict with your LAN: Your office uses
10.0.0.0/8? Home uses192.168.0.0/16? Neither overlaps with100.64.0.0/10. - Won't conflict with cloud VPCs: AWS, GCP, Azure VPCs typically use RFC 1918 space. No overlap.
- Stable across networks: Your device keeps its 100.x.y.z address whether you're on WiFi, cellular, ethernet, or roaming between them.
Note
The one edge case: If your ISP uses CGNAT, you might see 100.64.x.x addresses on your WAN interface too. In practice this rarely causes issues because Tailscale's WireGuard interface has higher routing priority for Tailscale-specific IPs, but it's worth knowing if you're debugging routing.
Split DNS
Route Specific Domains to Specific Nameservers
Split DNS lets you configure the tailnet so that queries for specific domains are resolved by specific nameservers — routed through the tailnet.
// In Tailscale DNS settings:
{
"dns": {
"nameservers": ["100.100.100.100"], // MagicDNS
"routes": {
"corp.example.com": ["10.0.0.53"], // Corporate DNS for internal domains
"staging.internal": ["10.1.0.53"] // Staging environment DNS
}
}
}Use case: You're at a coffee shop, but api.corp.example.com still resolves correctly because the query is routed through your tailnet to the corporate DNS server.
ACLs (Access Control Lists)
Tailscale ACLs are policy-as-code, defined in HuJSON (JSON with comments and trailing commas). They control which devices and users can communicate with which other devices and services. ACLs are evaluated on each node — there's no central chokepoint.
Warning
Default deny: If you define any ACL rules, the default policy becomes deny-all. Only explicitly allowed traffic passes. This is the opposite of the default (without ACLs) where all nodes in a tailnet can reach all other nodes.
Building Blocks
| Concept | Purpose | Example |
|---|---|---|
| Groups | Named collections of users | "group:engineering": ["alice@", "bob@"] |
| Tags | Labels for devices (replace user ownership) | "tag:server", "tag:k8s-node" |
| Tag Owners | Who can apply a tag to a device | "tag:server": ["group:ops"] |
| Hosts | Named aliases for specific IPs/CIDRs | "prod-db": "100.64.0.50" |
| autoApprovers | Auto-approve routes/exit nodes by tag | "routes": {"10.0.0.0/8": ["tag:router"]} |
ACL Example
{
// Groups: named user collections
"groups": {
"group:engineering": ["alice@example.com", "bob@example.com"],
"group:ops": ["carol@example.com", "dave@example.com"],
},
// Tag owners: who can assign which tags
"tagOwners": {
"tag:server": ["group:ops"],
"tag:k8s-node": ["group:ops"],
"tag:monitoring": ["group:ops"],
},
// ACL rules: who can reach what
"acls": [
// Ops can reach everything
{
"action": "accept",
"src": ["group:ops"],
"dst": ["*:*"],
},
// Engineering can reach servers on specific ports
{
"action": "accept",
"src": ["group:engineering"],
"dst": ["tag:server:22,80,443"],
},
// Monitoring can reach all nodes on prometheus port
{
"action": "accept",
"src": ["tag:monitoring"],
"dst": ["*:9090,9100"],
},
// K8s nodes can talk to each other on all ports
{
"action": "accept",
"src": ["tag:k8s-node"],
"dst": ["tag:k8s-node:*"],
},
],
// Auto-approve subnet routes and exit nodes
"autoApprovers": {
"routes": {
"10.0.0.0/8": ["tag:server"],
"192.168.0.0/16": ["tag:server"],
},
"exitNode": ["tag:server"],
},
}Tip
Tags vs Users: Tags are particularly important for server infrastructure. A tagged device (tag:server) is no longer owned by a specific user — it's "owned" by the tag. This means ACLs don't break when an employee leaves. If Carol tagged the server and then leaves the company, the server's ACL access is unchanged because it's governed by the tag, not Carol's account.
Where ACLs Are Evaluated
Tailscale distributes ACL rules to every node. Each node enforces the policy locally using packet filtering in the WireGuard layer. This means:
- No central bottleneck: ACL enforcement is distributed, not a single firewall in the path.
- Offline-capable: If the coordination server is temporarily unreachable, the last-pushed ACLs remain in effect.
- Fast updates: ACL changes are pushed to all nodes via the control plane connection (long-polling/WebSocket). Typical propagation: seconds.
Subnet Routers
Not every device can run the Tailscale agent. Subnet routers let you bridge between Tailscale and non-Tailscale networks, making entire subnets reachable through the tailnet.
Setting Up a Subnet Router
- Advertise routes on the machine that will act as the subnet router:
console console $ sudo tailscale up --advertise-routes=192.168.1.0/24,10.0.0.0/24 - Enable IP forwarding on the subnet router (Linux):
console console $ echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf $ echo 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf $ sudo sysctl -p /etc/sysctl.d/99-tailscale.conf - Approve routes in the Tailscale admin console (or via
autoApproversin ACLs). Unapproved routes are ignored by other nodes. - Done: Other tailnet nodes can now reach
192.168.1.50as if they were on the same LAN. Traffic flows: Laptop → WireGuard → Subnet Router → LAN → Printer.
Note
Use case — accessing on-prem from anywhere: Put a subnet router in your office or datacenter. Now every tailnet member (working from home, a coffee shop, or traveling) can reach the entire office network. No traditional VPN concentrator, no port forwarding, no exposing services to the internet.
Tip
High availability: Run two subnet routers advertising the same routes. Tailscale will use the one with lower latency and failover to the other if the primary becomes unreachable. No special configuration needed — just advertise the same routes from both.
Exit Nodes
Subnet routers expose specific subnets. Exit nodes route all traffic (the default route, 0.0.0.0/0) through a specific node. This is the traditional "VPN" behavior — all your internet traffic goes through the exit node.
- Routes specific subnets (e.g.,
192.168.1.0/24) - Internet traffic goes out your local connection
- Use case: access internal networks
- Routes ALL traffic (
0.0.0.0/0) - Internet traffic exits via the exit node's connection
- Use case: secure untrusted networks, geo-location
Setting Up an Exit Node
Server Side (the exit node)
$ sudo tailscale up --advertise-exit-node
Then approve in the admin console or via autoApprovers.
Client Side (use the exit node)
$ sudo tailscale up --exit-node=myserver
# To stop using the exit node:
$ sudo tailscale up --exit-node=
Warning
Bandwidth consideration: All your traffic flows through the exit node, which means the exit node's bandwidth is your bandwidth limit. A Raspberry Pi on a residential connection is a poor exit node for heavy usage. A cloud VM with 1 Gbps+ is ideal.
Taildrop
Peer-to-peer file transfer between tailnet devices. No cloud storage intermediary, no size limits, no file type restrictions. Files transfer directly over the WireGuard connection.
$ tailscale file cp ./backup.tar.gz myserver:
Sending backup.tar.gz to myserver... 100% (2.4 GB)
# On the receiving end:
$ tailscale file get ~/received/
Received backup.tar.gz (2.4 GB) from workstation
Tip
Like AirDrop, but cross-platform and anywhere: Works between Linux, macOS, Windows, iOS, Android. Files go peer-to-peer (or through DERP if direct fails), encrypted end-to-end by WireGuard. No file size limits.
Tailscale Funnel
Funnel is the reverse of everything else Tailscale does. Instead of making private services reachable within the tailnet, Funnel exposes a local port to the public internet via Tailscale's infrastructure.
$ tailscale funnel 3000
Available on the internet:
https://mydevice.tail1234.ts.net/
|-- proxy http://127.0.0.1:3000
Funnel Details
- HTTPS only: Tailscale terminates TLS with a Let's Encrypt certificate for
*.ts.net - Port limitations: Only ports 443, 8443, and 10000 are available for Funnel
- Use cases: Webhook receivers, demo deployments, sharing local dev server, CI/CD callbacks
- Not a CDN: Traffic goes through Tailscale's infrastructure to your device. Latency and bandwidth depend on your connection.
- Exposes a local port to your tailnet
- Only reachable by tailnet members
- No public DNS/TLS needed
tailscale serve 3000
- Exposes a local port to the public internet
- Anyone with the URL can reach it
- TLS terminated by Tailscale (Let's Encrypt)
tailscale funnel 3000
Tailscale SSH
Replaces SSH key management entirely. Authentication is handled by Tailscale identity — no authorized_keys, no key rotation, no SSH CA infrastructure.
$ tailscale ssh user@myserver
Welcome to myserver (Ubuntu 24.04)
user@myserver:~$
How Tailscale SSH Works
- Server side: Tailscale runs an SSH server embedded in the Tailscale daemon. When enabled, it intercepts SSH connections on the Tailscale interface.
- Authentication: Instead of SSH keys, the server verifies the connecting peer's Tailscale identity (which user, which device, which tags). This is enforced via ACLs.
- Authorization: SSH ACLs in the Tailscale policy define who can SSH into what, with what user:
{
"ssh": [
{
"action": "accept",
"src": ["group:ops"],
"dst": ["tag:server"],
"users": ["root", "deploy"],
},
{
"action": "accept",
"src": ["group:engineering"],
"dst": ["tag:server"],
"users": ["deploy"], // No root for eng
},
],
}Tip
Session recording: Tailscale SSH can record SSH sessions (audit logs) and require re-authentication for sensitive commands. This gives you compliance-friendly audit trails without deploying a bastion host or SSH CA.
Key Expiry & Node Authorization
Key Expiry
- Each node's WireGuard key has an expiration (default: 180 days)
- Expired nodes must re-authenticate (OAuth login again)
- Configurable per node or by tag
- Can disable key expiry for servers (
--auth-keywith no expiry flag) - Prevents stale/compromised devices from maintaining access indefinitely
Node Authorization
- New devices need admin approval before joining the tailnet
- Pre-authorized keys:
tailscale up --auth-key=tskey-...for headless servers - Auto-approve by tag via
autoApproversin ACLs - Prevents unauthorized devices from joining even if they have valid credentials
$ # Generate a pre-auth key for automated server provisioning
$ tailscale up --auth-key=tskey-auth-kAbCdEf123-ABCdef123456789
# Reusable keys (for provisioning multiple servers):
# Created in admin console with "reusable" flag
# Can also be tagged: --auth-key auto-assigns tag:server
Device Posture & Compliance
Tailscale can restrict access based on device state — not just identity. This is useful for compliance-heavy environments (SOC 2, HIPAA, etc.).
| Posture Check | What It Verifies | Use Case |
|---|---|---|
| OS Version | Minimum OS version (e.g., macOS 14+) | Ensure devices have recent security patches |
| Tailscale Version | Minimum client version | Ensure latest security fixes are deployed |
| Disk Encryption | FileVault (macOS), BitLocker (Windows) | Compliance: data at rest must be encrypted |
| Screen Lock | Screen lock is enabled | Prevent physical access to unlocked devices |
| Identity Provider | Valid, active IDP session | Revoked IDP accounts lose tailnet access immediately |
Note
Integration with MDM: Device posture works best alongside mobile device management (MDM). Tailscale checks what the MDM reports about the device, and ACLs can reference posture attributes. This bridges the gap between network access control and endpoint management.
Feature Overview
| Feature | What It Does | Key Command / Config |
|---|---|---|
| MagicDNS | Automatic DNS for all tailnet devices | hostname.tailnet.ts.net |
| ACLs | Fine-grained access control (tags, groups, ports) | HuJSON policy file |
| Subnet Routers | Bridge tailnet to non-Tailscale networks | --advertise-routes=10.0.0.0/24 |
| Exit Nodes | Route all traffic through a node | --advertise-exit-node / --exit-node=host |
| Taildrop | P2P file transfer between tailnet devices | tailscale file cp ./f host: |
| Funnel | Expose local port to public internet | tailscale funnel 3000 |
| Serve | Expose local port within tailnet | tailscale serve 3000 |
| Tailscale SSH | Identity-based SSH (no keys needed) | tailscale ssh user@host |
| Key Expiry | Force periodic re-authentication | Configurable per node/tag (default 180 days) |
| Device Posture | Restrict access based on device state | Admin console posture policies |