Show HN: Ts-SSH – SSH over Tailscale without running the daemon

6 hours ago 1

A streamlined command-line SSH client and SCP utility that connects to your Tailscale network using tsnet. Features powerful multi-host operations, batch command execution, and real tmux integration - all without requiring the full Tailscale daemon.

Perfect for DevOps teams who need fast, reliable SSH access across their Tailscale infrastructure.

🚀 Core SSH/SCP Functionality

  • Userspace Tailscale connection using tsnet - no daemon required
  • Multiple authentication methods: SSH keys, password prompts, or both
  • Interactive SSH sessions with full PTY support and terminal resizing
  • Secure host key verification using ~/.ssh/known_hosts
  • Direct SCP transfers with automatic upload/download detection

💪 Multi-Host Power Operations

  • --list: Fast host discovery with online/offline status
  • --multi host1,host2,host3: Real tmux sessions with multiple SSH connections
  • --exec "command" host1,host2: Batch command execution across hosts
  • --parallel: Concurrent command execution for faster operations
  • --copy file host1,host2:/path/: Multi-host file distribution
  • --pick: Simple interactive host selection

🛠️ Professional DevOps Features

  • ProxyCommand support (-W) for integration with standard tools
  • Cross-platform: Linux, macOS (Intel/ARM), Windows
  • Multi-language support: English and Spanish localization
  • Fast startup - no UI frameworks or complex initialization
  • Composable commands - works perfectly in scripts and automation
  • Clear error handling and helpful feedback
  • Go: Version 1.18 or later installed (go version).
  • Tailscale Account: An active Tailscale account.
  • Target Node: A machine within your Tailscale network running an SSH server that allows connections from your user/key/password.

You can install ts-ssh using go install (recommended) or build it manually from the source.

Using go install:

go install github.com/derekg/ts-ssh@latest

(Make sure your $GOPATH/bin or $HOME/go/bin is in your system's PATH)

Manual Build:

  1. Clone the repository:
    git clone https://github.com/derekg/ts-ssh.git cd ts-ssh
  2. Build the executable: You can now run ./ts-ssh.

Cross-Compilation:

You can easily cross-compile for other platforms. Set the GOOS and GOARCH environment variables. Use CGO_ENABLED=0 for easier cross-compilation.

  • For macOS (Apple Silicon):
    CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -o ts-ssh-darwin-arm64 .
  • For macOS (Intel):
    CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -o ts-ssh-darwin-amd64 .
  • For Linux (amd64):
    CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ts-ssh-linux-amd64 .
  • For Windows (amd64):
    CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -o ts-ssh-windows-amd64.exe .
Usage: ts-ssh [options] [user@]hostname[:port] [command...] ts-ssh --list # List available hosts ts-ssh --multi host1,host2,host3 # Multi-host tmux session ts-ssh --exec "command" host1,host2 # Run command on multiple hosts ts-ssh --copy file.txt host1,host2:/tmp/ # Copy file to multiple hosts ts-ssh --pick # Interactive host picker Powerful SSH/SCP tool for Tailscale networks. Options: -W string forward stdio to destination host:port (for use as ProxyCommand) -control-url string Tailscale control plane URL (optional) -copy string Copy files to multiple hosts (format: localfile host1,host2:/path/) -exec string Execute command on specified hosts -i string Path to SSH private key (default "/home/user/.ssh/id_rsa") -insecure Disable host key checking (INSECURE!) -l string SSH Username (default "user") -lang string Language for CLI output (en, es) -list List available Tailscale hosts -multi string Start tmux session with multiple hosts (comma-separated) -parallel Execute commands in parallel (use with --exec) -pick Interactive host picker (simple selection) -tsnet-dir string Directory to store tsnet state (default "/home/user/.config/ts-ssh-client") -v Verbose logging -version Print version and exit

Arguments:

  • For SSH: [user@]hostname[:port] [command...]
    • hostname must be the Tailscale MagicDNS name or Tailscale IP address of the target machine.
    • user defaults to your current OS username if not provided or specified with -l.
    • port defaults to 22 if not provided.
    • command... (optional): If provided, executes the command on the remote host instead of starting an interactive shell.
  • For SCP (direct CLI):
    • Upload: local_path [user@]hostname:remote_path
    • Download: [user@]hostname:remote_path local_path
    • The user@ in the remote argument is optional; if not provided, the user from -l or the default OS user will be used.
# List all Tailscale hosts with status ts-ssh --list # Detailed host information ts-ssh --list -v # Interactive host picker ts-ssh --pick
# Connect to a single host ts-ssh your-server # Connect as specific user ts-ssh admin@your-server ts-ssh -l admin your-server # Run a remote command ts-ssh your-server uname -a # Use specific SSH key ts-ssh -i ~/.ssh/my_key user@your-server

🚀 Multi-Host Power Operations

# Create tmux session with multiple hosts ts-ssh --multi web1,web2,db1 # Run command on multiple hosts (sequential) ts-ssh --exec "uptime" web1,web2,web3 # Run command on multiple hosts (parallel) ts-ssh --parallel --exec "systemctl status nginx" web1,web2 # Check disk space across all web servers ts-ssh --exec "df -h" web1.domain,web2.domain,web3.domain

📁 File Transfer Operations

# Single host SCP ts-ssh local.txt your-server:/remote/path/ ts-ssh your-server:/remote/file.txt ./ # Multi-host file distribution ts-ssh --copy deploy.sh web1,web2,web3:/tmp/ ts-ssh --copy config.json db1,db2:/etc/myapp/ # Copy with specific user ts-ssh --copy -l admin backup.tar.gz server1,server2:/backups/
# ProxyCommand integration scp -o ProxyCommand="ts-ssh -W %h:%p" file.txt server:/path/ # Version information ts-ssh -version # Verbose logging for debugging ts-ssh --list -v
# Use Spanish interface ts-ssh --lang es --list LANG=es ts-ssh --help # Use English (default) ts-ssh --lang en --help LC_ALL=en ts-ssh --help # Set permanent language preference export TS_SSH_LANG=es ts-ssh --help # Now shows Spanish

Language Detection Priority:

  1. CLI flag (--lang)
  2. TS_SSH_LANG environment variable
  3. LC_ALL environment variable
  4. LANG environment variable
  5. Default (English)

Supported Languages:

  • 🇺🇸 English (en) - Default
  • 🇪🇸 Spanish (es) - Complete translation

💡 Real-World DevOps Scenarios

# Deploy configuration to all web servers ts-ssh --copy nginx.conf web1,web2,web3:/etc/nginx/ ts-ssh --parallel --exec "sudo nginx -t && sudo systemctl reload nginx" web1,web2,web3 # Check service status across infrastructure ts-ssh --parallel --exec "systemctl is-active docker" node1,node2,node3 # Collect logs from multiple servers ts-ssh --exec "tail -100 /var/log/app.log" app1,app2,app3 # Emergency system info gathering ts-ssh --parallel --exec "uptime && free -h && df -h" web1,web2,db1,db2

The --multi flag creates real tmux sessions with SSH connections to multiple hosts. This provides a professional terminal multiplexing experience:

# Create tmux session with 3 hosts ts-ssh --multi web1,web2,db1

Once connected, use standard tmux key bindings:

  • Ctrl+B n - Next window (next host)
  • Ctrl+B p - Previous window (previous host)
  • Ctrl+B 1-9 - Switch to window number
  • Ctrl+B c - Create new window
  • Ctrl+B d - Detach from session
  • Ctrl+B ? - Show all key bindings
# List active tmux sessions tmux list-sessions # Reconnect to a detached session tmux attach-session -t ts-ssh-1234567890 # Kill a specific session tmux kill-session -t ts-ssh-1234567890

Note: The Tailscale authentication flow may show verbose logs during startup. Use -v for clearer diagnostic output if needed.

The first time you run ts-ssh on a machine, or if its Tailscale authentication expires, it will need to authenticate to your Tailscale network.

The program will print a URL to the console. Copy this URL and open it in a web browser. Log in to your Tailscale account to authorize this client ("ts-ssh-client" or the hostname set in code).

Once authorized, ts-ssh stores authentication keys in the state directory (~/.config/ts-ssh-client by default, configurable with -tsnet-dir) so you don't need to re-authenticate every time.

  • Host Key Verification: This tool performs host key verification against ~/.ssh/known_hosts by default. This is a crucial security feature to prevent Man-in-the-Middle (MITM) attacks.
  • -insecure Flag: The -insecure flag disables host key checking entirely. This is dangerous and should only be used in trusted environments or for specific testing purposes where you fully understand the security implications. You are vulnerable to MITM attacks if you use this flag carelessly.

This project is licensed under the MIT License - see the LICENSE file for details.

Read Entire Article