Skip to content

alltuner/nameplate

Repository files navigation

Nameplate

A Docker-based CoreDNS server that automatically creates DNS records for all machines in your Tailscale network (tailnet) under a custom domain. Uses the coredns-tailscale plugin to read machine information directly from the Tailscale daemon socket, with no API tokens required.

For example, a machine named media-server in your tailnet becomes resolvable as media-server.internal.example.com from any device on the tailnet.

How it works

CoreDNS runs with the coredns-tailscale plugin compiled in. The plugin connects to the Tailscale daemon socket (mounted into the container) and queries the local API to discover all machines visible to the host. It then serves A and AAAA records for each machine under your configured domain.

This is designed as a split DNS setup: Tailscale routes only your custom domain's queries to this server, while all other DNS queries continue to use your normal DNS resolver.

CoreDNS plugins

The Corefile includes the following plugins:

Plugin Purpose
tailscale Queries the Tailscale local API via the daemon socket and serves A/AAAA records for each machine in the tailnet. Supports CNAME aliases via Tailscale ACL tags (see CNAME aliases).
cache Caches DNS responses for CACHE_TTL seconds (default 30). Reduces load on the Tailscale socket and speeds up repeated lookups.
log Logs all queries to stdout. Useful for debugging. Can be removed in production if you find it too noisy.
errors Logs errors to stdout.
health Exposes an HTTP health check at /health on HEALTH_PORT (default 8080). Used by the Docker healthcheck.
ready Exposes an HTTP readiness check at /ready on READY_PORT (default 8181). Reports 200 once all plugins are loaded.

Quick start

A pre-built multi-arch image (amd64/arm64) is published at ghcr.io/alltuner/nameplate.

  1. Create a docker-compose.yml with your domain:

    services:
      coredns:
        image: ghcr.io/alltuner/nameplate:latest
        container_name: nameplate
        ports:
          - "53:53/tcp"
          - "53:53/udp"
        volumes:
          - /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock:ro
        environment:
          DOMAIN: internal.example.com  # <-- replace with your domain
        restart: unless-stopped
        healthcheck:
          test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"]
          interval: 30s
          timeout: 5s
          retries: 3
  2. Start the server:

    docker compose up -d
  3. Verify it works:

    dig @localhost my-machine.internal.example.com
  4. Configure Tailscale split DNS (see below).

Building locally: Clone the repo and use build: . instead of the image line in your compose file. See Upgrading versions for version details.

Configuration

All settings are controlled via environment variables. Copy .env.example to .env and adjust:

Variable Default Description
DOMAIN internal.example.com The domain under which tailnet machines are served
DNS_PORT 53 Port CoreDNS listens on (TCP and UDP)
TAILSCALE_SOCKET_PATH /var/run/tailscale/tailscaled.sock Path to the Tailscale socket on the host
CACHE_TTL 30 DNS cache duration in seconds
HEALTH_PORT 8080 Port for the health check HTTP endpoint
READY_PORT 8181 Port for the readiness check HTTP endpoint
UPSTREAM_DNS (empty) Upstream DNS server for forwarding unmatched queries (e.g., 1.1.1.1 or 100.100.100.100 for Tailscale MagicDNS). Leave empty to disable forwarding.

Configuring Tailscale split DNS

Split DNS tells Tailscale to route DNS queries for a specific domain to a nameserver you control, while leaving all other queries untouched. This is how you make *.internal.example.com resolve on every device in your tailnet.

Step 1: Note the Tailscale IP of your CoreDNS host

On the machine running this container:

tailscale ip -4

Note this IP (e.g., 100.64.x.x). This is the address Tailscale will send DNS queries to.

Step 2: Configure split DNS in the Tailscale admin console

  1. Go to the Tailscale admin console.
  2. Scroll to Nameservers.
  3. Under Add nameserver, select Custom....
  4. Click Add Split DNS.
  5. Enter:
    • Domain: your configured DOMAIN (e.g., internal.example.com)
    • Nameserver: the Tailscale IP from Step 1
  6. Save.

Step 3: Verify from another device on the tailnet

From any machine on your tailnet:

dig my-machine.internal.example.com

You should see an A record pointing to the machine's Tailscale IP.

Troubleshooting split DNS

  • Queries not reaching CoreDNS: Ensure the CoreDNS host's Tailscale IP is correct and the container is running. Check docker compose logs.
  • SERVFAIL responses: The Tailscale socket might not be accessible. Verify the socket path in .env matches the actual location on your host. On Linux it's typically /var/run/tailscale/tailscaled.sock, on macOS it's /Library/Tailscale/tailscaled.sock.
  • Stale results: Increase CACHE_TTL if you want longer caching, or decrease it if machines are being added/removed frequently.
  • Port conflicts: Tailscale split DNS only routes queries to port 53, so the server must listen on port 53. If port 53 is already in use (common on Linux where systemd-resolved binds a stub listener to 127.0.0.53:53), disable the stub listener by setting DNSStubListener=no in /etc/systemd/resolved.conf and restarting the service with sudo systemctl restart systemd-resolved.

CNAME aliases

The coredns-tailscale plugin supports CNAME records via Tailscale ACL tags. Add a tag prefixed with cname- to any machine in your Tailscale ACL policy:

{
  "tagOwners": {
    "tag:cname-git": ["autogroup:admin"],
  },
}

Then apply the tag to a machine. This creates a CNAME record:

git.internal.example.com -> my-server.internal.example.com

This is useful for giving friendly names to services running on specific machines.

Credits

Nameplate is a thin packaging layer around two excellent projects:

  • CoreDNS (GitHub) - A flexible, plugin-based DNS server written in Go, graduated from the CNCF.
  • coredns-tailscale by @damomurf - The CoreDNS plugin that makes Tailnet machine discovery and CNAME aliasing possible.

Upgrading versions

The CoreDNS version is pinned in the Dockerfile and must match the upstream version that plugin.cfg is based on. To upgrade CoreDNS, update both files together:

  1. Generate a new plugin.cfg from the target CoreDNS release (adding the tailscale line).
  2. Update COREDNS_VERSION in the Dockerfile to match.

The COREDNS_TAILSCALE_VERSION build argument in the Dockerfile can be overridden independently:

docker compose build --build-arg COREDNS_TAILSCALE_VERSION=v0.3.22

Further reading

About

Docker-based CoreDNS server that automatically creates DNS records for all machines in your Tailscale network under a custom domain

Topics

Resources

Code of conduct

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors