How to Start a Tailcat Server: CLI Configuration and Examples

Run tailcat serve to start a server that listens for incoming connections over Tailscale's WireGuard data-plane, optionally specifying ports, services, or directories via command-line flags.

Tailcat is an open-source command-line utility maintained in the tailscale/tailcat repository that enables secure networking over Tailscale's mesh. To start a Tailcat server, you invoke the binary with the serve sub-command (or no sub-command) and configure behavior through flags defined in main/cmd/tailcat/tailcat.go. The server implementation in main/tailcat.go handles the underlying WireGuard connections and service multiplexing.

Understanding Tailcat Server Architecture

Tailcat operates as a multi-mode program capable of functioning as a server, client, or utility. When starting a server, the CLI parses flags in main/cmd/tailcat/tailcat.go and initializes a tailcat.Server struct from main/tailcat.go. The server's Start method manages DERP region selection, listener setup, and address generation for client connections.

Essential Configuration Flags

Port and Service Exposure (--serve)

The --serve flag accepts a comma-separated list of ports or service names. Valid service names include ssh, files, exec, and exit-node. When omitted, the server accepts a single connection on any available port and streams data to stdout.

Authentication and Access Control (--key, --allow, --psk)

  • --key: Specifies the path or name of the server's private key (defaults to "default")
  • --allow: Whitelist of client public keys; empty allows all clients
  • --psk: Embeds a WireGuard pre-shared key in the address for additional security

Service-Specific Configuration (--files, --ssh-authorized-keys)

  • --files: Directory path for SFTP access with optional mode suffixes (:ro, :rw, :wo, :wo+)
  • --ssh-authorized-keys: File containing authorized SSH public keys when running the SSH service

Output Formatting (--full-address, --json)

  • --full-address: Embeds DERP node information directly in the printed address
  • --json: Outputs the listening address as parseable JSON to stdout

Practical Examples for Starting a Tailcat Server

Minimal Server (Default Mode)

Start a basic server that accepts one connection and writes to stdout:

tailcat serve

TCP Port Forwarding

Expose a specific TCP port to incoming connections:

tailcat serve --serve 8080

SSH Service with Public Key Authentication

Run an SSH server restricted to specific keys:

tailcat serve \
  --serve ssh \
  --ssh-authorized-keys ~/.ssh/id_rsa.pub

SFTP File Server (Read-Only)

Serve a directory over SFTP with read-only access:

tailcat serve \
  --serve files \
  --files /srv/shared:ro

Command Execution per Connection (Exec Service)

Run a command for each incoming connection, piping stdin/stdout through the WireGuard tunnel:

tailcat serve -- /usr/bin/tee /tmp/connection.log

Multiple Simultaneous Services

Combine SSH, SFTP, and TCP port listening:

tailcat serve \
  --serve ssh,files,8080 \
  --ssh-authorized-keys ~/.ssh/authorized_keys \
  --files /var/www:rw

Scripting and Automation

Output the address in JSON format for programmatic use:

tailcat serve --json

Server Initialization Lifecycle

When tailcat serve executes, the following sequence occurs in the source code:

  1. Flag Parsing: main/cmd/tailcat/tailcat.go validates CLI arguments and service combinations
  2. Server Construction: The CLI instantiates a tailcat.Server from main/tailcat.go with the parsed configuration
  3. DERP Selection: The Start method selects a DERP region (or extracts it from a provided address)
  4. Listener Setup: The server binds to requested ports and initializes service handlers
  5. Address Publication: The server prints the Tailcat address (or JSON) to stdout for client connection

Summary

  • Run tailcat serve to start a server with default settings that streams to stdout
  • Use --serve to expose specific ports or activate services like SSH, SFTP, or exec
  • Configure authentication via --key, --allow, and --ssh-authorized-keys
  • Control file access modes with suffixes like :ro (read-only) and :rw (read-write)
  • Use --json or --full-address to simplify automated client connections

Frequently Asked Questions

What is the difference between running tailcat and tailcat serve?

When invoked without sub-commands, Tailcat defaults to server mode identical to tailcat serve. Both commands initialize the tailcat.Server implementation in main/tailcat.go and listen for incoming WireGuard connections. The explicit serve sub-command is recommended for clarity in scripts and documentation.

How does Tailcat handle authentication for incoming connections?

Tailcat uses WireGuard public keys for transport-layer authentication. The --allow flag restricts connections to specific client public keys, while service-level authentication (like --ssh-authorized-keys) controls access to individual services such as SSH. The --psk flag adds a pre-shared key for additional security layers.

Can Tailcat serve multiple protocols simultaneously?

Yes. The --serve flag accepts comma-separated values allowing you to combine TCP ports with service names like ssh, files, and exec. The server multiplexes these services over the same Tailscale connection, as implemented in the listener setup logic within main/tailcat.go.

Where are the server configuration flags defined in the source code?

All CLI flags—including --serve, --files, and --ssh-authorized-keys—are defined in main/cmd/tailcat/tailcat.go. The tailcat.Server struct and its Start method, which processes these configurations into running services, are located in main/tailcat.go.

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 →