In this tutorial, I will show you how to easily create your own private VPN server with WireGuard running in a Docker container. I will walk you step by step through the installation, configuration, and how to add clients to your VPN server.
You can follow this tutorial with any Ubuntu- or Debian-based Linux distro. Other distros may also work, but it isn’t tested and optimized by the creator of the docker image, we’re using. In my case, I’ve used Ubuntu 20.04 LTS, because it already has installed the WireGuard kernel module.
install docker and docker-compose
Before we can create and start containers, we need to install Docker and Docker-compose. If you already have installed docker and docker-compose on your server, you may skip these steps.
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update && sudo apt-get install docker-ce docker-ce-cli containerd.io
sudo curl -L "https://github.com/docker/compose/releases/download/1.26.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
sudo usermod -aG docker $USER
newgrp docker
Create a docker-compose file
Next, we need to create a docker-compose file to easily manage your WireGuard container. To do that, I’m using a docker image and template from the website https://linuxserver.io. The guys from linuxserver.io are ethusiasts and manage docker images for the community.
First, I create a new folder in the /opt directory called /opt/wireguard-server and create a new docker-compose.yaml file in this directory. You should also change the ownership of this folder to your Linux user.
sudo mkdir /opt/wireguard-server
sudo chown christian:christian /opt/wireguard-server
vim /opt/wireguard-server/docker-compose.yaml
In this file, you can use the following template, please refer to the linuxserver/wireguard documentation: https://hub.docker.com/r/linuxserver/wireguard
version: "2.1"
services:
wireguard:
image: linuxserver/wireguard
container_name: wireguard
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/London
- SERVERURL=wireguard.domain.com #optional
- SERVERPORT=51820 #optional
- PEERS=1 #optional
- PEERDNS=auto #optional
- INTERNAL_SUBNET=10.13.13.0 #optional
volumes:
- /opt/wireguard-server/config:/config
- /lib/modules:/lib/modules
ports:
- 51820:51820/udp
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped
Replace the SERVERURL with the public IP address of your WireGuard Server, because your clients will need to connect from outside your local network. You can also set this to auto, the docker container will automatically determine your public IP address and use this in the client’s configuration.
start your WireGuard Server
Now you can start your WireGuard container with the following command and clients should be able to connect.
cd /opt/wireguard-server
docker-compose up -d
Distribute the config files to clients
You could also use the linuxserver/wireguard docker image for your clients. But I think it’s more practical for a client to install WireGuard directly on the host OS. If you want to know how to do that, you can also refer to my article about WireGuard installation and configuration on Linux.
When you have started the WireGuard container, it should automatically create all configuration files in your /opt/wireguard-server/config folder. All you need to do is to copy the corresponding peer1/peer1.conf file to your client and use that as your wg0.conf, for instance. If you want to connect mobile phones you can also just scan the peer1.png QR code, to print the QR code to the console, simply use the following command
docker exec -it wireguard /app/show-peer <peer-number>
Add additional clients
If you want to add additional clients, you simply can increase the PEERS parameter in the docker-compose.yaml file. After changing this value you need to restart your docker container with the –force-recreate parameter.
docker-compose up -d --force-recreate
Hallo Christian, erstmals danke für die tolle Dokumentation. Ich spiele gerade mit dem Gedanken, VPN für mein Smarthome einzurichten. Wie sicher ist es einen Tunnel im Docker Container zu terminieren? Auf dem Docker Host laufen doch einige interne Dienste wie Smarthome, NAS, etc. Danke und LG, Cavediver
Hallo 😉 Ingesamt ist WireGuard meine Meinung nach ein sehr sicheres Protokoll. Ich weis zwar nicht genau was dein Anwendungsfall ist, du solltest aber ohne Probleme WireGuard für eine VPN (egal mit oder ohne Docker) dafür einsetzen können. Was beim Docker Container zu beachten ist: Hier musst du eventuell etwas mit dem Netzwerk experimentieren, ob du das im host-modus laufen lässt, damit der Docker Container das Interface auf dem Host-System verwendet, oder ob du das Interface in dem Container terminieren lässt und der Traffic dann entsprechend geroutet wird. Generell ist das aber alles Möglich, da WireGuard sehr flexibel ist und es eigentlich nur darauf ankommt, wie du es konfigurierst.
Hello,
I did the setup exactly as mentioned by you and added a peer using the qr code. It shows connected but i cannot browse the internet when i get connected to the vpn server. please can you advise what could be wrong.
I would try to check if the network packets are arriving at the hosts wg0 interface. You should try with
tcpdump -envi wg0
on the docker containers wg0 interface. Note you need to execute from the docker container viadocker exec -it wireguard "tcpdump -envi wg0"
. Possible reasons are the packets are not arriving at the host or the host can’t process or forward these packets.Hi,
thought I’d drop a line as I had the exact same problem, peer connects fine, handshake ok, but then no internet access at all.
My setup uses docker (installed via OMV-extras) on OMV5 running on an arm64/buster. I didn’t read the small print though, if you use the same setup you need to change the iptables to iptable legacy (available in the same menu as the docker installation), as docker needs iptable legacy. So it turned out the wireguard docker wasn’t able to add the necessary routing because I was using the default nft, hence the handshake worked fine but the routing didn’t.
Hope this helps someone,
Kevin
Thank you buddy!
I think I followed the instructions, but it does not seem to be creating the peer1.conf
any suggestions where to start looking?
Thanks
Check if the “wireguard” kernel module is loaded on the host OS with
lsmod | grep wireguard
. If it’s not loaded it won’t create any configs. I had the same issue when there were pending software updates on the host OS, or sometimes it fails to install the kernel module on the host OS.Hi,
I have the same issue. I’m running Ubuntu Server 20.04.2 and it can’t create peers config file.
Is there a package to install ?
Thank you !
Hi, I have the same issue. I’m running Ubuntu Server 20.04.2 and it can’t create peers config file.
Any idea ?
Thank you !
can you help me to create and moderate the peers? meybe down or add new peers
If you’re using the docker image from this tutorial, you can simply change the number of peers in the compose file. It will automatically create new peer configs once you restart the container.
Danke Christian— sehr verständlich und läuft
hi, thank you for such a great video.
I want to install this docker in my centos 7, but I don,t know how to give permission or manually add this „cap_add:
– NET_ADMIN
– SYS_MODULE“
can you help me with this?
Thank you
You can add them with
--cap-add NET_ADMIN --cap-add SYS_MODULE
in the docker run command. Hope this helps 🙂Hi, I get this error:
E: The repository ‚https://download.docker.com/linux/ubuntu buster Release‘ does not have a Release file.
during:
sudo apt-get update && sudo apt-get install docker-ce docker-ce-cli containerd.io
nevermind, found it 🙂
okay, that’s great 🙂
Hey Christian,
Love your videos and blogs. I’d been trying to deploy wireguard in docker, and was finally able to do so thanks to your current video.
I have one question though. How can I create multiple interfaces and create peer files for those interfaces? I am able to create multiple peers, but am only able to create wg0. Is there any way to achieve this?
Hi Chrisitian!
I’m new to Docker but let me tell you that I’m blown away with the simplicity of that setup.
I’ve got a working Wireguard VPN up and running and it took me less then 5 minutes. That’s insane….
Anyways I just wanted to say thank you very much for that tutorial.
I’ll keep an eye on your website.
Hallo Christian,
tolles Tuturials!
Ich hab den WG Server eingerichtet. Läuft super. Hab DNS 1.1.1.1, 1.0.0.1
Wie bekomme ich den WG Server jetzt noch dazu DoT zu nutzen?
Danke
Stefan
Hi Stefan,
der WG Server hat leider noch keinen DOT client integriert, den müsstest du quasi ins Image manuell hinzufügen, oder du machst eine DNS weiterleitung auf den host, der dann einen DoT client ausführt. LG
Thanks Chrisitian – great guide. Something I discovered was you can also define peers with a comma seperated list – this is nice that way I can name the peers and keep track of my configs easier.
-e PEERS=1 Number of peers to create confs for. Required for server mode. Can be a list of names too: myPC,myPhone,myTablet…
Also have you tried to connect this with a wireguard GUI. One i tried was https://github.com/EmbarkStudios/wg-ui. I tried the userspace version to run the wireguard server internally but I couldn’t get that working. I also tried linking the wireguard-server to this contianer. Their is a runtime option
–wg-endpoint=”127.0.0.1:51820″ WireGuard endpoint address
I’ve tried setting this to my container name i.e. wireguard-server:51820 – I’m going to keep playing around and see what i can do.
But thank you for the guide – it was a great basline.
Thank you man! I’m glad you liked it 🙂
Danke. Vll kannst Du ja mal ein Video dazu machen:)
Hmm wäre ne gute Idee 😃
Hallo Christian
ich habe Fehler beim Handschake:
2021-01-17 20:43:23.434
[NET] peer(dIGM…ZyAU) – Sending handshake initiation
2021-01-17 20:43:28.540
[NET] peer(dIGM…ZyAU) – Handshake did not complete after 5 seconds, retrying (try 2)
Hallo Guido, das kann alles mögliche sein, wahrscheinlich ist ein Netzwerkproblem, d.h. Client kann server nicht auf dem Port erreichen, oder ähnliches.
Hi Christian, thanks for this guide. What do you need to change if you followed this guide but have no Internet when connected through the vpn?
Thanks
Daniel
I would first check if the packets are coming through the tunnel and next if your DNS and gateway setup is correct. Hope this helps
Hi
This is a very good tutorial thank you for the hard work.
Just a few suggestions firstly in the UK you don’t get a static IP address. So it may be a good idea to inform people about using a dyndns type setup so that the IP address can change but the profile will still work.
And the other thing is have you tested this one docker setup on raspberry pi at all?
And lastly do you need to open any ports on your router for this to work like you have to on openvpn?
Thank you again
I’ve not tested it on a raspberry pi, I know there is a solution out there to make WireGuard possible on Raspberry.
Hi Christian, great video! I ran into a problem when trying to run the “wg-quick up wg0” command. This is what I got:
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.13.13.2 dev wg0
[#] ip link set mtu 1420 up dev wg0
[ 3175.044515] wireguard: wg0: Cold not create IPv4 socket
RTNETLINK answer: Address Already in use
[#] ip link delete dev wg0
Any recommendations on how I can fix this issue? Thanks.
Hey, it seems that you already got an internal network that overlaps with the 10.x.x.x/8 network config.
Hi Christian! Great tutorial, thanks a lot! I’ve just installed everything – I had docker and docker-compose running already on the Raspberry Pi – and replaced a PiVPN installation. Worked flawlessly.
My only question is: Does adding more peers by recreating the container (docker-compose up -d –force-recreate) would recreate all existing peers configs as well, or would just add new peers sequentially (peer4, peer5, peer6…)? If so, that means we would need to reconfigure existing peers again each time new peers are added?
Thanks,
JP
Kann ich diese configuration mit watchtower automatisch updaten lassen?
Hello zusammen.
Ich habe den Container auf meinem Pi installiert.
Die VPN Verbindung steht auch, jedoch kann ich nur Weboberflächen aus meinem internen Netz erreichen und öffnen, aber eine Internetverbindung bekomme ich nicht hin…
Als DNS Server habe ich mal den Pi Hole (auch als Docker) eingerichtet, aber auch mal den Standard gelassen, ohne Erfolg.
Hat jemand eine Idee?