# How to Use tailcat for TCP Port Forwarding: A Complete Guide with Examples

> Learn how to use tailcat for TCP port forwarding with this complete guide. Easily proxy connections through an encrypted WireGuard tunnel without a central control plane.

- Repository: [Tailscale/tailcat](https://github.com/tailscale/tailcat)
- Tags: how-to-guide
- Published: 2026-09-06

---

**Use the `tailcat forward` sub-command to create local TCP listeners that proxy connections through an encrypted WireGuard tunnel to a remote tailcat server without requiring a central control plane.**

`tailcat` implements a lightweight, control-plane-free network pipe built on top of Tailscale's WireGuard data-plane. The **`forward`** sub-command enables TCP port forwarding by spawning local listeners and routing every incoming connection through a tailcat server over an encrypted tunnel. This guide walks through the architecture, mapping syntax, and practical examples based on the tailscale/tailcat source code.

## Architecture of tailcat TCP Port Forwarding

Understanding how tailcat port forwarding works helps diagnose issues and choose the right mapping strategy.

### Core Components

The implementation spans several key functions in [`cmd/tailcat/forward.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/forward.go):

| Component | Responsibility | Source Location |
|-----------|---------------|---------------|
| **`forwardCommand`** | Registers the CLI sub-command and parses flags | [`forward.go:L24-L31`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/forward.go#L24-L31) |
| **`runForward`** | Validates arguments, creates client, spawns listeners | [`forward.go:L50-L82`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/forward.go#L50-L82) |
| **`parseForwardSpec`** | Parses mapping strings into structured specs | [`forward.go:L63-L88`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/forward.go#L63-L88) |
| **`forwardListener`** | Accepts local connections and proxies to remote | [`forward.go:L15-L47`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/forward.go#L15-L47) |

### Connection Flow

When you run `tailcat forward`, here's what happens:

1. **Argument parsing** — `parseForwardSpec` processes each port mapping (e.g., `8080`, `18080:8080`, `13306:192.168.1.10:3306`)

2. **Listener creation** — `net.Listen` binds to the local address

3. **Goroutine spawning** — `forwardListener` runs indefinitely for each mapping

4. **Connection handling** — On each accept, the listener dials the remote side via `tailcat.Client.DialTCP` or `DialTCPPort`, then hands both connections to `tailcat.ProxyConns` for bidirectional data copying

5. **Encryption** — All traffic travels through the same WireGuard-protected tunnel used by standard tailcat connections

This design provides **NAT traversal**, **end-to-end encryption**, and **zero dependency on a central control plane**.

## Port Mapping Syntax for tailcat Forward

The `parseForwardSpec` function in [`cmd/tailcat/forward.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/forward.go) supports flexible mapping specifications:

| Syntax | Behavior |
|--------|----------|
| `8080` | Listen on `127.0.0.1:8080`, forward to remote port `8080` |
| `18080:8080` | Listen locally on `18080`, forward to remote port `8080` |
| `0:8080` | Auto-assign local port, print chosen address on startup |
| `13306:192.168.1.10:3306` | Listen on `13306`, forward to specific IP:port (requires exit node) |
| `--bind=0.0.0.0` | Override bind address for all listeners |

### Remote Target Resolution

- **Port-only targets** (`8080`, `18080:8080`): Use `DialTCPPort` → connects to `127.0.0.1:<port>` on the server

- **Full address targets** (`13306:192.168.1.10:3306`): Use `DialTCP` → connects to the specified `netip.AddrPort`

The server must run as an **exit node** to reach arbitrary remote IPs.

## Practical tailcat TCP Port Forwarding Examples

### Basic SSH Forwarding

Expose a remote server's SSH port locally:

```bash
tailcat forward tc1abc… 22

```

This listens on `127.0.0.1:22` and forwards to port 22 on the tailcat server.

### Listen on All Interfaces

Access forwarded ports from other machines on your network:

```bash
tailcat forward --bind=0.0.0.0 tc1abc… 2222:22

```

Now `ssh -p 2222 user@<your-local-ip>` works from any reachable host.

### Dynamic Port Assignment for Scripts

Let the OS pick an available port—useful when hardcoding isn't possible:

```bash
tailcat forward tc1abc… 0:80

# Output: forwarding [::]:xxxxx -> remote 127.0.0.1:80

```

Parse the output to discover the assigned port programmatically.

### Exit Node Forwarding to Internal Services

Reach a database behind the tailcat server:

```bash
tailcat forward tc1abc… 13306:192.168.1.10:3306

```

Connect with: `mysql -h 127.0.0.1 -P 13306 -u dbuser -p`

### Background Operation

Run persistently on POSIX systems:

```bash
nohup tailcat forward --bind=0.0.0.0 tc1abc… 8080 > /var/log/tailcat-forward.log 2>&1 &

```

### Verification

Test a forward mapping local 8080 → remote 80:

```bash
curl http://localhost:8080/

```

## Key Implementation Files

| File | Purpose | Link |
|------|---------|------|
| [`cmd/tailcat/forward.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/forward.go) | CLI implementation, parsing, listener management, connection proxying | [Source](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/forward.go) |
| [`cmd/tailcat/forward_test.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/forward_test.go) | Unit tests for mapping parsing and lifecycle handling | [Source](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/forward_test.go) |
| [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) | Core library: `Client`, `Server`, address handling, WireGuard tunnel | [Source](https://github.com/tailscale/tailcat/blob/main/tailcat.go) |

The `tailcat.Client` type provides `DialTCP` and `DialTCPPort` methods that establish TCP streams inside the encrypted tunnel. These are called by `forwardListener` in [`cmd/tailcat/forward.go`](https://github.com/tailscale/tailcat/blob/main/cmd/tailcat/forward.go) for each proxied connection.

## Summary

- **`tailcat forward`** creates local TCP listeners that tunnel through WireGuard to a remote server

- **Mapping syntax** supports simple ports (`8080`), local:remote pairs (`18080:8080`), auto-assignment (`0:8080`), and full address targets (`13306:192.168.1.10:3306`)

- **No control plane required** — direct peer-to-peer encryption via Tailscale's data plane

- **Exit node mode** enables forwarding to arbitrary IPs reachable by the server

- **Background-friendly** with standard shell tools like `nohup`

## Frequently Asked Questions

### Does tailcat forward require a Tailscale control plane?

No. Unlike standard Tailscale, `tailcat` operates control-plane-free. The WireGuard tunnel is established directly between peers using the tailcat address (`tc1abc…`). The `tailcat.Client` in [`tailcat.go`](https://github.com/tailscale/tailcat/blob/main/tailcat.go) manages the encrypted connection without coordinating with Tailscale's coordination server.

### Can I forward multiple ports in one command?

Yes. Pass multiple mapping arguments: `tailcat forward tc1abc… 8080 8443 3000`. Each spawns an independent `forwardListener` goroutine. You can mix syntax styles: `8080 18080:8080 0:3000`.

### What's the difference between `DialTCP` and `DialTCPPort`?

`DialTCPPort` accepts a port number and connects to `127.0.0.1:<port>` on the remote server—used for simple port-only mappings. `DialTCP` accepts a full `netip.AddrPort`—used when you specify a complete destination address like `192.168.1.10:3306`. Both return a `net.Conn` over the encrypted tunnel.

### How do I troubleshoot a failing forward?

Check: (1) The tailcat server is running and reachable—test with `tailcat dial tc1abc…`; (2) For exit-node forwarding, verify the server runs with exit node privileges; (3) Local bind permissions—ports <1024 need root; (4) Firewall rules on the `--bind` address. Enable verbose logging if available in your tailcat build.