Installing and Configuring WireGuard on Raspberry Pi OS (May 2020)
Installing and Configuring WireGuard on Raspberry Pi OS (January 2021)-> <-Installing and Configuring WireGuard on a Raspberry Pi

Raspbian is dead, long live Raspberry Pi OS. The May 2020 version of Raspberry Pi OS is not much of a change from Raspbian Buster. Both are based on Debian Buster, using the 4.19 Linux kernel. However, I had to change my instructions on I²C on the Raspberry Pi because of changes in the OS and I was wondering if the same would be true for WireGuard. Also I wanted to know if WireGuard was included in the beta upgrade to the 5.4 Linux kernel of the new OS. As it turned out, WireGuard is not included in the 5.4 upgrade, and the previous post about WireGuard which was based on Raspbian Buster remained valid. Nevertheless, I modified this post in the hope of improving it, because it is one of the most consulted pages on this site. Just in case I actually made things worse here is a link to the older version Installing and Configuring WireGuard on Raspbian Buster.

This post is obsolete. You should be reading the newer version. See Installing and Configuring WireGuard on Raspberry Pi for all the posts on this subject.
January 6, 2021

IMPORTANT NOTE: An addendum named Installing WireGuard on all Raspberry Pi Models (July 2020) was written to communicate as quickly as possible instructions sent by Daniel T. Wagner on July 27 showing how to install WireGuard on any Raspberry Pi from the Raspbian Testing repository. Since then this post and the addendum have been merged into a new version, Installing and Configuring WireGuard on Raspberry Pi OS (August 2020) which, hopefully, contains the easiest way to install, configure and use WireGuard on all Raspberry Pi models running Raspbery Pi OS. Please consult that newer document.

Table of Contents

  1. Prerequisites to Installing WireGuard on a Raspberry Pi 2 v1.2 or above
  2. Installing WireGuard
  3. Enabling Remote Access to the Local Network
    1. Public IP or Dynanic DNS Host Name
    2. Port Forwarding
    3. Enabling IP Forwarding
  4. Configuring WireGuard
    1. Install the Adrian Mihalko User Management Script
    2. Generate the Private and Public Server Keys
    3. Create and Edit the Server Definition File
    4. Edit the Client Configuration Template
    5. Edit the Server Configuration Template
    6. Create an Empty WireGuard Server Configuration File
    7. Enable Automatic Start of the wg0 Interface at Boot Time
    8. Start and Stop the WireGuard Interface Manually
    9. Check on the Status of the Server
  5. Managing Users
    1. Adding a First User
    2. Installing WireGuard on an Android Tablet
    3. Adding a Second User
    4. Installing WireGuard on an Android Telephone
  6. Using the WireGuard VPN
  7. Concluding Remarks

Prerequisites to Installing WireGuard on a Raspberry Pi 2 v1.2 or above toc

I will describe how to install the WireGuard virtual network server on a Raspberry Pi 3 model B running Raspberry Pi OS Lite or a recent release of Raspbian Buster Lite. There is no significant change between all these versions with regard to the installation of WireGuard as far as I can ascertain.

Raspberry Pi OS (32-bit) Lite
  Minimal image based on Debian Buster

  Version:        May 2020
  Release date:   2020-05-27
  Kernel version: 4.19
Raspbian Buster Lite
  Minimal image based on Debian Buster

  Version:        February 2020
  Release date:   2020-02-13
  Kernel version: 4.19

  Version:        June 2019
  Release date:   2019-06-20
  Kernel version: 4.19

The latest "official supported operating system" released by the Raspberry Pi Foundation can always be found on the Raspberry Pi OS (previously called Raspbian) downloads page. There are three versions of the OS, the Lite version is typically chosen if the Raspberry Pi is used as a server, while one of the "desktop" versions with a graphical user interface (GUI) should be obtained if the Pi is to be used as a full-fledged desktop computer with connected keyboard and monitor. Even if a GUI version is installed, it will be necessary to open a terminal to install WireGuard.

WireGuard may already be installed in future versions of Raspberry Pi OS. As promised, WireGuard is now included in the Linux kernel. Indeed, wg was included in Armbian Bionic for the Orange Pi PC 2. Try the following commands.

pi@raspberrypi:~$ which wg pi@raspberrypi:~$ which wg-quick

If the two programs are found (probably in /usr/bin/), WireGuard is installed so skip the rest of this section and section 3 and move on to Accessing the Local Network and the sections thereafter on configuring WireGuard. Otherwise the following steps must be performed beginning with a system update and the installation of the Linux kernel headers.

pi@raspberrypi:~ $ sudo apt update && sudo apt upgrade -y ... pi@raspberrypi:~ $ sudo apt-get install raspberrypi-kernel-headers Reading package lists... Done ... The following NEW packages will be installed: raspberrypi-kernel-headers 0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded. Need to get 24.9 MB of archives. After this operation, 163 MB of additional disk space will be used. ... Setting up raspberrypi-kernel-headers (1.20200601-1) ...

This process is lengthy. Note that the version of the headers will depend on the version of the operating system:

Raspbian Buster June 20191.20190517-1
Raspbian Buster Feb. 20201.20200212-1
Rasberry Pi OS May 20201.20200601-1

That is it for the prerequisites.

Installing WireGuard toc

WireGuard is not available in the Raspberry Pi OS repositories.

pi@raspberrypi:~ $ sudo apt install wireguard Reading package lists... Done Building dependency tree Reading state information... Done E: Unable to locate package wireguard

Luckily, it can be installed using the following steps based on instructions from Adrian Mihalko and Ryan Govostes (Feb 10, 2020).

pi@raspberrypi:~ $ echo "deb http://deb.debian.org/debian/ unstable main" | sudo tee --append /etc/apt/sources.list.d/unstable.list deb http://deb.debian.org/debian/ unstable main pi@raspberrypi:~ $ wget -O - https://ftp-master.debian.org/keys/archive-key-$(lsb_release -sr).asc | sudo apt-key add - --2020-04-18 19:56:18-- https://ftp-master.debian.org/keys/archive-key-10.asc Resolving ftp-master.debian.org (ftp-master.debian.org)... ... OK pi@raspberrypi:~ $ sudo apt update Hit:1 http://archive.raspberrypi.org/debian buster InRelease Hit:2 http://raspbian.raspberrypi.org/raspbian buster InRelease Get:3 http://deb.debian.org/debian unstable InRelease [146 kB] Get:4 http://deb.debian.org/debian unstable/main armhf Packages [7,994 kB] Get:5 http://deb.debian.org/debian unstable/main Translation-en [6,222 kB] Fetched 14.4 MB in 19s (746 kB/s) Reading package lists... Done Building dependency tree Reading state information... Done 373 packages can be upgraded. Run 'apt list --upgradable' to see them.

Clearly, we do not want to update all 373 packages of the current stable distribution with versions from the unstable repository. This is easily fixed by lowering the priority of the packages in the unstable depot.

pi@raspberrypi:~ $ printf 'Package: *\nPin: release a=unstable\nPin-Priority: 150\n' | sudo tee --append /etc/apt/preferences.d/limit-unstable Package: * Pin: release a=unstable Pin-Priority: 150 pi@raspberrypi:~ $ sudo apt update Get:1 http://deb.debian.org/debian unstable InRelease [146 kB] ... All packages are up to date. pi@raspberrypi:~ $ sudo apt-cache policy wireguard wireguard: Installed: (none) Candidate: 1.0.20200513-1 Version table: 1.0.20200513-1 500 500 http://deb.debian.org/debian unstable/main armhf Packages

Now the Raspberry Pi OS packages just installed will not be overriden by packages in the unstable repository, but the WireGuard package in the unstable repository will be installed because there is no WireGuard package with a higher priority in the stable repository.

pi@raspberrypi:~ $ sudo apt install wireguard -y Reading package lists... Done ... DKMS: install completed. Module build for kernel 4.19.118-v8+ was skipped since the kernel headers for this kernel does not seem to be installed. Setting up wireguard-tools (1.0.20200513-1) ... wg-quick.target is a disabled or a static unit, not starting it. Setting up wireguard (1.0.20200513-1) ... Processing triggers for man-db (2.8.5-2) ... Processing triggers for systemd (241-7~deb10u4+rpi1) ... Processing triggers for initramfs-tools (0.133+deb10u1) ...

It is now possible to verify that the Wireguard utilities have been installed.

pi@raspberrypi:~ $ which wg /usr/bin/wg pi@raspberrypi:~ $ which wg-quick /usr/bin/wg-quick

To further test the installation, let's create an empty interface configuration file and bring up the interface.

pi@raspberrypi:~ $ sudo touch /etc/wireguard/wg0.conf pi@raspberrypi:~ $ sudo wg-quick up wg0 [#] ip link add wg0 type wireguard [#] wg setconf wg0 /dev/fd/63 [#] ip link set mtu 1420 up dev wg0

Verification shows that the WireGuard module was loaded and the network interface is created and that the server is up and waiting for incoming UDP packets on port 40213.

pi@raspberrypi:~ $ lsmod | grep wire wireguard 143360 0 ip6_udp_tunnel 16384 1 wireguard udp_tunnel 16384 1 wireguard ipv6 454656 29 wireguard pi@raspberrypi:~ $ ifconfig wg0 wg0: flags=209<UP,POINTOPOINT,RUNNING,NOARP> mtu 1420 unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 1000 (UNSPEC) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 0 bytes 0 (0.0 B) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 pi@raspberrypi:~ $ sudo wg interface: wg0 listening port: 40213

However, it is rather pointless to bring up the interface because it will not do anything without proper configuration. Taking the interface down and stopping the server is just as easy, but note how the WireGuard module remains loaded.

pi@raspberrypi:~ $ sudo wg-quick down wg0 [#] ip link delete dev wg0 pi@raspberrypi:~ $ sudo wg pi@raspberrypi:~ $ ifconfig wg0 wg0: error fetching interface information: Device not found pi@raspberrypi:~ $ lsmod | grep wire wireguard 143360 0 ip6_udp_tunnel 16384 1 wireguard udp_tunnel 16384 1 wireguard ipv6 454656 27 wireguard pi@raspberrypi:~ $ sudo modprobe -r wireguard pi@raspberrypi:~ $ lsmod | grep wire pi@raspberrypi:~ $

Enabling Remote Access to the Local Network toc

Hopefully, the home local area network is not easily accessed from outside the LAN because that would mean that it is vulnerable to attacks from any bored script kiddie out there in the nasty world. However, easy yet secure access to the local network from anywhere on the Internet is possible when hosting WireGuard or another virtual private network server on the home network. The server is not the only element that needs to be in place for remote access. This section examines other prerequisites.

If you already have access to an IP camera, a home automation system or a self hosted cloud or NAS then you are probably quite familiar with dynamic host names and port forwarding so that you can skim through the next two steps, but do read carefully about 3.3 Enabling IP forwarding.

Public IP Address and Dynanic DNS Host Name toc

All the computers and devices connected to your home network (often called LAN for Local Area Network) are assigned an IP address by the DHCP server which is probably the router provided by your Internet Service Provider (ISP). On my system the router has for an IP address and the Raspberry Pi hosting the VPN server has a fixed IP address: If you think about it, there are many thousands of devices spread around the globe with that particular address. There is no hope that my Raspberry Pi can be reached from outside the LAN using as the destination address. That problem has been solved with clever routing algorithms. All my devices connected to the local network send their traffic to the router at when receiving or sending data to sites on the Internet. The router then passes each packet on to the ISP, changing the source IP address from say to a public IP address assigned to my network by the ISP. That assigned public IP is unique on the whole of the Internet so that sites that receive packets from devices on my LAN, can reliably reply using as the destination IP the public IP address assigned by my ISP. When the router receives these packets of data, it routes them to the appropriate device on the LAN. There's obviously a little bit of magic going on to keep track of which device gets which packets as they come in, but that's another story. The point is that to talk to my Raspberry Pi from outside the LAN, the public IP address assigned by the ISP must be known. That's not difficult to find. Usually the router with the outside connection to the Internet shows that information.

Otherwise, there are plenty of sites on the Web that will display your public IP address.

If only it were that simple. Unfortunately, the public IP address cannot be trusted because it is dynamically assigned by the ISP and may change from time to time. Any DHCP server can force a client to reconnect at any time and change the assigned IP at that point. Also, when one logs off a network, the DHCP server will reserve the assigned IP for a certain "lease" time should the client connect again. After the lease time is expired, the IP address is returned to the pool of available addresses that the DHCP server can assign to any new client. I have no idea just how long lease time is but it is not very short. Furthermore, devices like smart speakers and phones seem to be calling the mothership often enough to restart the lease so that I sometimes have the same public IP address for days on end. For the duration of this post, let's say that my sticky dynamic public IP address is Indeed, I could get away with using as the public IP address of my network for testing the WireGuard configuration later on.

In practice though, one should avoid using a dynamic IP address. Instead the local network should be reached through a dynamic host name. DNS is the domain name system which translates the name of a website such as www.google.com into an IP address ( Some sites offer a service, often free, that associates a domain name with an IP address. These sites update the IP addresses in their database at regular intervals. So, we will put in the HTML request the domain name obtained from the DNS service. The DNS will translate this name into an IP address that will be updated each time the ISP assigns a different IP address to the home server. There are plenty of sites on the Web that describe how to set up a dynamic domain name with any one of a number of DDNS providers and among them there is a description of how I did it using freedns.afraid.org back in 2018. So get yourself a dynamic host name, and learn how to signal any change in the public IP address assigned to your network to the DDNS service.

In what follows, my dynamic host name is deemed to be modomo.twilightparadox.com, which I hope is a fictitious name.

Port Forwarding toc

Part of the magic behind the routing of data packets across the router is that each packet must be sent through a "port". Ports are not physical entities, they are more like an apartment number added to a street address to ensure that a letter gets to the proper mail box. In the jargon, they are "end points" of a communication link and must be tacked on at the end of an IP address or host name. As an example, FTP control packets sent from the desktop computer to the Raspberry Pi, have as a destination address Some port numbers are implicit. All HTTP traffic is usually sent to port 80, while HTTPS traffic is sent to port 443. Try https://www.google.com:9090 in a browser. This will send the request to port 9090, which is specified after the colon. The search engine does not "listen" to that port, so nothing will be displayed unless you are very patient and then some sort of error message may appear. Try https://www.google.com:443 and you will see the familiar search page very quickly, but you don't have to write the port number, it is implicit the HTTPS protocol.

For security reasons, consumer class routers such as the one supplied by an ISP have a built-in firewall that controls incoming and outgoing network traffic. Typically, outgoing traffic can only be sent out if the end point (i.e. port) is for some "well known" use. Typically, incoming traffic is blocked outright unless it is part of an exchange initiated by a device on the LAN. That is why you can use a Web browser from your home computer to read this post! There's an obvious problem for us. How can the Raspberry Pi be reached if the firewall will not let through IP packets destined to the Pi. So a "hole" has to be punched through the firewall. In technical terms, a port forwarding rule has to be established. That rule will instruct the firewall to send any IP packet addressed to the correct port to be sent on to a Pi.

OpenVPN which is a very popular VPN package uses a default destination port, 1194 to be precise, although that can be changed. Furthermore, whichever port OpenVPN uses, it will identify itself when queried with a port scanner. WireGuard does not have a default port nor will it reply if the port it does use is probed. That means that when configuring WireGuard later on, you will have to choose a port number. The latter are 16 bit integers, which means they have a range from 0 to 65435. However, choosing a number between 0 and 1023 is generally a bad idea. Typically, tutorials on the installation of WireGuard use relatively big numbers such as 53133 which are in the dynamic, private or ephemeral range.

It is difficult to give instructions about implementing port forwarding because each router model is different. On mine, there is a Port Forwarding tab in the Basic menu, and a Add Rule button which displays the window shown below when clicked.

As can be seen the router wants to forward a range of ports, so I specified a range of one port. I used the same port number for the public (Internet facing) port and for the private (local network) port. The latter will be appended to the local IP address, In my case, all IP traffic sent to modomo.twilightparadox.com:53133 will end up at the outward facing edge of my router as traffic sent to The router will then pass it over to the local network as traffic destined to being sent to If everything is set up correctly, WireGuard will know what to do with it.

If you are having trouble setting up the port forwarding rules on your router, there are sites such as PF Network Utilities that have information about many router models. They also offer utilities that perform various including port forwarding, which I cannot endorse because I am much too paranoid to install such software and much too cheap to pay for it in the first place. I must say that the site provided accurate information about my router, but it was hidden behind a lot of advertising for their products.

Enabling IP Forwarding toc

If access to other LAN resources such as an IP camera or a Web server is needed, then IP forwarding has to be enabled on the computer hosting the WireGuard server. If you do not enable IP forwarding, you will not be taking full advantage of the virtual private network. I repeat, skipping IP forwarding only makes sense if the only device that needs to be reached from outside with the VPN is the WireGuard host machine.

pi@raspberrypi:~ $ cd /etc pi@raspberrypi:/etc $ ls -l sysctl* -rw-r--r-- 1 root root 2683 Apr 8 06:56 sysctl.conf sysctl.d: total 8 -rw-r--r-- 1 root root 51 Nov 26 2018 98-rpi.conf lrwxrwxrwx 1 root root 14 Apr 8 07:51 99-sysctl.conf -> ../sysctl.conf -rw-r--r-- 1 root root 639 May 17 2018 README.sysctl

Note how /etc/sysctl.d/99-sysctl.conf is a symbolic link to /etc/sysctl.conf. It will suffice to edit the later to enable IP packet forwarding.

pi@raspberrypi:/etc $ sudo nano sysctl.conf


... # Uncomment the next line to enable packet forwarding for IPv4 #net.ipv4.ip_forward=1 ...


# Uncomment the next line to enable packet forwarding for IPv4 net.ipv4.ip_forward=1

as instructed in the configuration.file. A reboot will be necessary for the change to take effect.

pi@raspberrypi:~ $ sudo reboot Connection to raspberrypi.local closed by remote host. Connection to raspberrypi.local closed. ... michel@hp:~$ ssh pi@raspberrypi.local ... ... pi@raspberrypi:~ $ sysctl net.ipv4.ip_forward net.ipv4.ip_forward = 1

Configuring WireGuard toc

Configuring WireGuard is probably the most complicated part of setting up the VPN. There are many tutorials on how to proceed, starting with the WireGuard Quick Start guide. Frankly, I could not make much of it, because I really did not and still do not know enough to configure network interfaces, ip routing and so on from the command line. I did find other resources on the Web that helped me gain some knowledge, but in the end I have found that Adrian Mihalko, who provided the instructions for installing WireGuard on the Raspberry Pi, also created a user management script that perfectly suited my needs and level of understanding. I wanted a VPN server on the home network and VPN clients on Android devices (could be iOS) and this is precisely what the script facilitates.

The instructions below are very detailed, perhaps too much so. I would suggest that you read User management with Wireguard User Management Script written by Adian Milhalko and return here for more information if needed.

It may be useful to raise a little point here. WireGuard operates a peer-to-peer network. However it is sometimes useful to look at it as a client-server model, such as here where the WireGuard peer running on the Raspberry Pi is viewed as a server and the WireGuard peers on various Android devices are viewed as clients. In the end, peer, server, client and user are all the same thing. Hopefully that will not be a source of confusion.

Install the Adrian Mihalko User Management Script toc

There is one prerequisite to install that will be used to generate QR-code images that will make it very easy to configure a WireGuard client on an Android or iOS device.

pi@raspberrypi:~ $ $ sudo apt install qrencode -y Reading package lists... Done Building dependency tree Reading state information... Done The following additional packages will be installed: libqrencode4 The following NEW packages will be installed: libqrencode4 qrencode 0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded. Need to get 71.8 kB of archives. ... Setting up libqrencode4:armhf (4.0.2-1) ... Setting up qrencode (4.0.2-1) ...

Next we download an archive containing the script and supporting files and extract its content to a directory that will be called ~/wp_config.

pi@raspberrypi:~ $ wget https://github.com/adrianmihalko/wg_config/archive/master.zip --2020-07-02 19:00:52-- https://github.com/adrianmihalko/wg_config/archive/master.zip Resolving github.com (github.com)... ... 2020-07-02 19:00:53 (2.60 MB/s) - ‘master.zip’ saved [3688/3688] pi@raspberrypi:~ $ unzip -j master.zip -d wg_config Archive: master.zip 3e27f97b45b201603d9522d41959b356e787260b inflating: wg_config/README.md inflating: wg_config/client.conf.tpl inflating: wg_config/server.conf.tpl inflating: wg_config/user.sh inflating: wg_config/wg.def.sample pi@raspberrypi:~ $ mkdir downloads pi@raspberrypi:~ $ mv master.zip downloads/wg_config_script.zip

I chose to create a ~/downloads directory and to moving the script archive in it with a more meaningful name, but it would have been fine to just delete the archive.

Instead of playing around with a zip archive, I could have followed the recommended installation process by installing git along with qrencode and then cloning the repository containing the script.

pi@raspberrypi:~ $ sudo apt install git qrencode -y ... pi@raspberrypi:~ $ git clone https://github.com/adrianmihalko/wg_config.git Cloning into 'wg_config'...

The following list of steps might look daunting; it is actually rather easy to configure the WireGuard server and to add clients or peers with the script.

Generate the Private and Public Server Keys toc

WireGuard encrypts the data exchanged over the virtual network. Much like SSH, asymmetric encryption is used to set up the secure session. Both server and client (or peers actually) have private and public keys, but only the latter are exchanged for authentication. Again, like SSH, the public keys are shared "out-of-band". In this case that means that the keys must be manually copied to each peer configuration file. There is no third party "certificate autority" for SSL certificates as in the HTTPS or OpenVPN protocols.

The first step, which is done only once, is to generate the private and public keys of the WireGuard server on the Raspberry Pi.

pi@raspberrypi:~ $ cd wg_config pi@raspberrypi:~/wg_config $ wg genkey | tee server_private.key | wg pubkey > server_public.key

If the command seems a bit opaque to you as it did to me, here is what it actually translates to:

pi@raspberrypi:~ $ wg genkey > server_private.key pi@raspberrypi:~ $ wg pubkey > server_public.key < server_private.key

These two keys are needed in the next steps. Copy them into a text editor on the desktop or open a second SSH session on the Raspberry Pi for easy access to the keys later.

pi@raspberrypi:~/wg_config $ cat server_public.key 5lFoBBjeLcJWC9xqS/Kj9HVwd0tRUBX/EQWW2ZglbDs= pi@raspberrypi:~/wg_config $ cat server_private.key aA+iKGr4y/j604LtNT+MQJ76Pvz5Q5E+qQBLW40wXnY=

Normally, one never makes the private key public. So the keys shown above are only for demonstration purposes, and you must replace those values with the one actually generated.

Create and Edit the Server Definition File toc

This step, performed once only, creates the wg.def file which contains data the script will use to make the server configuration and client configuration files.

pi@raspberrypi:~/wg_config $ cp wg.def.sample wg.def pi@raspberrypi:~/wg_config $ nano wg.def

This is the template.


And this is how I changed it.


If subnet 192.168.99.xxx is used on the local area network, then the value of _VPN_NET will need to be changed. The _SERVER_PORT is the UDP port that will have to be forwarded to the WireGuard sever by the LAN router or gateway. This can be (perhaps should be) changed. And, of course, it is necessary to change wg.example.com in _SERVER_LISTEN to the host name of the Raspberry Pi which, in my (fictitious) case is: modomo.twilightparadox.com as explained in 3.1 Public IP Address and Dynamic Host Name. Do not put the protocol prefix such as https://, just the domain name. As I explained above, the public IP address assigned to me by my ISP changes so rarely that I could get away with the public IP address instead of a host name for testing purposes.

If the CIDR notation is not familiar, just think of the trailing integer after the slash as the number of fixed most significant 1 bits in the subnet mask. Well, that's really clear. Perhaps working out the example will help. The subnet mask is 32 bits (or 4 bytes) of which the most significant 24 are 1s and the least significant 8 bits are 0. Hence the mask is So the virtual network peers will have IP addresses in the 192.168.99.xxx block. The server will be at, the first client at, the second at and so on. The script will handle this sequential allocation of IP addresses automatically.

Edit the Client Configuration Template toc

The client configuration template, client.conf.tpl, used by the script to create each user (or client) configuration file is quite short.

[Interface] Address = $_VPN_IP PrivateKey = $_PRIVATE_KEY [Peer] PublicKey = $_SERVER_PUBLIC_KEY AllowedIPs = Endpoint = $_SERVER_LISTEN

I changed the AllowdIPs.

pi@raspberrypi:~/wg_config $ nano client.conf.tpl

[Interface] Address = $_VPN_IP PrivateKey = $_PRIVATE_KEY [Peer] PublicKey = $_SERVER_PUBLIC_KEY AllowedIPs =, Endpoint = $_SERVER_LISTEN

Note that the first AllowedIPs ( is the address of the Wireguard server on the virtual network and the 32-bit mask means that the client/user will not be able to reach any other IP address on the 192.168.99.xx subnet. The second allowed IP address, which is the 192.168.1.xxx block of IP addresses corresponding to my home local network. The Raspberry Pi has a static IP address on that network:, the ISP supplied cable modem/router is at and its integrated DHCP server allocates IP addresses in the range where most of my IoT devices can be found. You will, of course, have to adjust the AllowedIPs to refer to the correct IP addresses in your particular situation. It could be that your LAN is on subnet 192.168.1.xxx as suggest above, or 192.168.1.xxx, but some LANs use other private IPv4 addresses such as 10.0.3.xxx.

Edit the Server Configuration Template toc

In the server configuration template, it is assumed that the name of the network interface used by the host of the WireGuard server is eth0. In my case, that is not correct because the Raspberry Pi is connected by Wi-Fi to the LAN, so it was necessary to edit the sever configuration template.

pi@raspberrypi:~/wg_config $ nano server.conf.tpl
[Interface] Address = $_SERVER_IP ListenPort = $_SERVER_PORT PrivateKey = $_SERVER_PRIVATE_KEY PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o wlan0 -j MASQUERADE

The eth0 interface is replaced by wlan0 in the PostUp and PostDown lines that define changes to the server iptables when activating or deactivating the VPN.

Again, this change needs to be done only once if at all.

Create an Empty WireGuard Server Configuration File toc

If the (empty) configuration file, wg0.conf, was not created when testing the installation of WireGuard in section 2. Installing WireGuard, now is the time to do it.

pi@raspberrypi:~/wg_config $ cd .. pi@raspberrypi:~ $ sudo touch /etc/wireguard/wg0.conf

This is done once only. The user management script will update this file each time it is used to add or delete a user. Consequently, the file should not be edited manually.

If you are familiar with managing services in systemd you can jump to 5. Managing Users, otherwise the next three subsections present the rudiments of systemctl commands to start and stop the WireGuard service.

Enable Automatic Start of the wg0 Interface at Boot Time toc

pi@raspberrypi:~ $ sudo systemctl enable wg-quick@wg0 Created symlink /etc/systemd/system/multi-user.target.wants/wg-quick@wg0.service → /lib/systemd/system/wg-quick@.service.

This too is only done once, normally. If for some reason WireGuard should not be installed at boot time, use the disable command.

pi@raspberrypi:~ $ sudo systemctl disable wg-quick@wg0 Removed /etc/systemd/system/multi-user.target.wants/wg-quick@wg0.service.

It will be possible to enable the service again later.

Start and Stop the WireGuard Interface Manually toc

Usually WireGuard will be started automatically as explained above, but sometimes especially when testing the installation of the server, it is useful to manually start and stop it.

pi@raspberrypi:~ $ sudo wg-quick up wg0 [#] ip link add wg0 type WireGuard [#] wg setconf wg0 /dev/fd/63 [#] ip link set mtu 1420 up dev wg0 pi@raspberrypi:~ $ sudo wg-quick down wg0 [#] ip link delete dev wg0

Note that the output will be more voluminous when the server configuration file is finally created as shown later.

Check on the Status of the Server toc

Again when testing, it may be of value to check on the status of the VPN server. This can be done with the systemd control utility and with the WireGuard utility.

pi@raspberrypi:~ $ sudo systemctl status wg-quick@wg0 ● wg-quick@wg0.service - WireGuard via wg-quick(8) for wg0 Loaded: loaded (/lib/systemd/system/wg-quick@.service; enabled; vendor preset: enabled) Active: active (exited) since Fri 2019-03-05 16:50:49 ADT; 20min ago Docs: man:wg-quick(8) man:wg(8) https://www.wireguard.com/ https://www.wireguard.com/quickstart/ https://git.zx2c4.com/WireGuard/about/src/tools/man/wg-quick.8 https://git.zx2c4.com/WireGuard/about/src/tools/man/wg.8 Process: 378 ExecStart=/usr/bin/wg-quick up wg0 (code=exited, status=0/SUCCESS) Main PID: 378 (code=exited, status=0/SUCCESS) Jul 03 16:48:58 domo systemd[1]: Starting WireGuard via wg-quick(8) for wg0... Jul 03 16:50:46 domo wg-quick[378]: [#] ip link add wg0 type wireguard Jul 03 16:50:46 domo wg-quick[378]: [#] wg setconf wg0 /dev/fd/63 Jul 03 16:50:47 domo wg-quick[378]: [#] ip address add dev wg0 Jul 03 16:50:47 domo wg-quick[378]: [#] ip link set mtu 1420 up dev wg0 Jul 03 16:50:49 domo systemd[1]: Started WireGuard via wg-quick(8) for wg0. pi@raspberrypi:~ $ sudo wg interface: wg0 listening port: 54130

The above is only an indication of the information that may be displayed. It will depend on the number of peers/clients that have been set up. And even more information will be displayed when a client or peer has created a tunnel (i.e. opened a VPN).

Managing Users toc

Adrian Mihalko discusses setting up a mobile client on IOS. I will show how to set up clients on a couple of Android devices: an old Nexus 7, that I often bring along when I am outside the house and a more recent Zenfone which is a so-called smartphone by Asus.

Adding a First User/Client toc

Here is the procedure to modify the configuration of the WireGuard server on the Raspberry Pi to accept a request from a client (user or peer) that wants to open a VPN connection, often called a "tunnel". At the same time, the client configuration file will be created. This is done by running the user.sh script with the -a option followed by a unique name identifying the user/client to create. In addition to modifying the

pi@raspberrypi:~ $ sudo wg-quick up wg0 [#] ip link add wg0 type wireguard [#] wg setconf wg0 /dev/fd/63 [#] ip link set mtu 1420 up dev wg0 pi@raspberrypi:~ $ cd wg_config pi@raspberrypi:~/wg_config $ sudo ./user.sh -a nexus7
████ ▄▄▄▄▄ █ ▄▄▄█  ▄ ██▀  █▄█▀▀▄▄██▄ █▀█  ▄ █▄ ▀██ ▄▄▄▄▄ ████
████ █   █ █  ▀▄▀█ ██ █▄▀▀▀▄▀▄  ▄▀▄ █ █▄▀▄▄█▀ ▀▄▀█ █   █ ████
████ █▄▄▄█ █▀▄▀█ ▀▀▀▄▄▀▄▀▄ ▀ ▄▄▄ ▄▄  ▄▄▀█▀▀█▀▀ ███ █▄▄▄█ ████
████▄▄▄▄▄▄▄█▄▀ █▄▀▄█ ▀▄▀ ▀▄▀ █▄█ █ █ █ █▄█ ▀ █▄█▄█▄▄▄▄▄▄▄████
████ ▄█▀ ▄▄▀▀▀   ▄ ▄█▀ ▄▀▄ ▀ ▄  ▄█▄  ▄▀██  ▀█▄ █ ▀█▄▀  ▄ ████
█████▀▀▄█ ▄ ▄ █▀▀▄▄▀  ▀██▄▄▄▀▄█▀█ ▄▄▀ █▀   ▀▄▀ ▀▄ ██▀▄█▀ ████
████▄▄▄▀▀▀▄▀█▀▄██▄▀█ ▀█ ▀█▀█ ▀█▀█▀ ▀ █▀▄█▄█▄██ ▀██▀█ █▀▄▀████
████▄▄▀▄▀ ▄█▀▄ █▄▄ ▀ ▀█ █  ▀ ▀█▄ ██▄ ▀ █▄▄▀█▄▀▄▄ ▄▀▀  █▄ ████
█████▄▄▄▄█▄ ▄▀▄▄▄▀▀▀▀█ █▀ ▄▄ ▄▄▀▄██ ██ █ ▀ █▀▀█ ▄▀█  █▀▀▀████
████▀▀▀   ▄▄  ▄▄███▄█ ▀█▄▄ █ ▄█▀██ ▀▄▀ ▄█▄ ▄ ▀ ██▀█▄▀▄█▀▄████
█████▀▄▄█ ▄▄▀██▀█▄ ▄ ▀█  █ ██▀▀▄ ▀▀▀ █ ▄▀▄▄▀██▀▀▀▄▀█▀▀▀▄ ████
████▀▀▀██ ▄█▄█ █▄▀█▄ ▀  █ ▀ ▄▀█▄ ▄▄▀▀▄▀▄▄▀████▀▄ ▀ ▀▄▀▄▄█████
██████▄▄ ▄▄▄  █▄▀▄ ▄█▄▀▄▀▄▄▀ ▄▄▄ ▄▄  ▄▀█▀ ▄▄ ▄▄▄ ▄▄▄  ▀▄▀████
█████▀█▀ █▄█ ▄███▄ ▀ ▄▄█ ▀▄  █▄█ ▀█▄▀ ▀█▄ ▄ ▄ ██ █▄█ █ █ ████
█████▄▀  ▄▄ ▄█▀▄█▄▀▄▄█▄ ▄▀▄█▄▄▄▄▄█ ▀ ▀▀▄▀ ▄█▄█ ▀▄▄   ▄▀▀ ████
████▄ ▄▀▄▄▄▀█▀▀██▄ ▄▀▄█▀  █▀▀█ █▄▀▀▄ ▄█▄ █▀▀▀▄  █▀▄  ▀▄▄▄████
████▀██ ▄▄▄▄█▄▀██▀▀▀▀▀ ▄▀ █ ▀ ▄███▀▄▄████▀▀ ██▄ ▄ ▀▄▄▄▀██████
████▄█▀ ▀▀▄█▀█▀▄▀██▄█▀ ▀█▄▀▀▀█  ███▀▄█ ▄█▄  ▄▄▀▀▄ █▀██▄█▄████
█████▀ █▄ ▄ ██▀██▄ ▄▄█▀▀▄▀▀▄█▀  ██▄▀ ▀▄▄█▄▄▀▀▀▀   ██ ▀▀█ ████
████ ▄▄▀█▀▄ █▀▄  ▀█▀ ▀ █▀▀▀▄▀ █▄█ ▀▀█ ▄▄▀█▄ █  ▀██▄█▀ █ ▄████
████▄▀▄▄▀█▄▀▄▄▄▀ ▄ ▄█▄▀█▀▄▄  ▄▄█▀█▄▄▄█▄▀▀ ▄█▀ ▀▀▄ ███ ▀ █████
████▄ ▀▄▄▄▄ ▀ ██ █▄▀ ███ ▄█▀ ██▀█  ▄▀ ▀▀█ ▄███  █▄█▀ ▄▀█ ████
███████▄██▄█▀ ▀▄▄  ▄ █▄ ██▀█ ▄▄▄ ▀█▀ ▀██▀▄▀▀▀██▄ ▄▄▄  ▀ ▀████
████ ▄▄▄▄▄ █▀▀█  ▀  █▄██▀▀▄▄ █▄█ ▀██▀▀█▀▀▄ ██▄▄▄ █▄█  ▄ █████
████ █   █ █▄  ██▄ ▀▀▀ █▄ ▄▄▄▄▄ ▄██ ▀▄███ ▀ ▀██▄ ▄▄  ▄▀▀▄████
████ █▄▄▄█ █▀█▀ ▀▀▀▄█ ▄▀ ▀▄█▄▀▄ ▀█ ▄█▀█▄█▄   ▀▄▀██▀██▄█  ████

The following message

Unable to modify interface: No such device wg set failed

will be printed just below the QR codes if the WireGuard service was not running on the Pi. In that case, rerun the two commands shown above. Don't worry about the QR code, it can be displayed later when needed to configure the WireGuard client on the Android or iOS device. The script executes very quickly but it nevertheless does quite a bit of work.

Here is the content of the user directory just created.

pi@raspberrypi:~/wg_config $ ls -l users/nexus7 total 24 -rw-r--r-- 1 root root 216 Jul 5 17:49 client.all.conf -rw-r--r-- 1 root root 216 Jul 5 17:49 client.conf -rw-r--r-- 1 root root 913 Jul 5 17:49 nexus7.all.png -rw-r--r-- 1 root root 913 Jul 5 17:49 nexus7.png -rw-r--r-- 1 root root 45 Jul 5 17:49 privatekey -rw-r--r-- 1 root root 45 Jul 5 17:49 publickey

There are two client configuration files, client.conf and client.all.conf and two QR code images that correspond to these configurations. One or both of these will be used to configure the Android or iOS client later on. Finally, as with the WireGuard server, the client has a private and public encryption key.

Here is the content of one of the client configuration files and the server configuration file.

pi@raspberrypi:~/wg_config $ cat users/nexus7/client.conf [Interface] Address = PrivateKey = gH5xInhP2NZw0t8hVgJPhTRDUh3Bir7FEynRcW8IHlg= [Peer] PublicKey = 5lFoBBjeLcJWC9xqS/Kj9HVwd0tRUBX/EQWW2ZglbDs= AllowedIPs =, Endpoint = modomo.twilightparadox.com:53133 pi@raspberrypi:~/wg_config $ sudo cat /etc/wireguard/wg0.conf [Interface] Address = ListenPort = 53133 PrivateKey = aA+iKGr4y/j604LtNT+MQJ76Pvz5Q5E+qQBLW40wXnY= PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o wlan0 -j MASQUERADE [Peer] PublicKey = BEnqBZ6rWcDO6lKhb6oXM7aRvE7fuIWCZw1PxgyMMyE= AllowedIPs =

There are two sections to a WireGuard configuration file. The first, [Interface] defines the IP address of the client or server on the virtual network. As already mentioned, the script will assign the first valid IP address on the virtual network, to the Raspberry Pi hosting the server. So the script assigned the next valid address,, to the Nexus 7 client. The script also generated public and private keys for the client and server and includes the private key of each in its interface definition. The second part of the configuration file lists all the peers with which a tunnel can be established. To create the virtual connection, the client must know how to reach the server (the Endpoint of its peer) and its public key. Similarly, the server must know its own address, on which UDP port it is listening, and the IP address and public key of any client (peer) that will be allowed to create a tunnel. Perhaps seeing the two configuration files side by side may make these links more obvious.

There is a second user configuration file.

pi@raspberrypi:~/wg_config $ cat users/nexus7/client.all.conf [Interface] Address = PrivateKey = gH5xInhP2NZw0t8hVgJPhTRDUh3Bir7FEynRcW8IHlg= [Peer] PublicKey = 5lFoBBjeLcJWC9xqS/Kj9HVwd0tRUBX/EQWW2ZglbDs= AllowedIPs = Endpoint = wg.example.com:53133

It is identical to the first one except for the AllowdIPs field. I use both configuration files as explained below.

Installing WireGuard on an Android Tablet toc

Now it is time to install the WireGuard Application from Google Play on the Android tablet. Launch the application.

start screen

Click on the blue button as told.

start screen

Click on Create from QR code.

It is now time to display the QR code image on the Raspberry Pi hosting the WireGuard server.

pi@raspberrypi:~/wg_config $ sudo ./user.sh -v nexus7

This time the two configuration files and the corresponding QR codes images will be displayed, but it will be necessary to scroll back to see them. Aim the tablet camera towards the QR code displayed on the desktop monitor. I started with the QR code for the client.conf file (with AllowedIPs =, Once the information was acquired, the following dialog appeared.

start screen

I named the tunnel "Rpi3-split" and then pressed on the CREATE TUNNEL button. I repeated the steps to add the second tunnel, named "RPi-all", from the second QR code.

start screen

The above screen capture done on the older tablet shows the appearance of the Android WireGuard application once the two tunnels were created . When selecting a tunnel, the "public" information is displayed on the right panel.

Adding a Second User toc

Allowing a second device to connect to the local network through the WireGuard VPN on the Raspberry Pi is now very simple. Just use the Mihalko script again.

pi@raspberrypi:~ $ cd wg_config pi@raspberrypi:~/wg_config $ sudo ./user.sh -a zenfone pi@raspberrypi:~/wg_config $ ls users nexus7 zenfone

This is the new /etc/wireguard/wg0.conf configuration file created by the script.

[Interface] Address = ListenPort = 53133 PrivateKey = aA+iKGr4y/j604LtNT+MQJ76Pvz5Q5E+qQBLW40wXnY= PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o wlan0 -j MASQUERADE [Peer] PublicKey = BEnqBZ6rWcDO6lKhb6oXM7aRvE7fuIWCZw1PxgyMMyE= AllowedIPs = [Peer] PublicKey = dVq8SvBwcKrFnBBQL2F7JcsVNB4jSf6f3kbtfnsYGCA= AllowedIPs =

The new client shows up as a second Peer in the server configuration file. That is all that need to be done on the server, all the "hard work" needed to create the first client does not have to be repeated.

Installing WireGuard on an Android Telephone toc

The configuration of the WireGuard client on the Android phone was very much a repeat of the previous experience with the Android tablet. Actually the only difference is in the appearance of the application given the smaller screen.

Click on the + button to "Add a tunnel using the blue button" as displayed in the application window, then select the SCAN FROM QR CODE. Display one the QR code images on the Raspberry Pi hosting the WireGuard server. You may need to scroll back to see the first image.

pi@raspberrypi:~/wg_config $ sudo ./user.sh -v zenfone

Once the image is acquired with the phone camera, fill in the Tunnel Name and then click on the CREATE TUNNEL button in the pop-up window. Repeat for the other configuration so that there will be two different ways to connect the WireGuard VPN on the Raspberry Pi.

Using the WireGuard VPN toc

Imagine the following scenario. I am sitting in a coffee shop, and I want to see the video feed from an IP camera at home. On the local network, I would start VLC and view the stream at the following address: rtsp:// On my tablet, I can do exactly the same thing as long as I start the WireGuard application and open one of the tunnels to the VPN server at home.

start screen

I just slide the wanted tunnel button to the right as shown above. On my Android phone the connection details are displayed by clicking on the tunnel name, but opening a tunnel would be done just the same, by sliding to the right the control beside the desired tunnel. As soon as that is done, I have access to all resources on my home network on 192.168.1.xxx just as if my Android device were connected directly to the LAN. I can therefore watch the rtsp:// video stream as if I were home. So simple and yet secure. Anyone eavesdropping on the Wi-Fi network in the shop or anywhere along the route between my tablet and my home router would see IP packets with encrypted content. Instead of seeing the address from which it could be surmised that there is an IP camera on my home network (554 is the typical RTSP port), the visible address will be which is the public IP address of the router and the obscure port used by the WireGuard interface which encodes everything else end-to-end, including the final destination address.

Note that the Android client gives very little feedback. A little key icon signifying the VPN is active will be shown at the top of the device screen. But that icon is present even if the settings are wrong or if the WireGuard server at home is not online. The only "symptom" that something is wrong will be that all devices on the 192.168.1.xxx subnet are unreachable!

What if I decide to consult the latest stock market indices? I would start a web browser and go to the say Yahoo! Finance in Canada: https://ca.finance.yahoo.com/. The destination IP,, is not on the 192.168.1.xxx subnet so routing of the packets would not go through the WireGuard tunnel. Instead, packets will be routed directly as if WireGuard was not even running. Anyone eavesdropping on the Wi-Fi network may be able to follow the data sent and received by the Android device.

If I then want to check my bank balance, I can either start a Web browser and establish a secure HTTPS connection with the bank's Web server or use the Google Play Store app provided by the bank. Either way, I am counting on the built-in encryption of the data exchanged to keep my password and the details of my finances private. However, being paranoid, before checking the balance, I usually start the other tunnel that I named rpi3-all or test-all where the Allowed IPs field is That means all traffic in and out of my device is sent to my home network and from there it is routed to its final destination. So my outgoing financial data is double encrypted on the first leg of its journey out of the coffee shop and incoming data is also double encrypted on the last leg from my home network. Maybe I should wear a tin foil hat to protect myself from the nefarious 5G network at the same time because for most of the way, the data is transiting all sorts of bridges, routers, backbones and so on with no more and no less encryption than when I consult my bank balance from my desktop computer at home. Still I find it reassuring to use the "universal" WireGuard tunnel at all times when using a public hotspot. No harm is done, and there is no perceptible slow down even with the extra hop involved.

Of course, if you use a public hotspot in search of anonymity, don't use the Allowed IPs= configuration because you are in effect using your own ISP account. On the other hand, do not assume that a public hotspot provides true anonymity. The coffee shop server knows which IP was assigned to your computer and the MAC address of the network card of your computer and may very well save that type of information.

Once you have thoroughly tested everything, I suggest it is time to look at all ports that were being forwarded at the LAN firewall. I was able to remove all holes punched through it for the home automation system, for IP cameras and so on and replace them with a single UDP port forwarded to the WireGuard service. Now there's a single small black hole in the firewall. Try it and you too may get a warm fuzzy feeling of security. Hopefully, I will not regret this in the future.

Concluding Remarks toc

I believe these instructions should also work with Raspbian Stretch as long as the utility dirmngr is in place before getting WireGuard. Note that I cannot confirm this. If you run into problems, you could consult an older post on the subject: Installing WireGuard on Raspbian Stretch and Buster.

On older Raspberry Pi models it is necessary to compile WireGuard from the source code. Some may want to compile the WireGuard source code instead of using the package from the unstable Debian repository. I did follow the detailed instructions by Adrian Mihalko (who else?) to install the software correctly on a Raspberry Pi 1 a few months ago. However, I have not verified that the instructions are still valid. There is a fairly recent (June 30, 2020) post by Karspars Dambis WireGuard on Raspberry Pi which says

The official instructions for compiling WireGuard from source files have been updated in early 2020 after the introduction of a separate package wireguard-linux-compat for the WireGuard Linux kernel module which has been decoupled from the user-space tools wireguard-tools.

I would suggest looking at those two posts if attempting to install WireGuard by compiling the source. Nick Sweeting (pirate), author of Some Unofficial WireGuard Documentation is another good source of information.

Not long after installing WireGuard on the older Raspberry Pi, I did the same on an Orange Pi Zero with an older version of Armbian. It was necessary to compile the source code much in the same way as on the older Raspberry Pi, but there was a bit of a struggle to get the prerequisite packages in place. There is no point in pursuing this any further given that the latest versions of Armbian based on the 5.4 Linux kernel include WireGuard. This is not too surprising since Armbian seems to be based on Ubuntu which comes with WireGuard built-in as of version 20.04. Indeed WireGuard was backported to version 18.04. On those systems, configuration is all that needs to be done to set up a local VPN server.

The Raspberry Pi OS can be upgraded to the 5.4 kernel. I did test this but unfortunately, WireGuard was not included at least in the 32-bit "lite" version. This was a beta version and the situation could be different when the move to the 5.4 kernel is officially completed. Time will tell but it does look like WireGuard will be in all Linux distributions in the future: Linus Torvalds pulled WireGuard VPN into the 5.6 kernel source tree.

Some may wonder about the throughput of the VPN. I was surprised that the VPN performed adequately even when routing all Internet traffic through it. This was true when the VPN service was running on a single core Raspberry Pi 1 (similar to a Pi Zero). It is true that my bandwidth demands are usually relatively light when I am in a coffee shop. Nevertheless, YouTube videos could be streamed simultaneously on a tablet and portable without noticeable degradation. Of course these were not in high-definition, but then I do not foresee a pressing need to view 4K videos in coffee shops in the forseeable future.

Including WireGuard in the Linux kernel will hasten its adoption by an ever-growing number of users. In my opinion, that is a good thing as I have found the VPN to be very reliable, useful and surprisingly seamless.

Installing and Configuring WireGuard on Raspberry Pi OS (January 2021)-> <-Installing and Configuring WireGuard on a Raspberry Pi