FreeBSD.software
Home/Blog/How to Set Up WireGuard VPN on FreeBSD
tutorial2026-03-29

How to Set Up WireGuard VPN on FreeBSD

Step-by-step guide to setting up WireGuard VPN on FreeBSD. Covers kernel module installation, key generation, server and client configuration, PF firewall rules, and troubleshooting.

# How to Set Up WireGuard VPN on FreeBSD

WireGuard is a modern, high-performance VPN protocol that has become the preferred choice for securing network traffic on FreeBSD servers. Unlike older solutions such as [OpenVPN](/blog/openvpn-freebsd-setup/) or IPsec, WireGuard uses a minimal codebase, modern cryptography, and a straightforward configuration model. This tutorial walks through every step of setting up a WireGuard VPN server on FreeBSD and connecting clients from Linux, macOS, Windows, iOS, and Android.

By the end of this guide you will have a fully working WireGuard VPN with NAT, PF firewall integration, DNS forwarding, and multiple peer support.

Why WireGuard on FreeBSD

Three reasons make WireGuard the right VPN choice for FreeBSD administrators.

**Simplicity.** A WireGuard configuration file is typically under 20 lines. Compare that to the hundreds of lines required for OpenVPN or the complexity of IPsec/IKEv2 setups. Fewer lines mean fewer misconfigurations and faster audits.

**Performance.** WireGuard runs inside the kernel as the if_wg module on FreeBSD. It avoids the overhead of userspace TLS processing that OpenVPN relies on. In benchmarks, WireGuard consistently delivers higher throughput and lower latency, often saturating a 1 Gbps link with minimal CPU usage.

**Modern cryptography.** WireGuard uses Curve25519 for key exchange, ChaCha20 for symmetric encryption, Poly1305 for authentication, BLAKE2s for hashing, and SipHash24 for hashtable keys. There are no cipher negotiations, no legacy algorithm choices, and no configuration knobs that could weaken security. The protocol either uses its fixed, audited cryptographic suite or it does not connect at all.

FreeBSD has shipped the if_wg kernel module since FreeBSD 13.0. The implementation is maintained in the base kernel tree and receives regular updates. If you are running FreeBSD 13 or later, WireGuard support is available without patching or compiling a custom kernel.

Prerequisites

Before starting, confirm you have the following:

- **FreeBSD 13.0 or later** -- The if_wg kernel module is included in the base system starting with FreeBSD 13. Earlier versions require a third-party port.

- **Root access** -- All commands in this guide require root privileges. Use su - or sudo as appropriate.

- **A public IP address** -- Your FreeBSD server needs a publicly reachable IP (or at minimum, a port-forwarded UDP port). A [FreeBSD-compatible VPS](/blog/best-vps-hosting-freebsd/) works well for this.

- **UDP port 51820 open** -- WireGuard uses UDP exclusively. Ensure your hosting provider or upstream firewall permits inbound UDP on port 51820.

- **A basic understanding of networking** -- You should be comfortable with IP addresses, subnets, and routing concepts.

Verify your FreeBSD version:

sh

freebsd-version

Expected output: 13.2-RELEASE or newer.

Step 1: Install WireGuard Tools

The if_wg kernel module ships with FreeBSD, but the userspace tools for key generation and interface management come from the wireguard-tools package.

sh

pkg install wireguard-tools

This installs wg (the configuration utility) and wg-quick (the interface management wrapper).

Step 2: Load the Kernel Module

Load the if_wg kernel module immediately:

sh

kldload if_wg

Verify it loaded:

sh

kldstat | grep if_wg

You should see a line containing if_wg.ko.

To load the module automatically at boot, add it to /etc/rc.conf:

sh

sysrc kld_list+="if_wg"

This appends if_wg to the kld_list variable. After a reboot, the module will load before any network services start.

Step 3: Generate Server Keys

WireGuard uses asymmetric Curve25519 key pairs. Each peer -- server and client -- needs its own private and public key.

Create the configuration directory and set restrictive permissions:

sh

mkdir -p /usr/local/etc/wireguard

chmod 700 /usr/local/etc/wireguard

Generate the server key pair:

sh

wg genkey | tee /usr/local/etc/wireguard/server_private.key | wg pubkey > /usr/local/etc/wireguard/server_public.key

chmod 600 /usr/local/etc/wireguard/server_private.key

View the keys (you will need them for configuration):

sh

cat /usr/local/etc/wireguard/server_private.key

cat /usr/local/etc/wireguard/server_public.key

Each key is a single line of Base64-encoded text. Keep the private key secret. The public key is safe to share with clients.

Step 4: Generate Client Keys

Repeat the key generation for each client. You can do this on the server or on the client machine itself. Generating on the client is more secure because the private key never crosses the network.

On the server (for convenience):

sh

wg genkey | tee /usr/local/etc/wireguard/client1_private.key | wg pubkey > /usr/local/etc/wireguard/client1_public.key

chmod 600 /usr/local/etc/wireguard/client1_private.key

If you generate keys on the client side, use the same wg genkey | wg pubkey pipeline and transfer only the public key to the server.

Step 5: Create the Server Configuration

Create the file /usr/local/etc/wireguard/wg0.conf with the following complete configuration. Replace placeholder values with your actual keys and network details.

ini

# /usr/local/etc/wireguard/wg0.conf

# FreeBSD WireGuard Server Configuration

[Interface]

# Server private key (from server_private.key)

PrivateKey = SERVER_PRIVATE_KEY_HERE

# VPN subnet address for this server

Address = 10.0.0.1/24

# UDP listen port

ListenPort = 51820

# Optional: run commands after interface comes up / goes down

PostUp = /usr/local/etc/wireguard/postup.sh

PostDown = /usr/local/etc/wireguard/postdown.sh

# --- Peer: Client 1 ---

[Peer]

# Client 1 public key (from client1_public.key)

PublicKey = CLIENT1_PUBLIC_KEY_HERE

# IP address assigned to this client inside the VPN

AllowedIPs = 10.0.0.2/32

# Optional: keep NAT mappings alive (useful if client is behind NAT)

# PersistentKeepalive = 25

**Key points:**

- Address = 10.0.0.1/24 assigns the server the first address in the VPN subnet.

- ListenPort = 51820 is the standard WireGuard port. Change it if you need to avoid detection or conflict.

- Each [Peer] block defines one client. AllowedIPs restricts which source IPs are accepted from that peer.

- PersistentKeepalive is only needed when a client sits behind NAT and needs to keep the connection alive. The server typically does not need it.

Step 6: Create the Client Configuration

Each client needs its own wg0.conf. Below is a complete client configuration that routes all traffic through the VPN (full tunnel).

ini

# Client WireGuard Configuration (wg0.conf)

[Interface]

# Client private key

PrivateKey = CLIENT1_PRIVATE_KEY_HERE

# VPN IP address for this client

Address = 10.0.0.2/24

# DNS server to use through the VPN

DNS = 10.0.0.1

[Peer]

# Server public key

PublicKey = SERVER_PUBLIC_KEY_HERE

# Route all traffic through VPN (full tunnel)

AllowedIPs = 0.0.0.0/0, ::/0

# Server public IP and port

Endpoint = YOUR_SERVER_PUBLIC_IP:51820

# Keep connection alive behind NAT

PersistentKeepalive = 25

If you only want to route VPN subnet traffic through the tunnel (split tunnel), change AllowedIPs to:

ini

AllowedIPs = 10.0.0.0/24

Client Setup by Platform

**Linux:**

sh

sudo apt install wireguard # Debian/Ubuntu

sudo cp wg0.conf /etc/wireguard/wg0.conf

sudo wg-quick up wg0

**macOS:**

Install the WireGuard app from the Mac App Store or use Homebrew:

sh

brew install wireguard-tools

sudo wg-quick up /path/to/wg0.conf

Alternatively, import the configuration file into the WireGuard macOS GUI app.

**Windows:**

Download the WireGuard installer from [wireguard.com/install](https://www.wireguard.com/install/). Open the application, click "Import tunnel(s) from file", select your wg0.conf, and click "Activate".

**iOS and Android:**

Install the WireGuard app from the App Store or Google Play. You can either import a .conf file or scan a QR code. To generate a QR code from the server:

sh

pkg install libqrencode

qrencode -t ansiutf8 < /usr/local/etc/wireguard/client1.conf

This prints a scannable QR code directly in your terminal.

Step 7: Configure PF Firewall Rules

FreeBSD's PF firewall needs rules to allow WireGuard traffic and (for full-tunnel VPNs) NAT the outbound traffic. If you are not yet familiar with PF, read the [PF firewall guide](/blog/pf-firewall-freebsd/) first.

Add the following to /etc/pf.conf:

pf

# /etc/pf.conf -- WireGuard additions

# Define interfaces and networks

ext_if = "vtnet0" # Your external interface (check with ifconfig)

wg_if = "wg0" # WireGuard interface

wg_net = "10.0.0.0/24" # WireGuard VPN subnet

# NAT: masquerade VPN traffic going out the external interface

nat on $ext_if from $wg_net to any -> ($ext_if)

# Allow WireGuard UDP port

pass in on $ext_if proto udp from any to any port 51820

# Allow all traffic on the WireGuard interface

pass in on $wg_if from $wg_net to any

pass out on $wg_if from any to $wg_net

# Allow forwarded traffic from VPN to external

pass out on $ext_if from $wg_net to any

# Allow established connections back

pass in on $ext_if from any to $wg_net flags any

Check your external interface name with ifconfig. Common names on FreeBSD VPS providers include vtnet0, em0, igb0, or bge0.

Verify the PF configuration syntax:

sh

pfctl -n -f /etc/pf.conf

If no errors appear, reload the ruleset:

sh

pfctl -f /etc/pf.conf

Enable PF if it is not already running:

sh

sysrc pf_enable="YES"

sysrc pflog_enable="YES"

service pf start

Step 8: Enable IP Forwarding and NAT

For the server to route traffic between the WireGuard interface and the external network, IP forwarding must be enabled.

Enable it immediately:

sh

sysctl net.inet.ip.forwarding=1

For IPv6 (if needed):

sh

sysctl net.inet6.ip6.forwarding=1

Make it persistent across reboots by adding to /etc/rc.conf:

sh

sysrc gateway_enable="YES"

sysrc ipv6_gateway_enable="YES"

The gateway_enable directive sets net.inet.ip.forwarding=1 at boot time. This is the FreeBSD-specific equivalent of Linux's net.ipv4.ip_forward.

Step 9: Start and Enable WireGuard

Start the WireGuard interface:

sh

wg-quick up wg0

You should see output indicating the interface was created and configured. Verify the interface exists:

sh

ifconfig wg0

Expected output:


wg0: flags=8051 metric 0 mtu 1420

options=80000

inet 10.0.0.1 netmask 0xffffff00

groups: wg

nd6 options=109

To start WireGuard automatically at boot, add to /etc/rc.conf:

sh

sysrc wireguard_enable="YES"

sysrc wireguard_interfaces="wg0"

Here is what your complete /etc/rc.conf additions should look like:

sh

# /etc/rc.conf -- WireGuard related entries

kld_list="if_wg"

gateway_enable="YES"

ipv6_gateway_enable="YES"

pf_enable="YES"

pflog_enable="YES"

wireguard_enable="YES"

wireguard_interfaces="wg0"

To stop the interface:

sh

wg-quick down wg0

To restart after configuration changes:

sh

wg-quick down wg0 && wg-quick up wg0

Step 10: Test the Connection

On the server

Check WireGuard status:

sh

wg show

Sample output:


interface: wg0

public key:

private key: (hidden)

listening port: 51820

peer:

allowed ips: 10.0.0.2/32

latest handshake: 23 seconds ago

transfer: 1.24 MiB received, 3.87 MiB sent

The latest handshake field confirms the client connected successfully. If this field is missing, the client has not yet initiated a connection.

On the client

After running wg-quick up wg0 on the client:

sh

# Ping the server's VPN address

ping 10.0.0.1

# Verify your public IP changed (full tunnel only)

curl ifconfig.me

# Trace the route

traceroute 1.1.1.1

If the ping succeeds and curl ifconfig.me returns your FreeBSD server's public IP, the full tunnel is working correctly.

Step 11: Adding Multiple Peers

To add more clients, generate a new key pair for each and append a [Peer] block to the server's wg0.conf.

Generate keys for a second client:

sh

wg genkey | tee /usr/local/etc/wireguard/client2_private.key | wg pubkey > /usr/local/etc/wireguard/client2_public.key

chmod 600 /usr/local/etc/wireguard/client2_private.key

Add the peer to the server configuration:

ini

# --- Peer: Client 2 ---

[Peer]

PublicKey = CLIENT2_PUBLIC_KEY_HERE

AllowedIPs = 10.0.0.3/32

Create the client configuration for client 2:

ini

# Client 2 WireGuard Configuration

[Interface]

PrivateKey = CLIENT2_PRIVATE_KEY_HERE

Address = 10.0.0.3/24

DNS = 10.0.0.1

[Peer]

PublicKey = SERVER_PUBLIC_KEY_HERE

AllowedIPs = 0.0.0.0/0, ::/0

Endpoint = YOUR_SERVER_PUBLIC_IP:51820

PersistentKeepalive = 25

Assign each client a unique IP address within the 10.0.0.0/24 subnet. With a /24 subnet you can support up to 253 peers (10.0.0.2 through 10.0.0.254).

Reload the server without dropping existing connections:

sh

wg syncconf wg0 <(wg-quick strip wg0)

This applies configuration changes without restarting the interface, so existing peer sessions remain active.

Step 12: DNS Configuration

If clients use the VPN as a full tunnel, they need DNS resolution through the VPN. You have three options.

Option A: Use a Public DNS Resolver

Set DNS = 1.1.1.1, 9.9.9.9 in the client configuration. This is the simplest approach but means DNS queries exit the VPN tunnel at the server and go to a third-party resolver.

Option B: Run a Local DNS Resolver on the Server

Install Unbound on your FreeBSD server:

sh

pkg install unbound

Configure /usr/local/etc/unbound/unbound.conf:

yaml

server:

interface: 10.0.0.1

interface: 127.0.0.1

access-control: 10.0.0.0/24 allow

access-control: 127.0.0.0/8 allow

hide-identity: yes

hide-version: yes

do-not-query-localhost: no

forward-zone:

name: "."

forward-addr: 1.1.1.1

forward-addr: 9.9.9.9

Enable and start Unbound:

sh

sysrc unbound_enable="YES"

service unbound start

Set DNS = 10.0.0.1 in client configurations. Now all DNS queries from VPN clients are resolved by your own server, preventing DNS leaks.

Option C: Use DNS Over TLS

Configure Unbound to forward queries over TLS for additional privacy:

yaml

server:

interface: 10.0.0.1

interface: 127.0.0.1

access-control: 10.0.0.0/24 allow

access-control: 127.0.0.0/8 allow

tls-cert-bundle: /etc/ssl/cert.pem

forward-zone:

name: "."

forward-tls-upstream: yes

forward-addr: 1.1.1.1@853#cloudflare-dns.com

forward-addr: 9.9.9.9@853#dns.quad9.net

This encrypts DNS queries between your server and the upstream resolver.

Step 13: Performance Tuning

WireGuard is fast by default, but a few tweaks can extract more throughput on FreeBSD.

MTU Optimization

The default MTU for WireGuard is 1420. If your external interface has a standard 1500-byte MTU, 1420 is correct (1500 minus 80 bytes of WireGuard overhead). If your provider uses jumbo frames or a non-standard MTU, adjust accordingly:

ini

[Interface]

MTU = 1420

For providers with a 1500-byte path MTU, do not increase this value. If you experience fragmentation issues (large transfers stalling), try lowering it to 1400 or 1380.

Increase Socket Buffer Sizes

For high-throughput scenarios, increase the UDP socket buffers:

sh

sysctl net.inet.udp.recvspace=4194304

sysctl net.inet.udp.maxdgram=4194304

Make them persistent in /etc/sysctl.conf:


net.inet.udp.recvspace=4194304

net.inet.udp.maxdgram=4194304

CPU Affinity

On multi-core servers handling many peers, you can pin the WireGuard processing to specific CPU cores using cpuset. This avoids cache thrashing:

sh

cpuset -l 0-1 -p $(pgrep -f wg0)

Disable TSO/LRO on the External Interface

If you observe poor throughput or packet drops, disabling TCP segmentation offload and large receive offload on the external interface can help:

sh

ifconfig vtnet0 -tso -lro

Make it persistent in /etc/rc.conf:

sh

ifconfig_vtnet0="inet YOUR_IP netmask YOUR_MASK -tso -lro"

Step 14: Troubleshooting Common Issues

Problem: Client cannot reach the server

1. **Check UDP port.** Confirm port 51820 is open. From another machine: nc -zu YOUR_SERVER_IP 51820. If it times out, check your PF rules or hosting provider firewall.

2. **Check the kernel module.** Run kldstat | grep if_wg. If missing, run kldload if_wg.

3. **Check the interface.** Run ifconfig wg0. If the interface does not exist, run wg-quick up wg0.

Problem: Handshake does not complete

1. **Verify keys.** The most common cause is mismatched keys. Confirm the server's [Peer] PublicKey matches the client's actual public key, and vice versa.

2. **Check AllowedIPs.** The server's AllowedIPs for the peer must include the client's assigned IP (e.g., 10.0.0.2/32).

3. **Check Endpoint.** The client config must have the correct server IP and port in the Endpoint field.

4. **Clock skew.** WireGuard uses timestamps for replay protection. If the server or client clock is significantly off, handshakes can fail. Install and enable NTP: sysrc ntpd_enable="YES" && service ntpd start.

Problem: Handshake completes but no traffic flows

1. **IP forwarding.** Verify sysctl net.inet.ip.forwarding returns 1.

2. **NAT rule.** Confirm PF is running (pfctl -s info) and the NAT rule is active (pfctl -s nat).

3. **Routing.** On the server, check that packets from 10.0.0.0/24 are being NATed. Run tcpdump -i wg0 -n to see if packets arrive on the WireGuard interface.

Problem: DNS is not working through the VPN

1. **Check the DNS setting.** The client wg0.conf must have a DNS line.

2. **Check the resolver.** If using DNS = 10.0.0.1, Unbound must be running and listening on that address.

3. **Test directly.** From the client: dig @10.0.0.1 example.com. If this works but normal resolution fails, the client's DNS override is not being applied. On Linux, check /etc/resolv.conf. On macOS, check scutil --dns.

Problem: Slow performance

1. **Check MTU.** Run ping -D -s 1392 10.0.0.1 from the client. If packets are lost, reduce the MTU in your WireGuard config.

2. **Check CPU.** Run top on the server. If a single core is at 100%, WireGuard processing is CPU-bound. Consider a server with faster single-core performance.

3. **Check the host.** On virtualized environments, network performance can vary. Consider a dedicated server or a [higher-tier VPS plan](/blog/best-vps-hosting-freebsd/).

Problem: Connection drops after some time

1. **PersistentKeepalive.** If the client is behind NAT, ensure PersistentKeepalive = 25 is set in the client's [Peer] block.

2. **Firewall state timeout.** PF's default UDP state timeout may be too short. Add to /etc/pf.conf: set timeout { udp.first 120, udp.multiple 120, udp.single 60 }.

Step 15: Server Hardening

A VPN server is a high-value target. Apply these additional measures. For a comprehensive approach, follow the [FreeBSD server hardening guide](/blog/hardening-freebsd-server/).

Restrict SSH to the VPN

Once WireGuard is working, restrict SSH access to the VPN subnet:

pf

pass in on $wg_if proto tcp from $wg_net to any port 22

block in on $ext_if proto tcp from any to any port 22

Log blocked traffic

Enable PF logging for dropped packets:

pf

block log all

View the log with:

sh

tcpdump -n -e -ttt -r /var/log/pflog

Restrict WireGuard access by source IP

If your clients have known IPs, restrict UDP 51820 to those addresses:

pf

table { 203.0.113.10, 198.51.100.20 }

pass in on $ext_if proto udp from to any port 51820

PostUp and PostDown Scripts

Earlier the server configuration referenced postup.sh and postdown.sh. These scripts handle firewall rules dynamically so you do not need to manually edit /etc/pf.conf every time WireGuard starts or stops.

Create /usr/local/etc/wireguard/postup.sh:

sh

#!/bin/sh

# Enable IP forwarding

sysctl net.inet.ip.forwarding=1

# Reload PF rules

pfctl -f /etc/pf.conf

Create /usr/local/etc/wireguard/postdown.sh:

sh

#!/bin/sh

# Reload PF rules (NAT will be removed since wg0 is down)

pfctl -f /etc/pf.conf

Make them executable:

sh

chmod 700 /usr/local/etc/wireguard/postup.sh

chmod 700 /usr/local/etc/wireguard/postdown.sh

If you prefer to keep your PF rules static (as shown in Step 7), you can remove the PostUp and PostDown lines from wg0.conf.

Complete Configuration Reference

Below are the final, complete configuration files for reference.

Server: /usr/local/etc/wireguard/wg0.conf

ini

[Interface]

PrivateKey = aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuU=

Address = 10.0.0.1/24

ListenPort = 51820

PostUp = /usr/local/etc/wireguard/postup.sh

PostDown = /usr/local/etc/wireguard/postdown.sh

[Peer]

# Client 1 - Laptop

PublicKey = xXyYzZaAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrR=

AllowedIPs = 10.0.0.2/32

[Peer]

# Client 2 - Phone

PublicKey = wWxXyYzZaAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQ=

AllowedIPs = 10.0.0.3/32

[Peer]

# Client 3 - Remote office

PublicKey = vVwWxXyYzZaAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpP=

AllowedIPs = 10.0.0.4/32

Client: wg0.conf (full tunnel)

ini

[Interface]

PrivateKey = uUvVwWxXyYzZaAbBcCdDeEfFgGhHiIjJkKlLmMnNoO=

Address = 10.0.0.2/24

DNS = 10.0.0.1

[Peer]

PublicKey = pPqQrRsStTuUvVwWxXyYzZaAbBcCdDeEfFgGhHiIjJ=

AllowedIPs = 0.0.0.0/0, ::/0

Endpoint = 203.0.113.1:51820

PersistentKeepalive = 25

Server: /etc/rc.conf additions

sh

kld_list="if_wg"

gateway_enable="YES"

ipv6_gateway_enable="YES"

pf_enable="YES"

pflog_enable="YES"

wireguard_enable="YES"

wireguard_interfaces="wg0"

unbound_enable="YES"

ntpd_enable="YES"

Server: /etc/pf.conf

pf

ext_if = "vtnet0"

wg_if = "wg0"

wg_net = "10.0.0.0/24"

set skip on lo0

set timeout { udp.first 120, udp.multiple 120, udp.single 60 }

scrub in all

nat on $ext_if from $wg_net to any -> ($ext_if)

block all

# Allow SSH (restrict to VPN for better security)

pass in on $ext_if proto tcp from any to any port 22

pass in on $wg_if proto tcp from $wg_net to any port 22

# Allow WireGuard

pass in on $ext_if proto udp from any to any port 51820

# Allow all VPN traffic

pass in on $wg_if from $wg_net to any

pass out on $wg_if from any to $wg_net

# Allow VPN traffic out to the internet

pass out on $ext_if from $wg_net to any

# Allow outbound from server itself

pass out on $ext_if from ($ext_if) to any

# Allow DNS to Unbound

pass in on $wg_if proto { tcp, udp } from $wg_net to 10.0.0.1 port 53

Frequently Asked Questions

Is WireGuard included in the FreeBSD base system?

The if_wg kernel module is included in the FreeBSD kernel source tree starting with FreeBSD 13.0 and is available as a loadable module on all supported releases from 13.0 onward. The userspace tools (wg, wg-quick) are installed separately via pkg install wireguard-tools.

Can I run WireGuard and OpenVPN on the same server?

Yes. They use different interfaces and different ports. WireGuard uses UDP 51820 by default and creates a wg0 interface, while [OpenVPN](/blog/openvpn-freebsd-setup/) typically uses UDP 1194 and creates a tun0 interface. Assign them non-overlapping VPN subnets (e.g., 10.0.0.0/24 for WireGuard and 10.0.1.0/24 for OpenVPN) and add appropriate PF rules for both.

How do I revoke a client's access?

Remove the client's [Peer] block from the server's wg0.conf and reload:

sh

wg syncconf wg0 <(wg-quick strip wg0)

There is no certificate revocation list as in OpenVPN. Removing the peer's public key from the server configuration is sufficient. The client will no longer be able to complete a handshake.

Does WireGuard support IPv6?

Yes. WireGuard natively supports IPv6. Add an IPv6 address to the [Interface] section:

ini

[Interface]

Address = 10.0.0.1/24, fd00:wg::1/64

Add IPv6 to peer AllowedIPs:

ini

[Peer]

AllowedIPs = 10.0.0.2/32, fd00:wg::2/128

On the client, include ::/0 in AllowedIPs to route all IPv6 traffic through the VPN.

How many clients can a single FreeBSD WireGuard server support?

There is no hard protocol limit. Practical limits depend on CPU speed, network bandwidth, and available memory. A modern 4-core VPS can handle hundreds of peers with light traffic. For high-throughput deployments with many simultaneous clients, monitor CPU usage and consider scaling horizontally with multiple servers.

Can I use WireGuard for site-to-site VPN between FreeBSD servers?

Yes. Configure each server as a peer of the other. Set AllowedIPs to include the remote site's LAN subnet. For example, if Site A has 192.168.1.0/24 behind it and Site B has 192.168.2.0/24, configure Site A's peer block for Site B with AllowedIPs = 10.0.0.2/32, 192.168.2.0/24 and vice versa. Enable IP forwarding on both sides and add routes for the remote subnets.

How do I update WireGuard on FreeBSD?

Update the userspace tools via pkg:

sh

pkg upgrade wireguard-tools

The kernel module updates with FreeBSD system updates (freebsd-update fetch install). After a kernel update, reboot to load the new module.

Summary

WireGuard on FreeBSD gives you a fast, auditable, and simple VPN with kernel-level performance. The setup consists of five essential steps: install the tools, load the kernel module, generate keys, write the configuration, and open the firewall. Everything after that -- DNS, multi-peer, performance tuning -- builds on the same straightforward foundation.

For related guides, see [OpenVPN on FreeBSD](/blog/openvpn-freebsd-setup/) for a comparison with the traditional approach, [PF firewall configuration](/blog/pf-firewall-freebsd/) for deeper firewall coverage, and [FreeBSD server hardening](/blog/hardening-freebsd-server/) for locking down the rest of your system.