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
- Two Banana Pi boards running Debian 13 or Armbian with kernel 6.1+ (Debian 13 setup or Armbian setup)
- Each board has a static LAN IP and can reach the internet
- At least one peer must have a public IP or a forwarded UDP port (the other can be behind NAT)
- SSH hardened on both boards (hardening 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
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
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
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 Setting | Fragmentation | Throughput | When to Use |
|---|---|---|---|
| 1420 | Rare | ~85 Mbps | Default, most networks |
| 1380 | None | ~80 Mbps | PPPoE or double-NAT |
| 1280 | None | ~70 Mbps | Worst-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
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
| No handshake after bringing up wg0 | Firewall blocking UDP 51820 on Site A | Check sudo ufw status, verify port forwarding on router |
| Handshake works, no traffic passes | AllowedIPs doesn't include the remote subnet | Add the remote LAN CIDR to AllowedIPs |
| Tunnel works, LAN devices can't reach remote | Missing IP forwarding or route | Check sysctl net.ipv4.ip_forward and LAN device routing |
| Intermittent drops every ~2 minutes | NAT timeout killing the session | Set PersistentKeepalive = 25 on the NAT'd peer |
| Fragmentation errors in dmesg | MTU too high for the path | Lower MTU to 1380 or 1280 |
| High CPU during transfers | Normal for A20 software crypto | Reduce 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
Related Reading
- Hardening an SBC for the internet — prerequisite firewall and SSH setup
- Secure cold-storage vault with Banana Pi — encrypted storage on the same hardware
- Kernel LTS for SBCs — WireGuard module availability by kernel version
- Move root to SATA — reduce CPU contention from SD I/O during VPN transfers
- Banana Pi product overview — hardware specifications