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.
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.
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
Check the tunnel status anytime
These commands show you what's happening:
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.xaddress - 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.xaddress
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 |