Setting up a WireGuard VPN with FreeBSD

Server Configuration

Install necessary packages

Install wireguard packages, we’all also install libqrencode to make it easier to setup the client on mobile devices, allowing them to scan QR Code.

# pkg install wireguard wireguard-tools libqrencode

Configure WireGuard

To configure the server we need to switch to the wireguard direct /usr/local/etc/wiregaurd and create a couple of directories for clarity.

# cd /usr/local/etc/wireguard
# umask 077
# mkdir server client

Create private/public key pairs

Create the private/public key pairs for our server:

# wg genkey > server/private.key
# wg pubkey < server/private.key > server/public.key

Create private/public key pairs for our client(s):

# mkdir client/android
# wg genkey > client/android/private.key
# wg pubkey < client/android/private.key > client/android/public.key

Creating the WireGuard Configuration File

Using an editor such as vi/vim/nano create the wg0.conf file with the following contents:

# Configure server interface
PrivateKey = ***********************
Address =, fc::1/64 
ListenPort = 51820

# Name = android
PublicKey = ***********************
AllowedIPs =, fc::2/128

Change the key values in the above config file, key values should be copied from the contents of the appropriate files NOT the path of the files:

  • Replace PrivateKey within the Interfaces section with the files contents of server/private.key
  • Replace PublicKey within the Peer section with the the file contents of client/android/public.key

Create the client configuration file

We’re creating the android.conf file on the server, this step is only required if you want the mobile client to be able to scan a QR Code, otherwise you can setup the client configuration file manually on the client.

Create the file /usr/local/etc/wireguard/client/android/android.conf wit the following contents:

PrivateKey = ***********************
Address =, fc::2/128
DNS =,  2606:4700:4700::1111

PublicKey = ***********************
AllowedIPs =, ::/0
Endpoint = endpoint.domain.tld:51820
# Keep connection alive
PersistentKeepalive = 25
  • Substitute the PrivateKey setting with the key from clients/android/server.key (this is the private key for client interface configuration)
  • Substite the Endpoint value for the hostname or IP of the FreeBSD Server you’re connecting to

Now we have the client configuration file, we can show a qr code from the server console to allow the mobile wiregaurd client to scan the QR code for it’s configuration using the command:

qrencode -t ANSIUTF8 < client/android/android.conf

Do not scan this until all steps are completed and the server is fully set up!

Configure FreeBSD System

Add the following contents to /etc/rc.conf:

# Wireguard

# Networking

Configure the pf firewall

Create the file /etc/pf.conf with the following contents, substituting the external interface defined by ext_if to name of your interface (for example, em0, re0, vtnet0):

# Interfaces
ext_if = "vtnet0"
wireguard_if = "wg0"

# Wireguard Settings
wireguard_net_v4 = ""
wireguard_net_v6 = "fc::0/64"

# Rules must be in order: options, normalization, queueing, translation, filtering

## Options
set skip on lo

## Translation
# Nat all wireguard to non-wireguard traffic
nat on $ext_if inet  from $wireguard_net_v4 to { any, !$wireguard_net_v4 } -> ($ext_if)
nat on $ext_if inet6 from $wireguard_net_v6 to { any, !$wireguard_net_v6 } -> {$ext_if}

## Filtering
# block all traffic in
block in on $ext_if

# Allow WireGuard connections in
pass in quick on $ext_if proto udp from any to $ext_if port 51820

# Allow all traffic out
pass out on $ext_if from any to any

Reboot the server

The set up is now complete, reboot the server to start the services.

Once the server has rebooted, you can now proceed to set up the mobile client by installing WireGuard and scanning the QR code as described above, that command again:

# cd /usr/local/etc/wireguard
# qrencode -t ANSIUTF8 < client/android/android.conf

Once you have set up the mobile client, you can use the following command from the server to check it’s status:

# wg show