Skip to main content
  1. Posts/

Hosting your Own VPN Using Docker and Wireguard

·1427 words·7 mins
Don’t expose yourself!

Introduction

If you want to have access to your internal home network services while you area away you would need to do one of two things.

  1. Open those ports to the open web
  2. Create a VPN to connect back to your home network. You do not want to pick option 1 unless you know for certain you have the means to protect those services from others on the web. Hosting a VPN is much safer becuase you can now access those interal sites without having to expose yourself to the open web. In this post I will go over how to setup a Wireguard VPN server on your home network so that you can keep your sites secure and accessible. In this guide I will show how to create a docker container running wireguard on a linux based system (Ubuntu Server 23.04).

Prerequisites

To get started you will need a few things.

  1. An always on device with a constant internet connection
    • This can be a number of things nowadays. A Raspberry Pi would do just fine. (One that supports Gigabit is preferred but not necessary)
    • Also if you plan to use a Raspberry Pi you may instead want to look into Pi-VPN. It will serve the same function as this guide but much simpler and not require the need for docker. This guide is for those who wish to use Wireguard as a container.
  2. Assuming you have selected the host you wish to run the service on you will need to now install docker, if you haven’t already.
If you want an easy GUI to manage containers be sure to check out Portainer!

Setting up Host

Now that you have all the software installed on the host you can now create the docker compose file. You may also use docker run if you wish but I like to use compose because it is easier to edit the values as need. In this guide I am using a Docker image from linuxserver.io which has a number of great images and documentation.

If you would like to explore the different options available for this image you can see their GitHub Repo for all the available environmental variables. For the sake of the guide I will include what I use as an example.

In your host machine create a file anywhere named: docker-compose.yml. Myself I like to make folders to hold my docker compose files so that I am more organized.

For example, my wireguard compose file is in /opt/wiregaurd. This has an added benefit of holding the addtional files that will be generated when starting the container. You can create this file by running: touch docker-compose.yml

Now in your preferred editor add the following to your compose file:

version: '1.0'
services:
  wireguard:
    image: lscr.io/linuxserver/wireguard
    container_name: wireguard
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    environment:
      - PUID=1001
      - PGID=100
      - TZ=Americas/Chicago
      - SERVERURL=auto
      - SERVERPORT=51820
      - PEERS=5 #change to match how many devies you want to use Wireguard on
      # - PEERDNS=192.168.5.55
    volumes:
      - /opt/wireguard:/config
      - /lib/modules:/lib/modules #do not change
    ports:
      - 51820:51820/udp
    restart: unless-stopped

Things to Note

There are several things in the compose file you may need to review and update based on your network configuration. See the following sections to understand what each section of the compose file does to the service configuration.

Environment (environment:)

Timezone

There are three key fields here that you need to adjust. You will need to update the - TZ variable to match your local timezone.

Ports

If you wish to change the port that wireguard is served on, you can edit the - SERVERPORT value to match the desired port.

You will also need to update the ports: section if you update this value. I would advice you leave this as the default for now.

Peers

You can set the number of peers you want wireguard to create.

Peer DNS

You’ll notice I have commented out PEERDNS in my example. This is an optional field is is used if you have your own DNS you wish to reference in your network. In this case I have my own internal DNS server, so I use that as my PEERDNS You can remove this line or if you have your own DNS, add its local IP address here.

Volumes (volumes:)

In the volumes: is there you can chose to map a host system folder that gets passed into the container. In my example you can see I have made it /opt/wireguard:/config. For the sake of the guide I suggest leaving it but you can change if you wish. I will be using this path the rest of the guide.

Ports (ports:)

You will need to also update the ports: section of this compose file to map correctly. There is a lot more flexiblity in this section that allows you to map host and container ports if you ever run into port conflicts. For now I would advice you leave as default.

Restart (restart:)

Restart tells on what condition the container will (re)start if the host machine was rebooted or shutdown. Here are a list of the different options available for restart:

Flag Description
no Don’t automatically restart the container.
on-failure[:max-retries] Restart the container if it exits due to an error, which manifests as a non-zero exit code. Optionally, limit the number of times the Docker daemon attempts to restart the container using the :max-retries option. The on-failure policy only prompts a restart if the container exits with a failure. It doesn’t restart the container if the daemon restarts.
always Always restart the container if it stops. If it’s manually stopped, it’s restarted only when Docker daemon restarts or the container itself is manually restarted.
unless-stopped Similar to always, except that when the container is stopped (manually or otherwise), it isn’t restarted even after Docker daemon restarts.

Once you have made your updates, save the file and in the same directory the docker-compose.yml file is in type: docker compose up -d. This will pull the image from the docker repo and start the container. Once you are able to enter another command you can use docker compose watch ps to see the status of the container while its starting. Once you see RUNNING you can use CTRL+C to exit the screen.

Now in order to connect to the VPN from your device you will need to add the peer config. You can do this two different ways.

Showing the QR Code

In the same directory as the docker-compose.yml execute the command: docker exec -it wireguard /app/show-peer 1 (1 being the number config you want to show.) And an QR code image will be displayed.

You can add this config to a smartphone by downloading the wireguard app from your devices app store and adding the tunnel there.

Grabbing the Peer File from the System.

In this method you will need to be root to gain access to the peer folders. Once you are root you can cd to /opt/wireguard/peer1 and in that folder you will see a file named peer1.conf. I use cat peer1.conf to print the contents of the file to the terminal window. Below is an example of that output. Your information will be different and I have also removed much of my info for security sake. This file contants secrets you do not want exposed online.

[Interface]
Address = 
PrivateKey = 
ListenPort = 51820
DNS = 

[Peer]
PublicKey = 
PresharedKey = 
Endpoint = 0.0.0.0:51820
AllowedIPs = 0.0.0.0/0, ::/0

Copy this and save this as a file on the host PC you wish to have the profile added as peer1.conf.

Now download the Wireguard application and add that file as your tunnel.

Turn on the tunnel and try to navigate to a known site like Google. If the site loads congratulations you now have your own VPN!

Its important to not that this type of VPN DOES NOT hide your traffic from your ISP or any other data provider.

Sites don’t Load When VPN is On

If you turn on your connection and you cant get a site like Google to load here are a few things you can try:

  • Verify you have port 51820 open on your router. You will need to open this port on your router for the connection from the internet to work.
  • Verify UFW allows connections on 51820. You can open UFW port by executing command: ufw allow 51820
  • If you entered a custom PEERDNS check to see if that DNS is serving queries correctly.