Skip to main content

How to set up a WireGuard VPN server in a Docker container

Tired of paying for a commercial VPN? I set up a private WireGuard VPN server in a Docker container on a cheap virtual machine (VM) I already have with Linode. No need to pay anything! TLDR: It works straight out of the box, but the documentation had a few gaps, which I have sought to clarify below. Set up time is about 10 minutes, including configuring client devices.

Setting up the WireGuard server container on the host machine

Begin by pulling the Wireguard image from Dockerhub onto your host machine:

docker pull linuxserver/wireguard

Paste the multi-line command below into a terminal and will build a WireGuard container and set it running. Before you do that, note that there are a few options you may wish to configure:

  • Change the PEERS=1 to however many client devices (peers or endpoints) you want to connect to this server. This setting controls how many sets of logon credentials are generated; each peer uses a unique set.
  • Change TZ=Europe/London to match your server timezone.
  • Consider changing the default port 51820 to some other high port to evade casual scanning.
docker run -d \
  --name=wireguard \
  --cap-add=NET_ADMIN \
  --cap-add=SYS_MODULE \
  -e PUID=1000 \
  -e PGID=1000 \
  -e TZ=Europe/London \
  -e SERVERPORT=51820 `#optional` \
  -e PEERS=1 `#optional` \
  -e PEERDNS=auto `#optional` \
  -e INTERNAL_SUBNET=10.13.13.0 `#optional` \
  -e ALLOWEDIPS=0.0.0.0/0 `#optional` \
  -e LOG_CONFS=true `#optional` \
  -p 51820:51820/udp \
  -v /path/to/appdata/config:/config \
  -v /lib/modules:/lib/modules \
  --sysctl="net.ipv4.conf.all.src_valid_mark=1" \
  --restart unless-stopped \
  linuxserver/wireguard

Your VPN server is now running! Note that if you are using a firewall you might need to open the relevant port to allow WireGuard traffic in. I use the Uncomplicated Firewall, for which the command is simply:

sudo ufw enable 51820

Peer credentials are stored in /config: Peers are numbered sequentially from 1 upwards. Each peer has a subdirectory in the /config directory of the WireGuard container, named peer1, peer2 and so on. For example, peer 3 credentials will be found in:

/config/peer3/

Setting up mobile clients

Setting up mobile clients is very easy if you use the official app, which is available for both Android and iOS. The app offers you the option to import a tunnel by scanning a QR code, and you should use this. To display the QR code for a client in a terminal, SSH into the host machine and type:

docker exec -it wireguard /app/show-peer 1

This will output a graphic display of the QR code, which you can scan in with your phone’s camera to import the configuration. Activate the tunnel to use it. You can also manually access the QR codes as .png images, which are stored in the numbered subdirectory of /config/. For example, you will find the QR code for peer 1 at /config/peer1/peer1.png and so on.

Setting up laptop/desktop clients

The official WireGuard app for laptop/desktop clients does not have the option to scan QR codes, so you have to either enter the configuration manually or import a configuration file. Happily, the configuration files for each peer were generated when you created the WireGuard container. To view the configuration file for peer 1 in the terminal, SSH into the host machine and type:

docker exec -it wireguard cat /config/peer1/peer1.conf

To view the configuration for other peers, change the number as required, eg. /config/peer2/peer2.conf. I suggest saving the files to your desktop and then importing them in the app. But you can also add an empty tunnel in the app and cut-and-paste the configuration in.

That's it, you're up and running!

Note: Rebuilding the WireGuard container does not change the keys!

You might think that blowing the WireGuard container away and recreating it from the image would change the server/client keys, but it doesn’t. The reason is that our RUN script above creates a persistent config volume on the host machine at the following path (yes, this is the actual path):

/path/to/appdata/config

So, when you recreate the volume, the data persists here. If you want to change the keys, then you need to delete this volume (supposedly, just deleting the peer folders will do it, but that didn’t work for me).

Performance testing

In terms of ping time through to servers in Singapore (40ms), a WireGuard tunnel makes almost no difference using a wired desktop client, maybe a millisecond or two. I do see around 25 ms of overhead when using wireless clients (ac) connected to the same network, but that’s still pretty good.

I had hoped that I could run my online gaming traffic through my private WireGuard server and get rid of my commercial VPN provider. Unfortunately, that was not to be. One advantage of commercial VPNs is that they often provide more efficient routing (lower ping) than the least-cost garbage routes your ISP dishes out. Playing on a US-based game server, the ping through my commercial VPN provider is 100ms better than my base internet connection. Sadly, my private WireGuard server does not give this benefit because it doesn’t offer better routing than my ISP provides.

But it's still awesome. And being so lightweight, I can run it as a utility on my existing virtual machine, without having to pay anything more.

Copyright, all rights reserved.