TunnelMesh CLI Reference
Complete command-line reference for TunnelMesh with examples and walkthroughs.
TL;DR - Most Common Commands
Sudo Required: TunnelMesh needs elevated privileges to create TUN network interfaces. Always run
with sudo except for init, version, and context commands.
Secure Token Storage: Auth tokens are passed via environment variable (TUNNELMESH_TOKEN), not CLI
flags. Never commit tokens to git or share them publicly. Store securely with chmod 600 permissions.
# Generate SSH keys (first time only)
tunnelmesh init
# Bootstrap coordinator (first node)
TOKEN=$(openssl rand -hex 32)
sudo tunnelmesh join --token $TOKEN --context coord
# Save token securely: echo "$TOKEN" > ~/.tunnelmesh/mesh-token.txt && chmod 600 $_
# Join existing mesh as peer (use same token)
sudo tunnelmesh join coord.example.com:8443 --token $TOKEN --context home
# List your contexts
tunnelmesh context list
# Switch active context (changes DNS resolution)
tunnelmesh context use work
# Check your connection status
tunnelmesh status
# List all peers in the mesh
tunnelmesh peers
# Speed test to another peer
tunnelmesh benchmark other-peer --size 100MB
# Install as system service (uses active context)
sudo tunnelmesh service install
sudo tunnelmesh service start
Contexts simplify management: After joining with --context, TunnelMesh remembers your
configuration. Subsequent commands use the active context automatically, so there is no need to specify -c
every time. Perfect for managing multiple meshes (work, home, dev).
Commands that work without a context: init, version, and context subcommands.
Installation
Download the latest release for your platform:
# Linux (amd64)
curl -LO https://github.com/zombar/tunnelmesh/releases/latest/download/tunnelmesh-linux-amd64
chmod +x tunnelmesh-linux-amd64
sudo mv tunnelmesh-linux-amd64 /usr/local/bin/tunnelmesh
# macOS (Apple Silicon)
curl -LO https://github.com/zombar/tunnelmesh/releases/latest/download/tunnelmesh-darwin-arm64
chmod +x tunnelmesh-darwin-arm64
sudo mv tunnelmesh-darwin-arm64 /usr/local/bin/tunnelmesh
# macOS (Intel)
curl -LO https://github.com/zombar/tunnelmesh/releases/latest/download/tunnelmesh-darwin-amd64
chmod +x tunnelmesh-darwin-amd64
sudo mv tunnelmesh-darwin-amd64 /usr/local/bin/tunnelmesh
# Windows (PowerShell as Administrator)
Invoke-WebRequest -Uri "https://github.com/zombar/tunnelmesh/releases/latest/download/tunnelmesh-windows-amd64.exe" -OutFile "tunnelmesh.exe"
Move-Item tunnelmesh.exe C:\Windows\System32\
Or build from source:
git clone https://github.com/zombar/tunnelmesh.git
cd tunnelmesh
make build
sudo mv tunnelmesh /usr/local/bin/
Quick Reference
| Command | Description |
|---|---|
tunnelmesh join |
Join mesh (as peer or coordinator) |
tunnelmesh context |
Manage mesh contexts |
tunnelmesh status |
Show connection status |
tunnelmesh peers |
List mesh peers |
tunnelmesh resolve <name> |
Resolve mesh hostname |
tunnelmesh leave |
Deregister from mesh |
tunnelmesh init |
Generate SSH keys |
tunnelmesh benchmark <peer> |
Speed test to peer |
tunnelmesh service |
Manage system service |
tunnelmesh update |
Self-update binary |
tunnelmesh version |
Show version info |
Global Flags
These flags work with all commands:
| Flag | Short | Description |
|---|---|---|
--config |
-c |
Path to config file |
--log-level |
-l |
Log level: debug, info, warn, error |
Commands in Detail
tunnelmesh init
Initialise TunnelMesh by generating SSH keys.
tunnelmesh init
What it does:
- Creates
~/.tunnelmesh/directory - Generates ED25519 key pair
- Saves private key to
~/.tunnelmesh/id_ed25519 - Saves public key to
~/.tunnelmesh/id_ed25519.pub
Example output:
INF keys generated path=~/.tunnelmesh/id_ed25519
INF public key path=~/.tunnelmesh/id_ed25519.pub
```text
---
### tunnelmesh join
Join the mesh network. When no server URL is provided, automatically bootstraps as a coordinator (first node in mesh).
```bash
tunnelmesh join [server-url] [flags]
Flags:
| Flag | Short | Description |
|---|---|---|
--token |
-t |
Authentication token |
--name |
-n |
Peer name (defaults to hostname) |
--context |
Save/update as named context | |
--exit-node |
Route internet through specified peer | |
--allow-exit-traffic |
Allow this peer as exit for others | |
--latitude |
Manual latitude (-90 to 90) | |
--longitude |
Manual longitude (-180 to 180) | |
--city |
City name for admin UI display | |
--enable-tracing |
Enable runtime tracing |
Example - Basic join:
sudo tunnelmesh join tunnelmesh.example.com \
--token your-secure-token
Example - Join with exit peer:
sudo tunnelmesh join tunnelmesh.example.com \
--token your-secure-token \
--exit-peer server-peer
Example - Join as exit peer:
sudo tunnelmesh join tunnelmesh.example.com \
--token your-secure-token \
--allow-exit-traffic \
--latitude 1.3521 \
--longitude 103.8198 \
--city Singapore
Example - Bootstrap coordinator (first node):
# No server URL = automatically becomes coordinator
sudo tunnelmesh join --token your-secure-token
Generate a config:
tunnelmesh init --peer --output peer.yaml
See peer.yaml.example for all options including
the coordinator section.
tunnelmesh context
Manage mesh contexts. Contexts store configuration paths, allocated IPs, and DNS settings for easy switching between multiple meshes.
tunnelmesh context <subcommand>
Subcommands:
| Subcommand | Description |
|---|---|
create |
Create a new context from a config file |
list |
List all contexts |
use |
Switch active context (updates DNS) |
delete |
Delete a context |
show |
Show context details |
tunnelmesh context create
Create a new context from a configuration file.
tunnelmesh context create <name> --config <path>
Flags:
| Flag | Description |
|---|---|
--config, -c |
Path to config file (required) |
Example:
# Create a peer context
tunnelmesh context create home --config ~/.tunnelmesh/home.yaml
# Create a coordinator context (with coordinator.enabled: true in config)
tunnelmesh context create coordinator --config /etc/tunnelmesh/coordinator.yaml
tunnelmesh context list
List all contexts with their status.
tunnelmesh context list
Example output:
NAME SERVER STATUS ACTIVE
home http://home-server:8080 running *
work https://work.mesh.io stopped
dev http://192.168.1.10:8080 -
```text
- `STATUS`: Service status (`running`, `stopped`, or `-` if no service installed)
- `ACTIVE`: `*` marks the currently active context
---
#### tunnelmesh context use
Switch the active context. This changes which mesh's DNS resolver handles `.tunnelmesh` domains.
```bash
tunnelmesh context use <name>
Example:
tunnelmesh context use work
What happens:
- Updates active context in
~/.tunnelmesh/.context - Reconfigures system DNS resolver for the new mesh's domain
- CLI commands now target the new context by default
Switching contexts doesn't stop running tunnels. Multiple meshes can run simultaneously—only the DNS "focus" changes.
tunnelmesh context delete
Delete a context. Prompts to stop/uninstall service if running.
tunnelmesh context delete <name>
Example:
tunnelmesh context delete dev
Prompts:
- If service is running: "Service is running. Stop and uninstall?"
- "Remove config file at /path/to/config.yaml?"
tunnelmesh context show
Show details of a specific context.
tunnelmesh context show [name]
If no name is provided, shows the active context.
Example output:
Context: home
Config: /home/user/.tunnelmesh/home.yaml
Server: http://home-server:8080
Mesh IP: 10.42.0.5
Domain: .tunnelmesh
DNS Listen: 127.0.0.53:5353
Service: tunnelmesh-home (running)
```text
---
### tunnelmesh status
Show current mesh status.
```bash
tunnelmesh status
Example output:
TunnelMesh Status
=================
Node:
Name: my-laptop
SSH Port: 2222
Private Key: /home/user/.tunnelmesh/id_ed25519
Key FP: SHA256:abc123...
Server:
URL: https://tunnelmesh.example.com
Status: connected
Mesh:
Mesh IP: 10.42.0.5
Last Seen: 2024-01-15 10:30:45
Connectable: yes
Total Peers: 5
Online: 4
TUN Device:
Name: tun-mesh0
MTU: 1400
DNS:
Enabled: yes
Listen: 127.0.0.53:5353
Cache TTL: 300s
tunnelmesh peers
List all peers in the mesh.
tunnelmesh peers
Example output:
NAME MESH IP PUBLIC IP LAST SEEN
-------------------- --------------- -------------------- --------------------
coordinator 10.42.0.1 203.0.113.10 2024-01-15 10:30:45
my-laptop 10.42.0.5 198.51.100.20 2024-01-15 10:30:42
server-eu 10.42.0.3 192.0.2.50 2024-01-15 10:30:40
home-pc 10.42.0.10 203.0.113.11 2024-01-15 10:25:00
tunnelmesh resolve
Resolve a mesh hostname to its IP address.
tunnelmesh resolve <hostname>
Examples:
# Resolve a peer name
tunnelmesh resolve coordinator
# Output: coordinator -> 10.42.0.1
# Resolve with domain suffix
tunnelmesh resolve coordinator.tunnelmesh
# Output: coordinator.tunnelmesh -> 10.42.0.1
# Resolve a DNS alias
tunnelmesh resolve nas
# Output: nas -> 10.42.0.2
tunnelmesh leave
Deregister from the mesh network.
tunnelmesh leave
This removes your peer record from the coordinator. Your mesh IP will be released and may be assigned to another peer. Use this when permanently leaving the mesh, not for temporary disconnects.
tunnelmesh benchmark
Run speed tests between peers.
tunnelmesh benchmark <peer-name> [flags]
Flags:
| Flag | Description | Default |
|---|---|---|
--size |
Transfer size | 10MB |
--direction |
upload or download | upload |
--output, -o |
JSON output file | |
--timeout |
Benchmark timeout | 2m |
--port |
Benchmark server port | 9998 |
--packet-loss |
Packet loss % (0-100) | 0 |
--latency |
Added latency | 0 |
--jitter |
Latency variation | 0 |
--bandwidth |
Bandwidth limit | unlimited |
Example - Basic speed test:
tunnelmesh benchmark server-peer
Example output:
Benchmarking server-peer (10.42.0.1)...
Direction: upload
Size: 10 MB
Duration: 125 ms
Throughput: 640.00 Mbps
Latency: 1.23 ms (avg), 0.95 ms (min), 2.10 ms (max)
Example - Large transfer with JSON output:
tunnelmesh benchmark server-peer --size 100MB --output results.json
Example - Chaos testing (simulate poor network):
# Simulate flaky WiFi
tunnelmesh benchmark server-peer --size 50MB --packet-loss 2
# Simulate mobile connection
tunnelmesh benchmark server-peer --size 10MB --latency 150ms --jitter 50ms
# Simulate bandwidth-constrained link
tunnelmesh benchmark server-peer --size 100MB --bandwidth 10mbps
# Combined stress test
tunnelmesh benchmark server-peer --size 20MB \
--packet-loss 3 \
--latency 50ms \
--jitter 20ms \
--bandwidth 20mbps
See Benchmarking Guide for detailed documentation.
tunnelmesh service
Manage TunnelMesh as a system service.
tunnelmesh service <subcommand> [flags]
Subcommands:
| Subcommand | Description |
|---|---|
install |
Install as system service |
uninstall |
Remove system service |
start |
Start the service |
stop |
Stop the service |
restart |
Restart the service |
status |
Show service status |
logs |
View service logs |
Common flags (all subcommands):
| Flag | Description |
|---|---|
--context |
Target specific context (default: active context) |
Install flags:
| Flag | Short | Description |
|---|---|---|
--user |
Run as user (Linux/macOS) | |
--force |
-f |
Force reinstall |
Logs flags:
| Flag | Description |
|---|---|
--follow |
Follow logs in real-time |
--lines |
Number of lines to show |
Example - Install peer service (context-based):
# First, join and create a context
sudo tunnelmesh join coord.example.com:8443 --token your-mesh-token --config /etc/tunnelmesh/peer.yaml --context home
# Install service for the active context
sudo tunnelmesh service install
# Start service
sudo tunnelmesh service start
# Check status
tunnelmesh service status
# View logs
tunnelmesh service logs --follow
Example - Install coordinator service:
# Create context and install (config has coordinator.enabled: true)
tunnelmesh context create coordinator --config /etc/tunnelmesh/coordinator.yaml
sudo tunnelmesh service install
sudo tunnelmesh service start
Example - Multiple meshes:
# Join two different meshes
sudo tunnelmesh join home-coord.example.com:8443 --token home-token --config ~/.tunnelmesh/home.yaml --context home
sudo tunnelmesh join work-coord.example.com:8443 --token work-token --config ~/.tunnelmesh/work.yaml --context work
# Install services for both
sudo tunnelmesh service install --context home
sudo tunnelmesh service install --context work
# Start both
sudo tunnelmesh service start --context home
sudo tunnelmesh service start --context work
# Control specific context
tunnelmesh service status --context work
tunnelmesh service logs --context home --follow
Service naming: Service names are derived from context names:
- Context
home→ Servicetunnelmesh-home - Context
work→ Servicetunnelmesh-work - Context
default→ Servicetunnelmesh
Platform-specific paths:
| Platform | Service Manager | Config Path | Log Command |
|---|---|---|---|
| Linux | systemd | /etc/tunnelmesh/ |
journalctl -u tunnelmesh |
| macOS | launchd | /etc/tunnelmesh/ |
tunnelmesh service logs |
| Windows | SCM | C:\ProgramData\TunnelMesh\ |
Event Viewer |
tunnelmesh update
Self-update to the latest version.
tunnelmesh update [flags]
Flags:
| Flag | Description |
|---|---|
--version |
Update to specific version |
--force |
Force update even if current |
--check |
Check for updates only |
Example - Check for updates:
tunnelmesh update --check
Example - Update to latest:
sudo tunnelmesh update
Example - Update to specific version:
sudo tunnelmesh update --version v1.2.3
tunnelmesh version
Show version information.
tunnelmesh version
Example output:
tunnelmesh v1.5.0
Commit: abc123def
Build Time: 2024-01-15T10:00:00Z
Configuration Files
Config File Locations
TunnelMesh searches for config files in order:
- Path specified by
--configflag ~/.tunnelmesh/config.yamltunnelmesh.yamlin current directorypeer.yamlin current directory
Configuration Reference
For complete configuration options with documentation, see peer.yaml.example.
The config includes an optional coordinator section for running as a coordinator.
Generate a config file with:
tunnelmesh init --peer # Generate peer.yaml with all options
Walkthroughs
These walkthroughs provide complete end-to-end setups for common use cases. Each includes cloud deployment, peer configuration, and verification steps.
Walkthrough 1: Personal VPN Setup
Set up TunnelMesh for personal use with a cloud server and laptop.
Prerequisites: Cloud server (DigitalOcean, AWS, etc.) with public IP, domain name (optional but recommended), and local machine with TunnelMesh installed.
Step 1: Deploy coordinator (cloud server)
# On your cloud server
sudo mkdir -p /etc/tunnelmesh
# Generate config
tunnelmesh init --peer --output /etc/tunnelmesh/coordinator.yaml
# Edit: set auth_token, enable coordinator.enabled, enable allow_exit_traffic
sudo nano /etc/tunnelmesh/coordinator.yaml
tunnelmesh context create vpn --config /etc/tunnelmesh/coordinator.yaml
sudo tunnelmesh service install
sudo tunnelmesh service start
Step 2: Connect laptop
# On your laptop
tunnelmesh init --peer --output ~/.tunnelmesh/vpn.yaml
# Edit: set server, auth_token, exit_peer: "cloud-server"
nano ~/.tunnelmesh/vpn.yaml
# Join and save as context
sudo tunnelmesh join vpn-coord.example.com:8443 --token vpn-token --config ~/.tunnelmesh/vpn.yaml --context vpn
Step 3: Verify connection
# Check status (uses active context)
tunnelmesh status
# Ping the server
ping cloud-server.tunnelmesh
# Your internet now routes through the cloud server
curl ifconfig.me
Walkthrough 2: Team Development Mesh
Connect a development team for direct machine access.
Step 1: Deploy minimal coordinator
# On a small cloud instance
tunnelmesh init --peer --output coordinator.yaml
# Edit: set auth_token, enable coordinator.enabled: true
nano coordinator.yaml
tunnelmesh context create team --config coordinator.yaml
sudo tunnelmesh service install
sudo tunnelmesh service start
Step 2: Each developer joins
# Each team member runs:
tunnelmesh init
sudo tunnelmesh join coordinator-ip:8080 \
--token team-secret-token \
--context team
Step 3: Team collaboration
# SSH to teammate
ssh alice.tunnelmesh
# Access teammate's dev server
curl http://bob.tunnelmesh:3000
# Connect to teammate's database
psql -h charlie.tunnelmesh mydb
Walkthrough 3: Home Lab Remote Access
Access home network from anywhere.
Step 1: Cloud coordinator
# Deploy to cloud (see terraform docs)
# Or manually:
tunnelmesh init --peer --output coordinator.yaml
# Edit: set auth_token, enable coordinator.enabled: true
nano coordinator.yaml
sudo tunnelmesh join --token my-secret-token --config coordinator.yaml
Step 2: Home server joins
# On your home server (Raspberry Pi, NAS, etc.)
tunnelmesh init --peer --output peer.yaml
# Edit: set name, server, auth_token, add dns.aliases for services
nano peer.yaml
sudo tunnelmesh join coord.example.com:8443 --token my-secret-token --config peer.yaml --context homelab
sudo tunnelmesh service install
sudo tunnelmesh service start
Step 3: Remote access
- Open admin dashboard:
https://cloud.example.com/ - Join the mesh from any device using the native client
- Access
nas.tunnelmesh,plex.tunnelmesh, etc.
Troubleshooting
"No active context"
# List available contexts
tunnelmesh context list
# Set an active context
tunnelmesh context use home
# Or specify context for commands
tunnelmesh status --context home
"Config file required"
# Join with --context to save configuration
sudo tunnelmesh join coord.example.com:8443 --token your-mesh-token --config /path/to/config.yaml --context mycontext
# Or create context manually
tunnelmesh context create mycontext --config /path/to/config.yaml
"Failed to create TUN device"
# Requires root/admin privileges
sudo tunnelmesh join
# Or check TUN device availability
ls -la /dev/net/tun
"Connection refused" to coordinator
# Check coordinator is running
curl http://coordinator:8080/health
# Check firewall
sudo ufw allow 8080/tcp # Linux
DNS not resolving mesh names
# Check resolver is running
dig @127.0.0.53 -p 5353 peer-name.tunnelmesh
# Check system resolver config
# macOS:
cat /etc/resolver/tunnelmesh
# Linux:
resolvectl status
Slow or unreliable connections
# Check transport type
tunnelmesh peers # Shows connection info
# Run benchmark to diagnose
tunnelmesh benchmark peer-name --size 10MB
# Check if using relay (slower)
# Look for "relay" in peers output
Environment Variables
| Variable | Description |
|---|---|
TUNNELMESH_CONFIG |
Default config file path |
TUNNELMESH_LOG_LEVEL |
Log level override |
TUNNELMESH_SERVER |
Default coordinator URL |
TUNNELMESH_TOKEN |
Default auth token |
TunnelMesh is released under the AGPL-3.0 License.