ROUTE AN INTERNAL DOMAIN WITH TAILSCALE SPLIT DNS
This guide shows you how to send DNS queries for an internal domain, here
corp.example.com, from tailnet devices to your own resolvers. The
domain-to-resolver mapping is kept in a Swamp behavior.
Prerequisites
TAILSCALE_API_KEYset to an API access token for the tailnet you are changing- The addresses of the resolvers that answer for the domain
Pull the extension
swamp extension pull @swamp/tailscaleThe output looks like:
INF extension·pull: Pulling "@swamp/tailscale"@"2026.10.13.1"
INF extension·pull: Identity verified: "@swamp/tailscale"@"2026.10.13.1"
INF extension·pull: Pulled "@swamp/tailscale"@"2026.10.13.1"
INF extension·pull: Extracted 54 files:Define the split DNS mapping
Create a behavior of type @swamp/tailscale/dns-split-nameservers named
corp-split-dns (see
Create and Run a Behavior)
with these global arguments:
globalArguments:
domain: corp.example.com
nameservers:
- 10.0.0.53
- 10.0.1.53domain is required, and each behavior of this type covers one domain. To route
more than one domain, create one behavior per domain.
To read the API token from a vault instead of the environment, add
apiKey: "${{ vault.get('default', 'tailscale-api-key') }}" to the global
arguments.
Apply the mapping
swamp behavior run corp-split-dns createThis command sets the nameservers for corp.example.com on the tailnet and
writes the result to the behavior's state.
Refresh from Tailscale
swamp behavior run corp-split-dns syncThis command reads the split DNS nameservers that the tailnet now serves for the domain and writes them to the behavior's state.
Change or remove the mapping
To change the resolvers, edit nameservers in the definition and run the
update method. To stop routing the domain, run delete, which clears the
domain's nameservers on the tailnet.
To set global resolvers, search paths and split DNS together in one behavior,
use @swamp/tailscale/dns-configuration instead. See the
Tailscale extensions reference
for its arguments.