# What Happens When NAT Traversal Fails in Tailcat: DERP Relay Fallback Explained

> Discover what happens when NAT traversal fails in Tailcat. Learn how DERP relay fallback ensures your encrypted WireGuard tunnel stays connected and get insights into path-detection commands.

- Repository: [Tailscale/tailcat](https://github.com/tailscale/tailcat)
- Tags: deep-dive
- Published: 2026-09-08

---

**When NAT traversal fails in Tailcat, the connection automatically falls back to a DERP (Designated Encrypted Relay Point) relay server, maintaining the encrypted WireGuard tunnel while reporting the fallback status through path-detection commands.**

Tailcat, an open-source networking tool within the Tailscale ecosystem, relies on the **magicsock** layer to establish direct peer-to-peer UDP paths between clients and servers. When symmetric NATs or restrictive firewall configurations prevent successful hole-punching, Tailcat handles these failures transparently without interrupting application traffic.

## Automatic DERP Relay Fallback

### Magicsock Layer Handling

In [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) (lines 13-15), the core library implements the logic that detects when direct UDP path establishment fails. The **magicsock** stack automatically transitions the encrypted WireGuard tunnel to use a **DERP (Designated Encrypted Relay Point)** server as a permanent fallback. This transition is transparent to the application layer, ensuring TCP and UDP traffic continues to flow without manual intervention or configuration changes.

### Path-Type Reporting

Tailcat exposes connection topology through the `ping` command, which actively probes whether traffic traverses a **direct** path or routes via a **DERP** hop. When NAT traversal fails, the command output explicitly marks the connection as using DERP, providing immediate visibility into the network path quality.

## Detecting NAT Traversal Failures via CLI

### Checking Current Path Status

Run the `ping` command to verify if the connection is operating over a relay:

```bash

# Ping the server; the output ends with "DERP" if NAT traversal failed.

tailcat ping ts://tcom...   # replace with your tailcat address

# Example output:

#   ping 127.0.0.1:22: pong (DERP)

```

When the output includes `(DERP)` instead of a direct IP address, NAT traversal has failed and the connection is using the fallback relay.

### Retrying Until Direct Connection

For automation scripts requiring direct peer-to-peer connectivity, use the `--until-direct` flag with a specified timeout. In [`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go) (lines 885-900), the CLI implements logic that repeatedly pings the server until a direct path is established or the timeout expires.

```bash

# Keep pinging until a direct path is established or 15s elapse.

tailcat ping --until-direct --timeout=15s ts://tcom...

# If NAT traversal never succeeds:

#   log.Fatalf("no direct path to the server after 15s")

# The command exits with status 1.

```

If the timeout expires without establishing a direct path, the command exits with a non-zero status, allowing shell scripts to detect and handle the failure.

## Programmatic Fallback Detection

Go applications integrating Tailcat can check the connection type programmatically. The connection object exposes state indicating whether it is currently routing through a DERP relay:

```go
// In Go code, after establishing a Tailcat client:
if conn.UsingDERP() {
    fmt.Println("Operating over DERP relay – NAT traversal failed.")
} else {
    fmt.Println("Direct peer-to-peer path established.")
}

```

This enables applications to log connectivity issues or adjust behavior based on path quality.

## Key Source Files Implementing NAT Handling

The following files in the `tailscale/tailcat` repository implement the NAT traversal failure logic:

- **[`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go)** (lines 13-15): Contains the core library logic for falling back to DERP relays when direct paths cannot be established.

- **[`cmd/tailcat/tailcat.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/tailcat.go)** (lines 885-900): Implements the CLI `ping` command, the `--until-direct` flag, and timeout handling that exits with an error when direct connections fail.

- **[`disco.go`](https://github.com/tailscale/tailcat/blob/main/disco.go)** (line ~2064): Handles discovery packets that actively trigger attempts to establish direct paths, containing the comment "actively triggers direct path discovery" near this section.

- **[`wire.go`](https://github.com/tailscale/tailcat/blob/main/wire.go)**: Defines the encrypted WireGuard protocol used for both direct and DERP-routed tunnels, ensuring cryptographic continuity regardless of path type.

- **[`README.md`](https://github.com/tailscale/tailcat/blob/main/README.md)**: Documents the high-level architecture, explicitly noting that DERP serves as the fallback mechanism when NAT traversal fails.

## Summary

- **Automatic fallback:** When direct UDP hole-punching fails, Tailcat transparently switches to DERP relays without dropping the WireGuard tunnel.
- **CLI visibility:** The `ping` command reports `(DERP)` in its output to indicate relay usage, while `--until-direct` enables scripts to wait for or fail on direct path establishment.
- **Programmatic access:** Go code can call connection methods to detect DERP usage and adjust application logic accordingly.
- **Continuous operation:** Even with failed NAT traversal, encrypted connectivity persists through the relay, though with potentially higher latency.

## Frequently Asked Questions

### Does Tailcat stop working if NAT traversal fails?

No. Tailcat remains fully functional when NAT traversal fails because it automatically falls back to a DERP relay server. The encrypted WireGuard tunnel continues to carry TCP and UDP traffic, though latency may increase due to the relay hop.

### How can I tell if my Tailcat connection is using a DERP relay?

Run the `tailcat ping` command and check the output suffix. If the response ends with `(DERP)`, the traffic is routing through a relay. Direct connections display the actual IP address and port of the peer instead.

### What causes NAT traversal to fail in Tailcat?

NAT traversal typically fails when both peers operate behind **symmetric NATs** or when firewalls strictly block UDP hole-punching. These network configurations prevent the magicsock layer from establishing a direct peer-to-peer path, triggering the DERP fallback.

### Can I force Tailcat to use only direct connections?

Yes. Use the `--until-direct` flag with the `ping` command and specify a timeout. If a direct path cannot be established within the timeout period, the command exits with a non-zero status. However, there is no option to prevent DERP fallback during normal operation without failing the connection entirely.