WireGuard on Banana Pi: Site-to-Site VPN Setup, MTU Tuning, and Throughput Testing (2026 Edition)

WireGuard VPN tunnel diagram connecting two Banana Pi boards for site-to-site encrypted networking

Why WireGuard on a Banana Pi

WireGuard has been in-tree since kernel 5.6 and is the simplest way to build a site-to-site tunnel between two SBCs — or between an SBC and a VPS. Compared to OpenVPN, it uses less CPU on the Allwinner A20 (roughly 3x faster throughput), has a smaller attack surface, and the config is a single file per interface.

This guide connects two Banana Pi boards across different networks. Both peers route traffic for their local subnets through the tunnel. By the end, devices on LAN A can reach devices on LAN B and vice versa.

Prerequisites

Network layout used in this guide:
Site A: LAN 192.168.1.0/24, Banana Pi at 192.168.1.1, public IP 203.0.113.10
Site B: LAN 192.168.2.0/24, Banana Pi at 192.168.2.1, behind NAT
Tunnel: 10.0.0.1/32 (Site A) ↔ 10.0.0.2/32 (Site B)

Install WireGuard

# On both peers
sudo apt update
sudo apt install wireguard wireguard-tools

# Verify the kernel module loads
sudo modprobe wireguard
lsmod | grep wireguard
# Should show: wireguard  xxxxxx  0
Kernel compatibility: If modprobe wireguard fails, your kernel may be too old or missing the module. Check with uname -r — you need 5.6+ for in-tree WireGuard. On Armbian, switch to the current branch kernel if you're on legacy. See the kernel LTS guide for branch options.

Generate Key Pairs

# On Site A
wg genkey | tee /etc/wireguard/site-a-private.key | wg pubkey > /etc/wireguard/site-a-public.key
chmod 600 /etc/wireguard/site-a-private.key

# On Site B
wg genkey | tee /etc/wireguard/site-b-private.key | wg pubkey > /etc/wireguard/site-b-public.key
chmod 600 /etc/wireguard/site-b-private.key

# Exchange public keys between peers (copy via SSH, not over unencrypted channels)
cat /etc/wireguard/site-a-public.key   # Copy this to Site B's config
cat /etc/wireguard/site-b-public.key   # Copy this to Site A's config

Configure Both Peers

Site A/etc/wireguard/wg0.conf:

[Interface]
Address = 10.0.0.1/32
ListenPort = 51820
PrivateKey = <contents of site-a-private.key>

[Peer]
PublicKey = <contents of site-b-public.key>
AllowedIPs = 10.0.0.2/32, 192.168.2.0/24
PersistentKeepalive = 25

Site B/etc/wireguard/wg0.conf:

[Interface]
Address = 10.0.0.2/32
PrivateKey = <contents of site-b-private.key>

[Peer]
PublicKey = <contents of site-a-public.key>
Endpoint = 203.0.113.10:51820
AllowedIPs = 10.0.0.1/32, 192.168.1.0/24
PersistentKeepalive = 25
Tip: Site B has no ListenPort because it's behind NAT. It initiates the connection to Site A's public endpoint. PersistentKeepalive = 25 keeps the NAT mapping alive.

Site-to-Site Routing

Enable IP forwarding on both peers and set up routes so LAN devices can reach the remote subnet:

# Enable IP forwarding (both peers)
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf

# Bring up the tunnel
sudo wg-quick up wg0

# Verify the tunnel is established
sudo wg show
# You should see a peer with a recent handshake timestamp

# Test tunnel connectivity
ping -c 3 10.0.0.2   # From Site A
ping -c 3 10.0.0.1   # From Site B

# Test cross-site LAN access
ping -c 3 192.168.2.100   # From Site A, reaching a device on Site B's LAN
LAN devices need a route back. Devices on Site A's LAN need to know that 192.168.2.0/24 is reachable via the Banana Pi. Either add a static route on each device, or set the Banana Pi as the default gateway. If you use the Banana Pi as a router, make sure masquerading is configured — see the firewall section below.

MTU Tuning for Allwinner A20

WireGuard encapsulation adds 60 bytes of overhead (IPv4) or 80 bytes (IPv6). The default Ethernet MTU is 1500, so the maximum WireGuard MTU is 1440 for IPv4. In practice, the Allwinner A20 EMAC driver handles 1420 reliably. Lower values reduce fragmentation but increase per-packet overhead.

MTU SettingFragmentationThroughputWhen to Use
1420Rare~85 MbpsDefault, most networks
1380None~80 MbpsPPPoE or double-NAT
1280None~70 MbpsWorst-case path MTU, IPv6 minimum

Add the MTU to your wg0.conf Interface section:

[Interface]
Address = 10.0.0.1/32
ListenPort = 51820
PrivateKey = <key>
MTU = 1420

To find the optimal MTU, test with clamped pings:

# From Site A, send 1392-byte payload (1392 + 28 IP/ICMP header = 1420)
ping -c 5 -M do -s 1392 10.0.0.2
# If this works, 1420 is fine. If it fails, reduce by 20 and retry.

Persistent Configuration with wg-quick

# Enable the tunnel to start at boot
sudo systemctl enable wg-quick@wg0

# Restart after config changes
sudo systemctl restart wg-quick@wg0

# Check status
sudo systemctl status wg-quick@wg0

Firewall Integration

If you followed the hardening guide, UFW blocks everything by default. Allow WireGuard and tunnel traffic:

# Allow WireGuard UDP port (Site A only — Site B doesn't need an inbound rule if behind NAT)
sudo ufw allow 51820/udp comment 'WireGuard'

# Allow forwarding through the tunnel (both peers)
sudo ufw route allow in on wg0 out on eth0
sudo ufw route allow in on eth0 out on wg0

# If LAN devices use the Banana Pi as gateway, add masquerading
# Add to /etc/ufw/before.rules, before the *filter section:
# *nat
# :POSTROUTING ACCEPT [0:0]
# -A POSTROUTING -s 192.168.2.0/24 -o eth0 -j MASQUERADE
# COMMIT

sudo ufw reload

Throughput Testing with iperf3

# Install on both peers
sudo apt install iperf3

# Start server on Site B
iperf3 -s -B 10.0.0.2

# Run test from Site A (through the tunnel)
iperf3 -c 10.0.0.2 -t 30 -P 4

# Expected results on Banana Pi (Allwinner A20, kernel 6.6, MTU 1420):
#   Single stream:  ~60-75 Mbps
#   4 parallel:     ~80-90 Mbps
#   CPU usage:      ~40-60% on one core during test
Performance note: The A20 does WireGuard in software (no crypto offload). Throughput is CPU-bound. If you need more, consider running root on SATA (SATA root guide) to free CPU from SD card I/O contention during transfers.

Troubleshooting

SymptomLikely CauseFix
No handshake after bringing up wg0Firewall blocking UDP 51820 on Site ACheck sudo ufw status, verify port forwarding on router
Handshake works, no traffic passesAllowedIPs doesn't include the remote subnetAdd the remote LAN CIDR to AllowedIPs
Tunnel works, LAN devices can't reach remoteMissing IP forwarding or routeCheck sysctl net.ipv4.ip_forward and LAN device routing
Intermittent drops every ~2 minutesNAT timeout killing the sessionSet PersistentKeepalive = 25 on the NAT'd peer
Fragmentation errors in dmesgMTU too high for the pathLower MTU to 1380 or 1280
High CPU during transfersNormal for A20 software cryptoReduce parallel streams; this is the hardware limit
# Diagnostic commands
sudo wg show                          # Tunnel status and handshake times
sudo journalctl -u wg-quick@wg0      # Service logs
ip route show table main              # Verify routes are installed
sudo tcpdump -i wg0 -c 20            # Watch traffic on the tunnel interface