How to Run Your Own Custom DERP Relays with Tailcat

Yes, Tailcat fully supports running your own custom DERP relays by either fetching a custom DERP map via the --derpmap-url flag or embedding the complete DERP region directly in the connection token using the --full-address flag.

Tailcat connects clients and servers through a DERP (Datacenter Endpoint Relay Protocol) relay only for the initial bootstrap; after that the Tailscale magicsock layer establishes direct peer-to-peer UDP paths. By default, Tailcat fetches the public DERP map from https://tailcat.dev/derpmap.json, but the software provides flexible configuration options when you need to run your own custom DERP relays with Tailcat instead of relying on public infrastructure.

How DERP Works in Tailcat

According to the source code in main/tailcat.go, Tailcat initializes with a DefaultDERPMapURL constant that points to the hosted map at tailcat.dev. During the connection handshake, the client retrieves this map to locate available relay regions. The DERPMapURL option in the library allows runtime overrides of this endpoint, enabling complete control over the relay infrastructure.

Method 1: Provide a Custom DERP Map URL

The most common approach to using private relays is hosting your own JSON-encoded tailcfg.DERPMap file and pointing Tailcat to it. This method requires clients to fetch the map at startup but allows centralized management of relay configurations.

In cmd/tailcat/tailcat.go, the CLI defines the --derpmap-url flag (lines 59-60) which sets the DERPMapURL option in the underlying library. When specified, Tailcat fetches your custom map instead of the default.

Creating the DERP Map JSON

The JSON file must follow the same schema as Tailscale's public derpmap.json, specifying regions, nodes, IP addresses, and STUN ports:

{
  "regions": {
    "999": {
      "region_id": 999,
      "name": "my-custom-derp",
      "nodes": [
        {
          "name": "derp-1",
          "region_id": 999,
          "ipv4": "203.0.113.10",
          "ipv6": "2001:db8::10",
          "port": 443,
          "stun_port": 3478
        }
      ]
    }
  }
}

Configuring Tailcat to Use the Custom Map

Host the JSON file on any accessible HTTP(S) server, then specify the URL when starting Tailcat:


# Server mode

tailcat --derpmap-url=http://localhost:8000/derpmap.json

# Client mode

tailcat --derpmap-url=http://localhost:8000/derpmap.json <addrblob>

Method 2: Embed the Full DERP Region in the Connection Token

For scenarios where clients cannot fetch external maps, Tailcat supports embedding the complete DERP region directly into the connection token. When the server starts with the --full-address flag, the generated ConnBlob contains the full DERP region serialized inline rather than just a region ID.

As implemented in main/tailcat.go (lines 24-30 and 62-68), this approach serializes the region data using the wire format defined in main/wire.go. Clients receiving this token can establish DERP connections without any external map fetches, making this ideal for air-gapped or offline environments.


# Generate a full-address token

tailcat --full-address

# Output: a longer address blob containing the complete DERP region

# Client connects using the embedded region (no --derpmap-url required)

tailcat <full-addr-blob>

Setting Up a Private DERP Server

To complete your custom relay deployment, run the official derper binary from the Tailscale repository. This server handles the DERP protocol and STUN for NAT traversal.


# Install the DERP server

go install tailscale.com/cmd/derper@latest

# Start with self-signed certificates for testing

derper -listen=:443 -certfile=cert.pem -keyfile=key.pem

# Host your map file (in another terminal)

python3 -m http.server 8000

Once running, update your derpmap.json to point to your server's public IP and port, then distribute the map URL to clients or use --full-address to bake the configuration directly into connection tokens.

Summary

  • Tailcat uses DERP only for bootstrap before attempting direct UDP connections via magicsock.
  • Two configuration methods exist: --derpmap-url for external map fetching, and --full-address for inline region embedding.
  • Source files controlling this behavior include main/tailcat.go (defaults and options), cmd/tailcat/tailcat.go (CLI flags), and main/wire.go (serialization).
  • Custom DERP servers can be built using Tailscale's derper binary with standard JSON map definitions.

Frequently Asked Questions

Do I need to use Tailscale's public DERP servers with Tailcat?

No. While Tailcat defaults to fetching https://tailcat.dev/derpmap.json, you are not required to use these public relays. The --derpmap-url flag allows you to specify any compliant DERP map endpoint, and the --full-address flag eliminates external map dependencies entirely.

What is the format of the DERP map JSON file?

The file must be a valid tailcfg.DERPMap JSON object containing a regions dictionary where each region specifies a unique region_id, name, and an array of nodes with ipv4, ipv6, port, and stun_port fields. This matches the format used by Tailscale's public infrastructure.

Can clients connect without fetching an external DERP map?

Yes. When the server is started with --full-address, the connection token (ConnBlob) serialized in main/wire.go includes the complete DERP region metadata. Clients using this token can connect directly to your custom relay without making any HTTP requests to map URLs.

Does using a custom DERP relay affect WireGuard encryption?

No. The DERP relay only handles encrypted packets and connection coordination. All WireGuard encryption, key exchange, and NAT traversal logic remain unchanged and continue to provide end-to-end encryption regardless of which DERP server handles the initial bootstrap.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →