Overview
WolfNet creates a secure private network across the internet. Connect servers across data centres, cloud providers, and on-premises infrastructure as if they were on the same local network. Every packet is encrypted end-to-end with WireGuard-class cryptography.
WolfNet works seamlessly whether your peers are on the same LAN, behind NAT, or spread across continents. LAN peers are discovered automatically, remote peers connect via public IP or DynDNS hostname, and peers behind restrictive firewalls reach each other through relay forwarding — all without any manual port forwarding.
Key Features
- Encrypted mesh networking — X25519 + ChaCha20-Poly1305 (WireGuard-class)
- Invite/Join — Connect peers with a single token, no manual config
- LAN auto-discovery — Peers on the same network find each other automatically
- DynDNS support — Remote access via dynamic DNS hostnames, re-resolved every 60 seconds
- Relay forwarding — No port forwarding needed — peers relay traffic automatically
- Gateway mode — Route internet traffic through a WolfNet peer with NAT
- Peer exchange (PEX) — Nodes share their peer lists so the mesh self-heals
- Container integration — Docker and LXC containers get their own WolfNet addresses
- TUN-based — Layer 3 networking with automatic route management
- Built-in VPN — Access your entire infrastructure from anywhere
- Docker container — Run on Unraid, Synology, TrueNAS, or any Docker host — no full install needed
- IBM Power ready — Pure Rust, builds natively on ppc64le
How It Works
WolfNet creates a TUN virtual network interface (wolfnet0) on each node and assigns it
an IP address on the 10.10.10.0/24 private subnet. All traffic between peers is
encrypted end-to-end using modern cryptography.
Encryption
Every WolfNet node generates an X25519 keypair on first run. When two peers connect, they perform a Diffie-Hellman key exchange to derive a shared secret used as the ChaCha20-Poly1305 encryption key. This is the same cryptographic foundation used by WireGuard and TLS 1.3.
- Key exchange — X25519 (Curve25519 Diffie-Hellman)
- Encryption — ChaCha20-Poly1305 AEAD (authenticated encryption)
- Peer identity — SHA-256 derived 4-byte peer IDs for packet routing
- Replay protection — Monotonic nonce counters with direction flags
- Key storage — Private key at
/etc/wolfnet/private.key(mode 0600)
Packet Flow
- Application sends traffic to a
10.10.10.xaddress - The kernel routes it through the
wolfnet0TUN interface - WolfNet reads the packet, looks up the destination peer
- The packet is encrypted with the peer’s shared ChaCha20-Poly1305 key
- The encrypted packet is sent as a UDP datagram to the peer’s real endpoint
- The receiving peer decrypts and writes the packet to its local TUN interface
- The kernel delivers the packet to the destination application
Installation
WolfNet is included with WolfStack — if you have WolfStack installed, you already have WolfNet. For standalone installation on machines without WolfStack:
curl -sSL https://raw.githubusercontent.com/wolfsoftwaresystemsltd/WolfNet/main/setup.sh | sudo bash
The installer compiles WolfNet from source using Rust, creates the systemd service, and generates a default configuration.
Manual Build
git clone https://github.com/wolfsoftwaresystemsltd/WolfScale.git
cd WolfScale/wolfnet
cargo build --release
sudo cp target/release/wolfnet /usr/local/bin/
sudo cp target/release/wolfnetctl /usr/local/bin/
Requirements
- Linux with TUN device support (
/dev/net/tun) - Root privileges (creates network interfaces)
- UDP port 9600 open for tunnel traffic
- UDP port 9601 open for LAN discovery (optional)
Docker Container
Run WolfNet on systems where you can’t install the full WolfStack binary — Unraid, Synology, TrueNAS, or any Docker host. The container runs in host networking mode and creates an encrypted mesh tunnel just like the native install.
Quick Start
Get an invite token from an existing WolfNet node (wolfnet invite or via the WolfStack UI), then:
docker run -d --name wolfnet \
--network host \
--cap-add NET_ADMIN \
--device /dev/net/tun:/dev/net/tun \
-v wolfnet-config:/etc/wolfnet \
-e WOLFNET_JOIN_TOKEN=<your-invite-token> \
ghcr.io/wolfsoftwaresystemsltd/wolfnet:latest
Docker Compose
services:
wolfnet:
image: ghcr.io/wolfsoftwaresystemsltd/wolfnet:latest
container_name: wolfnet
restart: unless-stopped
network_mode: host
cap_add:
- NET_ADMIN
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- wolfnet-config:/etc/wolfnet
environment:
- WOLFNET_JOIN_TOKEN=${WOLFNET_JOIN_TOKEN}
volumes:
wolfnet-config:
Run with: WOLFNET_JOIN_TOKEN=<token> docker compose up -d
How It Works
- Host networking — The container shares the host’s network namespace so the TUN interface is visible on the host
- No kernel modules — WolfNet uses userspace cryptography (X25519 + ChaCha20-Poly1305), not WireGuard kernel modules. Only
/dev/net/tunis needed - Auto key generation — On first run, the entrypoint generates a key pair and saves it to the config volume
- Join token — Pass
WOLFNET_JOIN_TOKENon first run to automatically join an existing mesh. The token is only used once - Persistent config — The
/etc/wolfnetvolume stores your private key and config across container restarts
Environment Variables
| Variable | Default | Description |
|---|---|---|
WOLFNET_JOIN_TOKEN | — | Invite token from an existing node |
WOLFNET_ADDRESS | auto | Override the auto-assigned WolfNet IP |
WOLFNET_PORT | 9600 | UDP port for tunnel traffic |
WOLFNET_GATEWAY | false | Enable NAT gateway mode |
WOLFNET_DISCOVERY | true | Enable LAN broadcast peer discovery |
WOLFNET_INTERFACE | wolfnet0 | TUN interface name |
WOLFNET_HOSTNAME | container hostname | Hostname reported to peers |
Platform Notes
| Platform | Status | Notes |
|---|---|---|
| Unraid | Fully supported | Unraid template available. Config at /mnt/user/appdata/wolfnet |
| Synology DSM | Supported | May need insmod /lib/modules/tun.ko if TUN not loaded |
| TrueNAS Scale | Supported | Debian-based, TUN available by default |
| Generic Linux | Fully supported | Any Docker host with /dev/net/tun |
Satellite Node (Full NAS Integration)
Combine WolfNet with WolfDisk to join your NAS to the WolfStack cluster with both networking and distributed storage:
services:
wolfnet:
image: ghcr.io/wolfsoftwaresystemsltd/wolfnet:latest
restart: unless-stopped
network_mode: host
cap_add: [NET_ADMIN]
devices: ["/dev/net/tun:/dev/net/tun"]
volumes: [wolfnet-config:/etc/wolfnet]
environment:
- WOLFNET_JOIN_TOKEN=${WOLFNET_JOIN_TOKEN}
wolfdisk:
image: ghcr.io/wolfsoftwaresystemsltd/wolfdisk:latest
restart: unless-stopped
privileged: true
network_mode: host
devices: ["/dev/fuse:/dev/fuse"]
volumes:
- wolfdisk-config:/etc/wolfdisk
- wolfdisk-data:/var/lib/wolfdisk
- /mnt/wolfdisk:/mnt/wolfdisk:rshared
environment:
- WOLFDISK_ROLE=client
- WOLFDISK_PEERS=${WOLFDISK_LEADER}
depends_on: [wolfnet]
volumes:
wolfnet-config:
wolfdisk-config:
wolfdisk-data:
Quick Start
1. Initialize the first node
sudo wolfnet init --address 10.10.10.1
This creates /etc/wolfnet/config.toml and generates an X25519 keypair.
2. Start WolfNet
sudo systemctl start wolfnet
sudo systemctl enable wolfnet
3. Generate an invite token
sudo wolfnet --config /etc/wolfnet/config.toml invite
This outputs a token containing your public key, endpoint, and network details. Share it with the machine you want to connect.
4. Join from a second node
# Install WolfNet on the second machine, then:
sudo wolfnet --config /etc/wolfnet/config.toml join <token>
The joining node is automatically assigned the next available IP address (e.g. 10.10.10.2)
and added as a peer. It also prints a reverse token — run that command on the
first node so both sides know about each other.
5. Restart both nodes
sudo systemctl restart wolfnet
Both peers are now connected with encrypted tunnels. Test with:
ping 10.10.10.2 # From node 1
wolfnetctl peers # View connected peers
wolfnetctl status # View this node's status
💡 WolfStack users: You can also generate invite tokens and add peers from the WolfStack dashboard under Networking → WolfNet, or use the cluster settings page to automatically sync WolfNet connections across all cluster nodes with one click.
Configuration
WolfNet is configured via /etc/wolfnet/config.toml. The wolfnet init
command or WolfStack installer creates this file for you.
# WolfNet Configuration
[network]
interface = "wolfnet0" # TUN interface name
address = "10.10.10.1" # This node's WolfNet IP address
subnet = 24 # Subnet mask (CIDR)
listen_port = 9600 # UDP port for tunnel traffic
gateway = false # Enable gateway/NAT mode
discovery = true # Enable LAN auto-discovery broadcasts
mtu = 1400 # TUN interface MTU
[security]
private_key_file = "/etc/wolfnet/private.key"
# Add peers — one [[peers]] block per remote node
[[peers]]
public_key = "base64_encoded_x25519_public_key"
endpoint = "203.0.113.5:9600" # IP:port or hostname:port
allowed_ip = "10.10.10.2" # Peer's WolfNet address
name = "server-2" # Friendly name (optional)
[[peers]]
public_key = "another_base64_key"
endpoint = "home.example.com:9600" # DynDNS hostname supported
allowed_ip = "10.10.10.3"
name = "home-server"
Configuration Reference
| Section | Key | Default | Description |
|---|---|---|---|
[network] |
interface |
wolfnet0 |
Name of the TUN interface created by WolfNet |
address |
— | This node’s WolfNet IP address (e.g. 10.10.10.1) |
|
subnet |
24 |
Subnet mask in CIDR notation | |
listen_port |
9600 |
UDP port for encrypted tunnel traffic | |
gateway |
false |
Enable gateway mode (NAT for internet access) | |
discovery |
true |
Enable UDP broadcast for LAN auto-discovery | |
mtu |
1400 |
Maximum transmission unit for the TUN interface | |
[security] |
private_key_file |
/etc/wolfnet/private.key |
Path to the X25519 private key file |
[[peers]] |
public_key |
— | Peer’s base64-encoded X25519 public key (required) |
endpoint |
— | Peer’s IP:port or hostname:port (optional if using discovery) | |
allowed_ip |
— | Peer’s WolfNet IP address (required) | |
name |
— | Friendly name for identification (optional) |
Peer Discovery
WolfNet uses multiple methods to find and connect to peers, allowing it to work across any network topology.
LAN Auto-Discovery
When discovery = true (the default), WolfNet broadcasts a UDP announcement every
2 seconds on port 9601. Any other WolfNet node on the same LAN segment will
automatically detect it and establish a connection — no configuration needed.
This means if you install WolfNet on two servers on the same network and run
wolfnet init on each, they will find each other automatically.
Static Endpoints
For peers on different networks, specify their public IP address or hostname in the
endpoint field:
[[peers]]
public_key = "abc123..."
endpoint = "203.0.113.5:9600" # Static public IP
allowed_ip = "10.10.10.2"
name = "cloud-server"
DynDNS Hostnames
If a peer has a dynamic IP address (e.g. a home server), use a DynDNS hostname instead. WolfNet re-resolves hostnames every 60 seconds, so if the IP changes, the connection recovers automatically:
[[peers]]
public_key = "xyz789..."
endpoint = "myserver.duckdns.org:9600" # DynDNS hostname
allowed_ip = "10.10.10.3"
name = "home-lab"
💡 Tip: Free DynDNS services like DuckDNS or No-IP work perfectly with WolfNet. Set up the DynDNS client on your router or server, then use the hostname in your WolfNet config.
Peer Exchange (PEX)
Every 30 seconds, each WolfNet node shares its full peer list with all connected peers. This allows the mesh to self-heal — if node A knows node B and node C, but B and C don’t know each other, A will introduce them via PEX. If direct connection isn’t possible, traffic is relayed through A.
Endpoint Roaming
WolfNet identifies peers by their cryptographic key, not by IP address. If a peer’s public IP changes (e.g. after a NAT rebind or ISP address change), WolfNet updates the endpoint mapping automatically when the next packet arrives. Combined with DynDNS re-resolution, this makes WolfNet resilient to IP address changes.
Remote Access Setup
One of WolfNet’s most powerful features is that the same configuration works for both local and remote access. A peer on the LAN connects via auto-discovery, and the same peer connecting from a coffee shop reaches your network via the configured endpoint — no config changes needed.
Scenario: Office + Home Server
You have a server at the office with a static IP and a server at home with a dynamic IP. You want both to be on the same WolfNet and accessible from anywhere.
Office Server (static IP: 203.0.113.5)
[network]
address = "10.10.10.1"
listen_port = 9600
discovery = true # Also discover LAN peers
[[peers]]
public_key = "home_server_public_key_base64"
endpoint = "home.duckdns.org:9600"
allowed_ip = "10.10.10.2"
name = "home-server"
Home Server (dynamic IP, DuckDNS: home.duckdns.org)
[network]
address = "10.10.10.2"
listen_port = 9600
discovery = true
[[peers]]
public_key = "office_server_public_key_base64"
endpoint = "203.0.113.5:9600"
allowed_ip = "10.10.10.1"
name = "office-server"
Router Configuration
For the home server behind NAT, forward UDP port 9600 to the home server’s local IP in your router settings. Alternatively, if you can’t forward ports, the home server can still connect — the office server just needs to be reachable, and traffic will flow through the established connection.
💡 No port forwarding? If neither side can open ports, use a third node with a public IP as a relay. Traffic between the two will be automatically relayed through the third node via peer exchange. See Relay Forwarding below.
Scenario: Laptop VPN Access
Install WolfNet on your laptop and join the network. Whether you’re in the office (LAN
discovery finds peers automatically) or working remotely (connects via the configured endpoint),
you have the same access to all 10.10.10.x resources.
# On any WolfStack node, generate an invite:
sudo wolfnet --config /etc/wolfnet/config.toml invite
# On your laptop:
sudo wolfnet --config /etc/wolfnet/config.toml join <token>
sudo systemctl restart wolfnet
# Now access everything:
ssh admin@10.10.10.1 # SSH to a server
firefox https://10.10.10.1:8553 # WolfStack dashboard
mysql -h 10.10.10.2 -u root mydb # Database on another node
curl http://10.10.10.100:8080 # App in a container
Scenario: Multi-Site Cluster
Connect servers across multiple data centres and cloud providers. Each site discovers local peers automatically, and cross-site connections use static endpoints or DynDNS:
tunnel
access
Only one peer at each site needs a cross-site endpoint configured. Other peers at the same site are discovered via LAN and reached via relay or PEX.
Invite/Join System
The invite/join system is the easiest way to connect peers. An invite token encodes everything the joining node needs: the inviter’s public key, endpoint, WolfNet IP, subnet, and listen port.
How It Works
- Generate token: Run
wolfnet inviteon an existing node. WolfNet auto-detects your public IP and encodes it into the token. - Join: Run
wolfnet join <token>on the new node. It decodes the token, assigns itself the next available IP, and adds the inviter as a peer. - Reverse token: The join command prints a reverse token. Run this on the original node to complete the bidirectional peering.
- Restart: Restart WolfNet on both nodes — they connect immediately.
# Node A — generate invite
$ sudo wolfnet --config /etc/wolfnet/config.toml invite
Invite token:
sudo wolfnet --config /etc/wolfnet/config.toml join eyJwa...
# Node B — join using the token
$ sudo wolfnet --config /etc/wolfnet/config.toml join eyJwa...
Joined network! This node is 10.10.10.2
Run this on the inviting node to complete the connection:
sudo wolfnet --config /etc/wolfnet/config.toml join eyJxb...
# Node A — run the reverse token
$ sudo wolfnet --config /etc/wolfnet/config.toml join eyJxb...
# Both nodes — restart
$ sudo systemctl restart wolfnet
💡 WolfStack dashboard: In the WolfStack UI, go to Networking → WolfNet to generate invites, add peers, and manage connections graphically. The cluster settings page also has a Update WolfNet Connections button that automatically syncs WolfNet peers across all cluster nodes.
Relay Forwarding
Relay forwarding happens automatically — no configuration needed. When a peer cannot be reached directly (e.g. both sides are behind NAT), traffic is relayed through a mutually reachable peer.
How Relay Works
- Node B shares its peer list with A and C via peer exchange (PEX)
- A learns about C (with
relay_via = B) and C learns about A - When A sends traffic to C, it routes through B automatically
- If A and C later establish a direct connection (e.g. via port forwarding), the relay is dropped in favour of the direct path
Any WolfNet node with two or more peers automatically enables IP forwarding and can act as a relay. The WolfStack dashboard shows relayed peers with a purple “Relay via” badge in the Global View.
Gateway Mode
Gateway mode turns a WolfNet node into a NAT gateway, allowing other peers to route their internet traffic through it. This is useful when you want to give peers internet access through a specific exit point.
Enable Gateway
Set gateway = true in the gateway node’s config:
[network]
address = "10.10.10.1"
gateway = true # Enable NAT gateway
You can also toggle this from the WolfStack dashboard under Networking → WolfNet.
What Gateway Mode Does
- Detects the external network interface automatically
- Enables IP forwarding (
/proc/sys/net/ipv4/ip_forward) - Adds iptables MASQUERADE rules for NAT
- Allows forwarding from WolfNet to the internet
- Blocks inbound traffic from the internet to the WolfNet subnet
- All rules are cleaned up automatically on shutdown
Client Configuration
On a client node that should use the gateway for internet access, add a default route via the gateway’s WolfNet IP:
# Route all traffic through the gateway peer
sudo ip route add default via 10.10.10.1 dev wolfnet0
Container Integration
WolfStack can assign WolfNet IP addresses to Docker containers and LXC containers,
making them directly addressable across the mesh network. Container IPs are allocated
from the 10.10.10.100–254 range.
Docker Containers
When creating a Docker container in WolfStack, assign a WolfNet IP via the WolfNet IP field. This is stored as a Docker label:
# WolfStack sets this automatically, but you can also do it manually:
docker create --name myapp --label wolfnet.ip=10.10.10.100 nginx
WolfStack then:
- Adds the WolfNet IP as an alias on the container’s network interface
- Adds a route for the WolfNet subnet inside the container
- Adds a host-side route so other WolfNet peers can reach the container
- Publishes the container route to remote peers via
routes.json
The container is now reachable at 10.10.10.100 from any peer on the mesh.
LXC Containers
LXC containers use a marker file at /var/lib/lxc/<name>/.wolfnet/ip.
WolfStack assigns the IP and configures routing automatically, including Proxmox LXC
containers with dedicated WolfNet bridge interfaces.
Route Propagation
Container IP mappings are written to /var/run/wolfnet/routes.json. The WolfNet
daemon reads this file and uses it to route traffic destined for containers on remote
nodes to the correct host peer. Routes are propagated across the cluster automatically.
Platform Support
WolfNet is Linux only. It requires a Linux TUN device (/dev/net/tun)
and runs as a root daemon. There are no clients for iOS, Android, Windows, or macOS.
Supported architectures: x86_64, aarch64 (ARM64), armv7, and ppc64le (IBM Power). Any Linux system that can build Rust and create a TUN interface can run WolfNet.
CLI Reference
wolfnet (Daemon & Management)
| Command | Description |
|---|---|
wolfnet |
Run the WolfNet daemon (requires root) |
wolfnet init --address <ip> |
Generate default config with the given WolfNet address |
wolfnet invite |
Generate an invite token for another node (auto-detects public IP) |
wolfnet join <token> |
Join a WolfNet network using an invite token |
wolfnet genkey --output <path> |
Generate a new X25519 keypair |
wolfnet pubkey |
Print this node’s base64 public key |
wolfnet token |
Print public key and endpoint in token format |
Options: --config <path> (default: /etc/wolfnet/config.toml),
--debug (verbose logging)
wolfnetctl (Status & Monitoring)
| Command | Description |
|---|---|
wolfnetctl status |
Show this node’s status (IP, port, gateway, uptime, peer count) |
wolfnetctl list servers |
List all nodes on the network with their roles |
wolfnetctl peers |
Detailed peer info: hostname, IP, endpoint, status, traffic, relay info |
wolfnetctl info |
Combined status + peers output |
wolfnetctl reads from /var/run/wolfnet/status.json — the daemon must
be running.
Systemd Service
The installer creates a systemd service automatically. Common commands:
# Start WolfNet
sudo systemctl start wolfnet
# Check status
sudo systemctl status wolfnet
# View logs
sudo journalctl -u wolfnet -f
# Enable on boot
sudo systemctl enable wolfnet
# Restart after config change
sudo systemctl restart wolfnet
# Reload config without restart (sends SIGHUP)
sudo kill -HUP $(pidof wolfnet)
Network Ports
| Port | Protocol | Purpose |
|---|---|---|
| 9600 | UDP | Encrypted tunnel traffic (handshakes, data, keepalives, PEX) |
| 9601 | UDP | LAN auto-discovery broadcasts (local network only) |
💡 Firewall tip: Only port 9600/UDP needs to be open for remote access. Port 9601 is used for LAN discovery only and does not need to be exposed to the internet.
File Locations
| Path | Purpose |
|---|---|
/etc/wolfnet/config.toml |
Main configuration file |
/etc/wolfnet/private.key |
X25519 private key (base64, mode 0600) |
/var/run/wolfnet/status.json |
Live daemon status (updated every 5 seconds) |
/var/run/wolfnet/routes.json |
Container/VM IP → host WolfNet IP routing table |
Security
- X25519 key exchange — Modern elliptic-curve Diffie-Hellman providing forward secrecy
- ChaCha20-Poly1305 AEAD — Authenticated encryption used by WireGuard, TLS 1.3, and Google’s QUIC protocol
- No central server — Peer-to-peer mesh with no single point of compromise
- No third-party relay — All traffic stays on your own infrastructure
- Token-based authentication — Only devices with a valid invite token can join
- Replay protection — Monotonic nonce counters prevent packet replay attacks
- Key isolation — Private key stored with mode 0600, never transmitted
- Session re-establishment — Sessions are re-established on every handshake, resetting all counters
Troubleshooting
Peers not connecting
- Check that UDP port 9600 is open in your firewall on both sides
- Verify the endpoint address is correct:
wolfnetctl peersshows the last known endpoint - For DynDNS, ensure the hostname resolves correctly:
dig myhost.duckdns.org - Check logs for handshake errors:
sudo journalctl -u wolfnet -f - Run with
--debugfor verbose output:sudo wolfnet --debug
Connected but can’t ping
- Verify both nodes have
wolfnet0interface up:ip addr show wolfnet0 - Check for conflicting firewall rules blocking the
10.10.10.0/24subnet - Ensure the peer shows as connected:
wolfnetctl peers
LAN discovery not working
- Both nodes must be on the same Layer 2 network (same switch/VLAN)
- Check that
discovery = truein config on both nodes - Some cloud providers and corporate networks block UDP broadcast — use static endpoints instead
Connection drops after IP change
- Use a DynDNS hostname instead of a static IP in the
endpointfield - WolfNet re-resolves hostnames every 60 seconds automatically
- Keepalives are sent every 25 seconds to maintain NAT mappings
WolfNet status shows no peers
# Check if daemon is running
sudo systemctl status wolfnet
# Check status file
cat /var/run/wolfnet/status.json | python3 -m json.tool
# Check config
cat /etc/wolfnet/config.toml
# View public key (must match what peers have)
sudo wolfnet --config /etc/wolfnet/config.toml pubkey
Container not reachable via WolfNet
- Verify the container has a WolfNet IP assigned in WolfStack
- Check that routes are published:
cat /var/run/wolfnet/routes.json - Ensure iptables FORWARD rules allow traffic between
wolfnet0and the container bridge - Restart WolfStack to reapply container routes:
sudo systemctl restart wolfstack
High CPU when running alongside Tailscale
If you run WolfNet and Tailscale on the same machine and use Tailscale IPs
(100.x.x.x) as WolfNet peer endpoints, a cross-tunnel routing loop can
occur that causes both wolfnet and tailscaled to spike to
high CPU usage (often 90%+ and 180%+ respectively).
What happens
Tailscale discovers that WolfNet peers are reachable at their WolfNet IPs (e.g.
10.10.10.236) via the wolfnet0 route. Tailscale then sends
its own WireGuard tunnel traffic (UDP port 41641) to those IPs. The kernel routes this
into wolfnet0, WolfNet encrypts it and sends it to the peer’s
Tailscale endpoint — which goes back through Tailscale. The result is Tailscale
traffic being double-encrypted and routed through itself in a feedback loop.
Symptoms
wolfnetat ~90% CPU,tailscaledat ~180% CPU- Stopping WolfNet immediately drops Tailscale CPU to near zero
- Spikes are intermittent — they start when traffic flows between peers with Tailscale endpoints
tcpdump -i wolfnet0 -n -c 20shows traffic on port 41641 (Tailscale’s WireGuard port)
How to diagnose
# Check if Tailscale traffic is flowing through wolfnet0
sudo tcpdump -i wolfnet0 -n -c 20
# If you see packets on port 41641, that's the loop:
# 10.10.10.2.41641 > 10.10.10.236.41641: UDP, length 432
# Confirm with strace (run for ~10 seconds, then Ctrl+C)
sudo strace -c -p $(pidof wolfnet)
# Look for read/sendto counts in the hundreds of thousands
Fix: block Tailscale from using WolfNet IPs
From v0.5.17 onwards, WolfNet detects Tailscale automatically and applies this fix on startup. No manual action is needed — just update WolfNet. The details below are for reference or for older versions.
The fix is an iptables rule that prevents Tailscale’s WireGuard traffic (port 41641) from being routed into the WolfNet subnet. This does not affect WolfNet traffic — WolfNet uses its own port (default 9600) and continues working normally over Tailscale.
# Block Tailscale WireGuard traffic from entering wolfnet subnet
# Replace 10.10.10.0/24 with your WolfNet subnet if different
sudo iptables -I OUTPUT -p udp --dport 41641 -d 10.10.10.0/24 -j DROP
# Make it persistent (Debian/Ubuntu)
sudo apt install -y iptables-persistent
sudo netfilter-persistent save
# Make it persistent (RHEL/Fedora)
sudo iptables-save > /etc/sysconfig/iptables
Apply this rule on every machine running both WolfNet and Tailscale. CPU usage should drop to near zero immediately.
Alternative: use public IPs instead of Tailscale IPs
If your peers have public IP addresses or DynDNS hostnames, use those as WolfNet
endpoints instead of Tailscale 100.x.x.x addresses. This avoids
double-encryption entirely — WolfNet connects directly and provides its own
end-to-end encryption.
# Instead of a Tailscale IP:
# endpoint = "100.112.240.87:9600"
# Use the real public IP or DynDNS hostname:
endpoint = "203.0.113.5:9600"
endpoint = "myhome.dyndns.org:9600"