Skip to main content
MagicDNS provides automatic DNS resolution for devices on your Headscale network, allowing you to access devices by name instead of IP address.

What is MagicDNS?

MagicDNS automatically creates DNS records for all devices in your network:
  • Access devices by hostname: ssh myserver instead of ssh 100.64.0.10
  • Full domain names: myserver.headscale.net
  • Works across all devices in your network
  • No manual DNS configuration needed

MagicDNS Configuration

MagicDNS is configured in config/config.yaml:
config/config.yaml
dns:
  magic_dns: true
  base_domain: headscale.net
  nameservers:
    global:
      - 1.1.1.1
      - 1.0.0.1

Configuration Options

OptionDescriptionExample
magic_dnsEnable/disable MagicDNStrue or false
base_domainDomain suffix for devicesheadscale.net or example.com
nameservers.globalUpstream DNS servers["1.1.1.1", "8.8.8.8"]

Change Base Domain

1

Edit configuration

config/config.yaml
dns:
  magic_dns: true
  base_domain: mynetwork.local  # Your custom domain
  nameservers:
    global:
      - 1.1.1.1
      - 1.0.0.1
2

Restart Headscale

docker compose restart headscale
3

Reconnect devices

On each device:
sudo tailscale down
sudo tailscale up --login-server http://localhost:8000

Using MagicDNS

Once enabled, access devices by hostname:
# Instead of
ssh [email protected]

# Use
ssh user@myserver

# Or full domain
ssh [email protected]

Custom DNS Records

Add custom DNS records for non-Tailscale devices:
config/config.yaml
dns:
  magic_dns: true
  base_domain: headscale.net
  
  nameservers:
    global:
      - 1.1.1.1
      - 1.0.0.1
  
  extra_records:
    - name: nas
      type: A
      value: 192.168.1.100
    
    - name: router
      type: A
      value: 192.168.1.1
    
    - name: printer
      type: A
      value: 192.168.1.50
Now you can access these devices:
ssh [email protected]
curl http://router.headscale.net
ping printer.headscale.net

Split DNS

Route specific domains through your Tailscale network:
config/config.yaml
dns:
  magic_dns: true
  base_domain: headscale.net
  
  nameservers:
    global:
      - 1.1.1.1
      - 1.0.0.1
    
    restricted_nameservers:
      "internal.company.com":
        - 192.168.1.53
      
      "home.local":
        - 192.168.1.1
      
      "lab.internal":
        - 10.0.1.53

Split DNS Use Cases

Internal Services

Route company domain queries to internal DNS:
"internal.company.com":
  - 192.168.1.53

Home Lab

Resolve home lab domains:
"lab.home":
  - 192.168.1.1

Multiple Locations

Different DNS servers per location:
"office.local":
  - 10.0.1.1
"home.local":
  - 192.168.1.1

Development

Route dev domains to dev DNS:
"dev.local":
  - 10.0.10.53

Custom Nameservers

Global Nameservers

Used for all DNS queries not handled by MagicDNS:
config/config.yaml
nameservers:
  global:
    - 1.1.1.1
    - 1.0.0.1

DNS Over HTTPS (DoH)

For enhanced privacy, use DNS over HTTPS:
config/config.yaml
dns:
  magic_dns: true
  base_domain: headscale.net
  nameservers:
    global:
      - https://cloudflare-dns.com/dns-query
      - https://dns.google/dns-query

Hostname Management

Set Custom Hostname

When connecting a device:
sudo tailscale up \
  --login-server http://localhost:8000 \
  --authkey YOUR_KEY \
  --hostname my-custom-name
The device will be accessible as:
  • my-custom-name
  • my-custom-name.headscale.net

Change Hostname

1

Disconnect from network

sudo tailscale down
2

Reconnect with new hostname

sudo tailscale up \
  --login-server http://localhost:8000 \
  --authkey YOUR_KEY \
  --hostname new-name
3

Verify new name

tailscale status

Hostname Conflicts

If multiple devices have the same hostname, use full domain:
# Might be ambiguous
ssh myserver

# Explicit with user prefix
ssh alice.myserver.headscale.net
ssh bob.myserver.headscale.net

Testing DNS Resolution

Verify MagicDNS is Working

nslookup myserver.headscale.net

Check DNS Configuration

# View Tailscale DNS settings
tailscale status --json | jq .MagicDNSSuffix

# Check resolv.conf (Linux)
cat /etc/resolv.conf

# Verify DNS servers
tailscale status --json | jq .DNS

DNS in Docker Containers

When running Tailscale in Docker, DNS may need special configuration:
docker-compose.yml
services:
  myapp:
    image: myapp:latest
    network_mode: "service:tailscale"
    depends_on:
      - tailscale

  tailscale:
    image: tailscale/tailscale:latest
    hostname: myapp-container
    environment:
      - TS_AUTHKEY=${TS_AUTHKEY}
      - TS_LOGIN_SERVER=http://host.docker.internal:8000
      - TS_STATE_DIR=/var/lib/tailscale
      - TS_ACCEPT_DNS=true  # Enable MagicDNS
    volumes:
      - tailscale-data:/var/lib/tailscale
      - /dev/net/tun:/dev/net/tun
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    restart: unless-stopped

volumes:
  tailscale-data:

Advanced DNS Configuration

DNS Search Domains

Add search domains for shorter names:
config/config.yaml
dns:
  magic_dns: true
  base_domain: headscale.net
  
  search_domains:
    - headscale.net
    - local
    - home
Now you can use:
# Instead of
ssh myserver.headscale.net

# Just use
ssh myserver

Per-User DNS Settings

Use ACL policies to control DNS access:
config/policy.json
{
  "acls": [
    {
      "action": "accept",
      "src": ["tag:personal"],
      "dst": ["192.168.1.53:53"],
      "comment": "Personal devices can query home DNS"
    },
    {
      "action": "accept",
      "src": ["tag:guests"],
      "dst": ["*:*"],
      "comment": "Guests cannot access DNS server"
    }
  ]
}

Troubleshooting

MagicDNS Not Working

1

Verify MagicDNS is enabled

config/config.yaml
dns:
  magic_dns: true  # Must be true
  base_domain: headscale.net
2

Check Tailscale DNS settings

tailscale status --json | jq .MagicDNSSuffix
# Should return: "headscale.net"
3

Restart Headscale after config changes

docker compose restart headscale
4

Reconnect devices

sudo tailscale down
sudo tailscale up --login-server http://localhost:8000

DNS Resolution Slow

config/config.yaml
# Use faster DNS servers
nameservers:
  global:
    - 1.1.1.1        # Cloudflare (usually fastest)
    - 8.8.8.8        # Google

Cannot Resolve Hostnames

# Test if device is reachable by IP
ping 100.64.0.10

# If IP works but hostname doesn't, check DNS
nslookup myserver.headscale.net

# Verify MagicDNS is enabled on device
tailscale status
# Should show "MagicDNS: yes"

Split DNS Not Working

1

Verify restricted nameserver is reachable

# From Tailscale network
ping 192.168.1.53

# Test DNS query
dig @192.168.1.53 internal.company.com
2

Ensure subnet route is enabled

docker exec headscale headscale routes list
# Verify route to DNS server subnet is enabled
3

Check ACL allows DNS traffic

config/policy.json
{
  "acls": [
    {
      "action": "accept",
      "src": ["*"],
      "dst": ["192.168.1.53:53"]
    }
  ]
}

Best Practices

Use Descriptive Hostnames

--hostname web-server-prod
--hostname db-primary
--hostname laptop-alice
Makes it clear what each device is

Document DNS Records

Keep a list of custom DNS records:
extra_records:
  - name: nas  # Network storage
  - name: router  # Main gateway
  - name: printer  # Office printer

Use Reliable DNS Servers

Choose fast, reliable upstream DNS:
nameservers:
  global:
    - 1.1.1.1  # Primary
    - 8.8.8.8  # Backup

Test Before Production

Test MagicDNS thoroughly:
nslookup myserver.headscale.net
ping myserver
curl http://myserver

Next Steps

Build docs developers (and LLMs) love