2025 August 16

Setting up Tailscale with Magic DNS on an OpenWrt Router

Recently I picked up a compact travel router that had an easy way of flashing OpenWrt. I had fun tinkering with it and I wanted to add it to my Headscale network (self-hosted Tailscale).

The Router

Following the initial setup in OpenWrt's Tailscale guide was straightforward. With forwarding set up I could connect to my other Tailscale machines by IP address, even without Tailscale installed on the router's clients, but conneting using just the machine name with Magic DNS doesn't work.

OpenWrt's Tailscale Guide

Either ways, you should follow the Initial Setup section of the guide there.

If you log in through SSH and have a look at /etc/resolv.conf you'll find Tailscale overwrites it with something like so:

# resolv.conf(5) file generated by tailscale
# For more info, see https://tailscale.com/s/resolvconf-overwrite
# DO NOT EDIT THIS FILE BY HAND -- CHANGES WILL BE OVERWRITTEN

nameserver 100.100.100.100
search xxxxxx.ts.net

The search xxxxxx.ts.net line sets the search domain, which behind the scenes adds it as a suffix when your query doesn't have a top level domain.

So when you try and connect to your machine via something like machine, it actually looks up machine.xxxxxx.ts.net on your Tailscale service's DNS nameserver at 100.100.100.100 and you get back the machine's Tailscale IP address.

While neat, I don't really like how Tailscale configures this by overwriting the /etc/resolv.conf file, so I tend to disable this with

tailscale set --accept-dns=false

Thankfully this still keeps the nameserver running, allowing us to replicate the rest manually or through OpenWrt's built-in tools.

To do this, we push the search domain to the router's clients by going to Network > Interfaces > lan > Edit > DHCP Server > Advanced Settings and adding an entry for DHCP-Options with option:domain-name,lan xxxxxx.ts.net.

Placing lan first lets us prioritise machine.lan over machine.xxxxxx.ts.net for speed, but if you were relying on Tailscale to bypass the target machine's local firewall, you can place it second or remove it.

Setting DHCP-Options

Next up, under Network > DHCP and DNS > Forwards we add an entry for DNS Forwards /*.xxxxxx.ts.net/100.100.100.100. Tailscale's DNS returns anything outside its purview with "not found" so we need to restrict it to just your Tailscale domain.

This is also where you can add other upstream DNS servers like Cloudflare's 1.1.1.1.

Setting DNS Forwards

If you'd rather do all this through command line you could use something like:

uci add_list dhcp.lan.dhcp_option='option:domain-name,lan xxxxxx.ts.net'
uci add_list dhcp.@dnsmasq[0].server='/*.xxxxxx.ts.net/100.100.100.100'

As a final touch my travel router has a switch on the side that OpenWrt can listen to and run commands. I wanted this switch to toggle on/off routing all traffic through one of my Tailscale exit nodes, making it a VPN switch.

So I wrote a script you can save to /etc/rc.button/BTN_0, or adapt it to your router's buttons if its different. (Don't forget to add it to your list of files to preserve when upgrading, under System > Backup / Flash Firmware > Configurations).

#!/bin/sh

[ "${BUTTON}" = "BTN_0" ] || exit 0

EXIT_NODE="nodename"

remove_wan_forwarding() {
    uci show firewall |\
    awk -F. '{print $2}' |\
    awk -F= '{print $1}' |\
    grep @forwarding |\
    sort -u |\
    while read i; do
        src=`uci get "firewall.$i.src"`
        dest=`uci get "firewall.$i.dest"`
        [[ "$src" = "lan" ]] && [[ "$dest" = "wan" ]] || continue
        uci delete "firewall.$i"
    done
}

if [ "${ACTION}" = "pressed" ]; then
    logger "tailscale tunnel on"
    tailscale set --exit-node="$EXIT_NODE" --exit-node-allow-lan-access=true
    remove_wan_forwarding
    uci commit firewall
    service firewall restart
elif [ "${ACTION}" = "released" ]; then 
    logger "tailscale tunnel off"
    tailscale set --exit-node= --exit-node-allow-lan-access=false
    remove_wan_forwarding
    uci add firewall forwarding
    uci set firewall.@forwarding[-1]=forwarding
    uci set firewall.@forwarding[-1].src=lan
    uci set firewall.@forwarding[-1].dest=wan
    uci commit firewall
    service firewall restart
fi

This whole thing took a couple of days of trial and error to figure out, but I hope it has been useful! For good measure I also added a script from OpenWrt's guide that simplifies reinstalling the Tailscale packages on upgrade.