| pick your distro, get ZFS on root
kldload — your platform, your way, free
Source

Connect Two Machines

Your first WireGuard tunnel — two kldload installs talking to each other securely, done in about 5 minutes.

What you need: two machines running kldload, both connected to the internet (or the same network). That's it. WireGuard is already installed.

What you'll end up with: a private encrypted network between the two machines. Machine A gets address 10.77.0.1, Machine B gets 10.77.0.2. They can ping each other, connect to services, share files — all encrypted.

Why kldload doesn't wrap WireGuard commands

Unlike ZFS — where operations like snapshot management benefit from wrappers that add auto-timestamps, path resolution, and confirmation prompts — WireGuard's native commands are already concise and purpose-built. wg genkey, wg pubkey, wg show, and wg-quick up/down map directly to single tasks with no ambiguity.

kldload ships WireGuard installed and ready. Everything you do in this guide is the native WireGuard tooling — there's nothing kldload-specific to learn. If you already know WireGuard, you already know how to use it on kldload.

What is WireGuard?

WireGuard is a VPN — a way to create an encrypted "tunnel" between machines. Think of it as a private phone line that no one else can listen to, even if your traffic crosses the public internet.

analogy: a pneumatic tube between two offices — only your stuff goes through it, nobody else can see inside

Public key / private key

WireGuard uses key pairs to identify machines. Your private key stays secret on your machine. Your public key gets shared with the other machine — it's how the other side knows it's talking to you.

analogy: your public key is your address. your private key is your front door key. sharing your address is fine. never share your door key.

Step 1 — Generate keys on Machine A

Run these commands on Machine A (the first machine, which will be 10.77.0.1):

# Generate a private key and save it
wg genkey | tee /etc/wireguard/private-a.key | wg pubkey > /etc/wireguard/public-a.key

# Lock down the private key — only root should read it
chmod 600 /etc/wireguard/private-a.key

# Show both keys — copy these somewhere, you'll need them
cat /etc/wireguard/private-a.key
cat /etc/wireguard/public-a.key

You'll see two long strings of random-looking characters. They'll look something like:

# private key (keep this secret — never share it)
qL8Kx2mN3pRvWtYuZoAeBcDfGhIjKlMnOpQrStUvWxY=

# public key (safe to share with the other machine)
mK7Jw1lO2nQsVuXyAaDeBcEfGgHhIjKlMnOpQrStUvW=

Your keys will look different — they're randomly generated. That's correct.


Step 2 — Generate keys on Machine B

SSH into Machine B (the second machine, which will be 10.77.0.2) and run the same commands:

wg genkey | tee /etc/wireguard/private-b.key | wg pubkey > /etc/wireguard/public-b.key
chmod 600 /etc/wireguard/private-b.key

cat /etc/wireguard/private-b.key
cat /etc/wireguard/public-b.key

Copy Machine B's public key. You'll paste it into Machine A's config in the next step.

What to copy where

You need to cross-share the public keys only:
• Machine A's config needs Machine B's public key
• Machine B's config needs Machine A's public key

The private keys never leave their machine.


Step 3 — Create the config on Machine A

On Machine A, create the file /etc/wireguard/wg0.conf. Replace the placeholder values with your actual keys and Machine B's real IP address:

cat > /etc/wireguard/wg0.conf << 'EOF'
[Interface]
# This machine's WireGuard address (the private tunnel address)
Address = 10.77.0.1/24

# This machine's private key
PrivateKey = PASTE_MACHINE_A_PRIVATE_KEY_HERE

# WireGuard will listen on this port for incoming connections
ListenPort = 51820

[Peer]
# Machine B's public key
PublicKey = PASTE_MACHINE_B_PUBLIC_KEY_HERE

# Machine B's WireGuard tunnel address
AllowedIPs = 10.77.0.2/32

# Machine B's real IP address and WireGuard port
# (the actual IP you use to SSH into Machine B)
Endpoint = MACHINE_B_REAL_IP:51820

# Send a keepalive packet every 25 seconds to keep the tunnel alive
PersistentKeepalive = 25
EOF

chmod 600 /etc/wireguard/wg0.conf

A filled-in example looks like this:

[Interface]
Address = 10.77.0.1/24
PrivateKey = qL8Kx2mN3pRvWtYuZoAeBcDfGhIjKlMnOpQrStUvWxY=
ListenPort = 51820

[Peer]
PublicKey = xZ9Qw2kP3mRtVuYzBbCeDfGhIjKlMnOpQrStUvWxYzA=
AllowedIPs = 10.77.0.2/32
Endpoint = 192.168.1.50:51820
PersistentKeepalive = 25

Step 4 — Create the config on Machine B

On Machine B, create /etc/wireguard/wg0.conf with the mirrored settings:

cat > /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.77.0.2/24
PrivateKey = PASTE_MACHINE_B_PRIVATE_KEY_HERE
ListenPort = 51820

[Peer]
PublicKey = PASTE_MACHINE_A_PUBLIC_KEY_HERE
AllowedIPs = 10.77.0.1/32
Endpoint = MACHINE_A_REAL_IP:51820
PersistentKeepalive = 25
EOF

chmod 600 /etc/wireguard/wg0.conf

Notice what changed between the two configs

Address: Machine A is 10.77.0.1, Machine B is 10.77.0.2
PrivateKey: each machine uses its own private key
PublicKey in [Peer]: each machine uses the other machine's public key
AllowedIPs: Machine A allows traffic from .2, Machine B allows traffic from .1
Endpoint: each machine points at the other's real IP


Step 5 — Open the firewall port

WireGuard listens on UDP port 51820. Allow it through the firewall on both machines:

# On both machines:
firewall-cmd --permanent --add-port=51820/udp
firewall-cmd --reload

If you're using ufw instead (common on Debian):

ufw allow 51820/udp

Step 6 — Start the tunnel

Run this on both machines:

wg-quick up wg0

Expected output on Machine A:

[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.77.0.1/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] ip -4 route add 10.77.0.2/32 dev wg0

No errors means it worked. Now check the interface is up:

wg show
interface: wg0
  public key: mK7Jw1lO2nQsVuXyAaDeBcEfGgHhIjKlMnOpQrStUvW=
  private key: (hidden)
  listening port: 51820

peer: xZ9Qw2kP3mRtVuYzBbCeDfGhIjKlMnOpQrStUvWxYzA=
  endpoint: 192.168.1.50:51820
  allowed ips: 10.77.0.2/32
  latest handshake: 2 seconds ago
  transfer: 148 B received, 212 B sent

If you see latest handshake with a recent time, the tunnel is working.


Step 7 — Test it

From Machine A, ping Machine B's tunnel address:

ping 10.77.0.2
PING 10.77.0.2 (10.77.0.2) 56(84) bytes of data.
64 bytes from 10.77.0.2: icmp_seq=1 ttl=64 time=0.842 ms
64 bytes from 10.77.0.2: icmp_seq=2 ttl=64 time=0.731 ms
64 bytes from 10.77.0.2: icmp_seq=3 ttl=64 time=0.698 ms

From Machine B, ping Machine A:

ping 10.77.0.1

Both should respond. If one direction works but not the other, double-check that the firewall port is open on both machines.


Step 8 — Make it permanent

Right now the tunnel only runs until you reboot. To start it automatically on boot, enable the systemd service on both machines:

systemctl enable --now wg-quick@wg0
Created symlink /etc/systemd/system/multi-user.target.wants/wg-quick@wg0.service
  → /lib/systemd/system/wg-quick@.service

That's it. The tunnel will come back up automatically after any reboot.


Troubleshooting

Ping doesn't work

Check wg show — if latest handshake is missing or very old, the peers haven't connected. Common causes:

• Wrong public key in the config (copy-paste error)
• Wrong real IP in Endpoint
• Firewall blocking UDP 51820
• One machine is behind NAT that blocks incoming UDP

wg-quick up fails

Usually a config file syntax error. Check that:

• There's no extra whitespace before [Interface] or [Peer]
• The private key is the full base64 string, not truncated
• The file is at /etc/wireguard/wg0.conf exactly

run: wg-quick up wg0 2>&1 — the error message is usually clear

Check the tunnel status anytime

These commands show you what's happening:

wg show
ip addr show wg0
systemctl status wg-quick@wg0

What's next

Now that your two machines can talk to each other, you can use this tunnel for many things:

  • SSH from one machine to the other using the 10.77.0.x address
  • Replicate your ZFS data — automatic encrypted backups over this tunnel
  • Run services on one machine and access them from the other without opening public ports
  • Add more machines — each gets its own 10.77.0.x address

WireGuard commands — kldload vs native reference table

kldload does not wrap WireGuard commands. All commands below are native WireGuard — the same on any Linux system. They are listed here for reference.

Task kldload Native WireGuard Notes
Generate a key pair (use native) wg genkey | tee private.key | wg pubkey > public.key WireGuard is already installed on kldload; no setup needed
Bring a tunnel up (use native) wg-quick up wg0 Reads /etc/wireguard/wg0.conf
Bring a tunnel down (use native) wg-quick down wg0 Removes the wg0 interface cleanly
Check tunnel status (use native) wg show Shows peers, handshake times, transfer stats
Start tunnel on boot (use native) systemctl enable --now wg-quick@wg0 Standard systemd service; persists across reboots
Open firewall for WireGuard (use native) firewall-cmd --permanent --add-port=51820/udp kldload uses firewalld; Debian can use ufw instead