Building a wireless access point and VPN gateway with a Raspberry Pi

This post describes my experience using a Raspberry Pi as a wireless VPN gateway. In particular, how to solve the terrible performance I had initially.

I decided to use a Raspberry Pi (because I had one spare) running ExpressVPN, and connected to the router/modem via wired Ethernet.

My initial attempt, a few years ago, was with a Raspberry Pi 3B running the “Buster” version of Raspberry Pi OS. There were a couple of problems with this.

  1. It was very complicated to set up the network configuration required. See, for example, Using Raspberry Pi as a Wired Router or Turn your RaspberryPi into a WiFi Router!.
  2. The bandwidth available was limited, making the system unusable: I was getting a download speed of 0.3 Mbps (with a latency of over 2 seconds!). For comparison, my broadband connection provides around 30 Mbps.

With the availability of the Raspberry Pi 4, I thought I would try again. I expected better results because of the faster processor and faster Ethernet interface (as we will see, the processor is the key factor).

Setting up the Raspberry Pi

I am going to assume you are reasonably familiar with setting up and using a Raspberry Pi. If not, there are many good resources to help with getting started.

I also assume you are comfortable using the Linux command line either with a monitor and keyboard connected to the Raspberry Pi, or connecting via SSH.

Installing the OS

The Raspberry Pi Imager provides an easy way to install the Raspberry Pi OS onto an SD card. This allows you to:

  • Choose the OS to install [1]
  • Create your user account
  • Configure the Wi-Fi network to use [2]
  • Enable login with SSH as part of the process.

You set all these in the Use OS customisation dialog: click Edit Settings to configure these options and then Yes to apply them to the installation.

Notes:

  1. Install a Bookworm (or later) version of the OS. We can use the 64-bit Lite version of the OS for this project (without the desktop GUI, etc) because the Raspberry Pi will be running “headless” (without a display).

    Some sources say that ExpressVPN requires the 32-bit version of the OS to be installed. That is not (or no longer) the case. I have successfully tested it with both the 32-bit and 64-bit OS. There are some extra dependencies to be installed for the 64-bit OS, described below.

  2. We do not want to set up Wi-Fi here, as we will be configuring it later as an access point. So leave that information blank.

Updating the software

After booting the Raspberry Pi for the first time, it is always a good idea to make sure all the software is up to date:

$ sudo apt update
$ sudo apt full-upgrade -y

Creating the Wi-Fi access point

Set the Wi-Fi region

Because we did not configure the Wi-Fi when the OS was installed, you will probably see the following message when the Raspberry Pi boots:

Wi-Fi is currently blocked by rfkill.
Use raspi-config to set the country before use.

This is because the Wi-Fi interface is disabled to prevent an unconfigured interface from transmitting on the wrong frequencies for the country it’s operating in.

So, before we can use the Wi-Fi interface as an access point, we need to configure the region. You can use the interactive config tool to this:

$ sudo raspi-config

Go to Localisation Options then WLAN Country to set the correct region.

You can also set this directly from the command line. For example:

$ sudo raspi-config nonint do_wifi_country GB

Wireless access point configuration

The latest version of Raspberry Pi OS (“Bookworm”) uses NetworkManager for managing network connections. This greatly simplifies the task of setting up the Raspberry Pi as an access point.

This post on the Raspberry Pi forums provides a useful summary of basic NetworkManger commands (using the command line interface, nmcli): Getting Started with Network Manager.

The following commands set up the Wi-Fi interface as a wireless access point.

  1. Create a new connection called “vpn-gateway” using the Wi-Fi interface, with the network name (SSID) “VPN”:
  2. Configure the connection as an access point (“ap”).
  3. Set the security type and password (make sure you set this to something more secure than “password”!).
  4. Finally, enable the new connection
# Create the conection
$ sudo nmcli con add con-name vpn-gateway ifname wlan0 type wifi ssid VPN autoconnect true
# Configure as an access point
$ sudo nmcli con modify vpn-gateway 802–11-wireless.mode ap 802–11-wireless.band bg ipv4.method shared
# Set the security type and password
$ sudo nmcli con modify vpn-gateway wifi-sec.key-mgmt wpa-psk
$ sudo nmcli con modify vpn-gateway wifi-sec.psk "password"
# Enable the connection
$ sudo nmcli con up vpn-gateway

That’s it. You should now be able to connect to the access point from your laptop or phone.

Tips

You can delete the connection if, for example, you mess up the configuration and need to start again (yes, I did that, more than once):

$ sudo nmcli con delete vpn-gateway

And, although it’s not really necessary, I also renamed the Ethernet interface (to get rid of the spaces in the name) with the command:

$ sudo nmcli con modify Wired\ connection\ 1 connection.id wired

Installing and setting up ExpressVPN

ExpressVPN is available for the Raspberry Pi. They have a page with instructions for downloading and installing it: How to set up and use the ExpressVPN app for Raspberry Pi. I have summarised the main steps below but there is more detail, and some troubleshooting tips, on that page.

You will need to be logged in to your account to access the downloads. When you log in you will see your “VPN Activation Code”. You will need this to activate ExpressVPN after you have installed it.

Click on the Linux link in the list of operating systems and devices. Then select Raspberry Pi OS in the dropdown box on the right. You can then click on the Download link to download the file and then copy it to the Raspberry Pi. Or you can copy that link and download the file directly the from the command line on the Raspberry Pi. For example:

$ wget https://www.expressvpn.works/clients/linux/expressvpn_3.82.0.2-1_armhf.deb

Make sure you use the URL for the current version of ExpressVPN.

Note: Before installing on a 64-bit OS, you need to install some dependencies, as follows:

$ sudo apt install libc6:armhf -y

Then install the ExpressVPN package (using the name of the file you downloaded):

$ sudo dpkg -i expressvpn_3.82.0.2–1_armhf.deb -y

Next, you need to activate ExpressVPN. Enter the activation code you saw when you logged in (it is also shown on the download page):

$ expressvpn activate

Connecting

Before making any connections, you need to set the protocol that the VPN will use. This is essential to get the maximum performance (see Performance):

$ expressvpn protocol udp

Now you can connect to your chosen location. For example, to connect to London, UK:

$ expressvpn connect uklo

Note: if you get the message “Please disconnect first before trying to connect again” (because the VPN connected automatically on startup) then you should disconnect before connecting again to ensure that the correct protocol is used.

You can get a list of locations with:

$ expressvpn list

If you don’t specify a location, then it will use the location it thinks will give you the best performance (the “Smart Location”).

Testing

You can check the connection with the status command:

$ expressvpn status
Connected to UK - London
- If your VPN connection unexpectedly drops, internet traffic will be blocked to protect your privacy.
- To disable Network Lock, disconnect ExpressVPN then type 'expressvpn preferences set network_lock off'.

You can also check the VPN is working by getting the external IP address before and after connecting the VPN. For example:

$ curl https://api.ipify.org
178.239.198.90

To ensure the VPN always connects when the system boots, set auto-connect:

$ expressvpn autoconnect on

This will automatically reconnect to the last location used.

Performance

The first time I tried running ExpressVPN on the Raspberry Pi 4, I got very disappointing results. The download speed was about 1.4 Mbps. Significantly better than my initial experiments with the Raspberry Pi 3B but still not good.

Note: You can run a speed test from the command line with the speedtest-cli tool:

$ sudo apt install speedtest-cli -y
  ...
$ speedtest-cli - secure
Retrieving speedtest.net configuration…
Testing from UK Dedicated Servers (194.32.120.96)…
Retrieving speedtest.net server list…
Selecting best server based on ping…
Hosted by YouFibre (Manchester) [262.12 km]: 72.492 ms
Testing download speed...............................................................................
.Download: 25.43 Mbit/s
Testing upload speed......................................................................................................
Upload: 2.74 Mbit/s

I disconnected the VPN and the download speed was around 26 Mbps, pretty much what we expect.

Protocols

So it was the VPN that was causing the slowdown.

This Reddit thread suggests downgrading to an earlier version of ExpressVPN (and includes a link to a suitable version).

That worked, but didn’t seem very satisfactory. A bit more research shows that it is the encryption used by the VPN that slows things down (some processors include hardware that can be used to accelerate encryption algorithms, the Raspberry Pi processors don’t). This is consistent with the fact that the older version of ExpressVPN predates the “Lightway” protocol used by default in the latest versions.

Going back to the latest version and experimenting with different protocols confirmed this. By switching to the tcp or udp protocols, full speed was restored. But either of the Lightway protocols caused a massive drop in speed.

The udp protocol should, theoretically, be slightly faster and the tcp protocol should be slightly more reliable. I didn’t see any significant difference between them.

Summary table

 
Download/Upload Speed (Mbps)
VPN Status Raspberry Pi 3B Raspberry Pi 4B
Disconnected 27.7 / 2.8 27.6 / 2.8
Connected (auto) 0.2 / 0.5 1.4 / 1.6
Connected (udp) 6.5 / 2.5 25.9 / 2.7

The faster processor in the Raspberry Pi 4 makes the Lightway protocol handling faster and brings the udp protocol up to (very nearly) full speed.

Conclusion

I now have a Wi-Fi access point that provides VPN access. At the moment, it is a bare PCB lying on the floor. So the next step is to put it in a case, and maybe add a basic UI for turning the VPN on and off, and selecting the location.

However, I have now discovered that my “smart” TV is not as smart as I thought and can’t display the services I was interested in. So finding an alternative solution to that is the next project (maybe using a Raspberry Pi again).