Create your own VPN server with WireGuard in Docker

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

54 thoughts on “Create your own VPN server with WireGuard in Docker”

  1. 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

    Reply
    • 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.

      Reply
  2. 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.

    Reply
    • 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 via docker 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.

      Reply
    • 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

      Reply
    • I resolved the issue, problem is the the DNS address Core DNS file is not pointing to correct path
      I suggest in the peerx.conf file change DNS = 8.8.8.8.

      Reply
  3. I think I followed the instructions, but it does not seem to be creating the peer1.conf
    any suggestions where to start looking?
    Thanks

    Reply
    • 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.

      Reply
      • 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 !

        Reply
      • 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 !

        Reply
    • 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.

      Reply
  4. 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

    Reply
  5. 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?

    Reply
  6. 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.

    Reply
  7. 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

    Reply
    • 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

      Reply
  8. 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.

    Reply
  9. 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)

    Reply
  10. 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

    Reply
  11. 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

    Reply
  12. 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.

    Reply
  13. 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

    Reply
  14. 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?

    Reply
  15. Hallo Christian,

    ich verfolge Deine Tutorials seit geraumer Zeit. Super!
    Ich habe das Tutorial zu WG mit Docker angeschaut. Zuerst hatte ich es 1:1 so befolgt. Läuft.
    Jetzt, da ich mehrere Docker Container habe, habe ich folgendes gemacht:
    Docker CE installiert
    Portainer installiert
    In Portainer den Inhalt der Wiregaurd yaml als Stack reinkopiert => WG läuft auch

    Aber:
    Wie erstelle ich jetzt die Konfigurationen? Wenn ich in der SSH Shell

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

    eingeben kommt der Fehler “grep: /config/wg0.conf: No such file or directory”

    Kannst Du mir bitte weiterhelfen?

    Und vll auch gleich sagen, wie ich nun am besten Watchtower in portainer hinzufüge, damit alle Container automatsich upgdated werden bzw.ich zumindest informiert werde, wenn es ein update gibt.

    Danke & viele Grüße

    Christian

    Reply
    • Hallo Christian,

      zu dem Fehler kann ich leider nichts genaues sagen, da ich selbst WG nicht mit Portainer deployed habe. Ich kann mir nur vorstellen dass hier irgendwas mit den Volumes durcheinander gekommen ist oder eventuell irgendwas nicht richtig übernommen wurde.

      Zu Watchtower habe ich ein Video und auch Tutuorial gemacht, ich hab’s dir mal verlinkt 🙂
      https://youtu.be/5lP_pdjcVMo

      LG

      Reply
  16. Hallo Christian,

    wenn ich aber Wireguard nur mit Docker Compose deploye, dann funktioert das ja. Mit Protainer sollte ich das ganze aber dann verwalten können, und ggfs. auch einfach aktualiseren, oder?

    Reply
    • Ja du kannst container, die du nicht mit portainer erstellt hast trotzdem mit portainer editieren. Teilweise geht das aber nur bedingt, die compose Stacks z.b. leider nicht komplett

      Reply
  17. Hallo Christian,

    und wie mache ich ein Update auf die neueste Wireguard Version wenn ich es genau nach Deiner Anleitung installiert habe?
    Kann man es auch automatisch updaten?

    Danke

    Stefan

    Reply
  18. Great video works perfect.
    But I want to add a different DNS server to my vpn (for pihole Adblock).
    If I set this there is no internet connection with the vpn anymore.
    The DNS server has a different ip range.
    How can I set this to work?
    Thanks!!

    Reply
  19. Same problem here as mentioned by a few others…. DNS
    I wanted to use my own (pi-hole) DNS but I have also changed it to 8.8.8.8
    It works now, but I have to put up with pesky adds when using the VPN.
    What should be changed to use my own DNS?
    The Pi-Hole works perfectly for all the clients on my local network, just goes wrong with the VPN

    Reply
  20. @Jon:
    You should use “PEERDNS=auto” in your Dockerfile for Wireguard. This will make “setting peer DNS to 10.13.13.1 to use wireguard docker host’s DNS.” (if “INTERNAL_SUBNET=10.13.13.0” or nothing is set, because both are the default).

    Pi-Hole should also listen on your VPS localhost/127.0.0.1 address. But be sure to have set “Listen on all interfaces
    (Allows only queries from devices that are at most one hop away (local devices))” on https://pi-hole/admin/settings.php?tab=dns if you don’t want to become a “public dns resolver” 😉

    How to enable https for Pi-Hole:
    https://discourse.pi-hole.net/t/enabling-https-for-your-pi-hole-web-interface/5771

    Reply

Leave a Comment

I accept the Privacy Policy