Programster's Blog

Tutorials focusing on Linux, programming, and open-source

Deploy WireGuard Server With Web UI Through Docker

Introduction

This tutorial will show you how to quickly deploy your own WG Easy wireguard server, which I found to be the easiest and fastest way to deploy a VPN server, which even worked behind my dedicated server's NAT (after having configured port forwarding).

If you wish to deploy a Wireguard server from scratch (e.g. not using Docker and without a nice web UI, then I would suggest you watch Christian Lempa's video on Wireguard Installation and configuration instead.

Prerequisites

  • Docker and Docker Compose

Steps

Create A Password Hash

WG Easy has switched to using bcrypt hashed passwords rather than having the password as plain text in the environment variables.

To create a strong random password and generate its bcrypt form, I found the easiest solution to be this PHP script I created and ran in the terminal:

<?php

$randomPassword = base64_encode(random_bytes(32));
print "Random password is: " . PHP_EOL;
print $randomPassword . PHP_EOL . PHP_EOL;

$hash = password_hash($randomPassword, PASSWORD_BCRYPT);
print "Bcrypt hash is: "  . PHP_EOL;
print $hash . PHP_EOL;

When specifying it in the .env file later, one will need to make sure to wrap the generated hash in single quotes ('), in order for it to pass through correctly.

Create The Docker Compose File

Log into your server and create the following docker-compose.yml file wherever you wish to "install" this.

services:

  wg-easy:
    image: ghcr.io/wg-easy/wg-easy:${WG_VERSION}
    container_name: wg-easy
    ports:
      - "51820:51820/udp"
      - "51821:51821/tcp"
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
    volumes:
      - ./volumes/wireguard:/etc/wireguard
    environment:
      - WG_HOST
      - PASSWORD_HASH

Create The Environment File

Now create a file called .env with the following content, in the same directory as the docker-compose.yml file you just created. Be sure to update the values as appropriate to you.

# Specify the version of WG Easy to use. Check out 
# https://github.com/wg-easy/wg-easy/pkgs/container/wg-easy/versions
# or use the value "latest"
WG_VERSION="14"


# Specify the IP/FQDN that client's should try to connect to.
# E.g. your server's public IP address. E.g. "wireguard.mydomain.com" or "168.52.43.28"
WG_HOST=""


# Provide the random password outputted by the script.
PASSWORD_HASH='$2y$XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

Extra - Full List Of Possible Configuration Options

In case you are interested, the full list of possible environment variables can be found on the GitHub page. If you make use of any additional environment variables, be sure to add them to the docker-compose.yml file as appropriate. E.g. just adding them to the .env file won't work on its own.

A common setting that you may wish to set is the WG_DEFAULT_DNS which you may wish to change to an internal DNS server that you deployed, or 8.8.8.8, 8.8.4.4 if you would prefer to use Google DNS instead of the default, which is Cloudflare's on 1.1.1.1.

You may also wish to add UI_TRAFFIC_STATS with a value of true, in order to turn on traffic stats which default to being off.

Deploy

Run the following command to start the Wireguard server now that we have created the docker-compose.yml and .env files.

docker-compose up -d

Optional - Nginx Configuration

I am using a manually deployed Nginx reverse proxy for which I added the following example configuration to have it serve up the site. Tweak it as you need, making sure to update the FQDN and your wireguard servers internal IP address.

server {
    listen 80;
    server_name wireguard.mydomain.com;
    return 302 https://wireguard.mydomain.com$request_uri;
}

server {
    listen 443 ssl;

    server_name wireguard.programster.org;

    access_log  /var/log/nginx/access.log;

    ssl_certificate      ssl/wireguard.mydomain.com/site.crt;
    ssl_certificate_key  ssl/wireguard.mydomain.com/private.pem;

    ssl_protocols        TLSv1.3;
    ssl_ciphers RC4:HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers on;
    keepalive_timeout    60;
    ssl_session_cache    shared:SSL:10m;
    ssl_session_timeout  10m;

    location / {
        proxy_pass      http://w.x.y.z:51821/;
        include         /etc/nginx/proxy.conf;
    }
}

Alternatively, you may be interested in using something simpler like Nginx Proxy Manager.

Optional - Change The UDP Port

Unfortuantely, I have a TURN server deployed on that IP whose UDP port range is 49152 - 65535, for which the default wireguard UDP Port of 51820 is right in the middle of. Thus I opted to change the Wireguard server to operate on port 40000 instead. This was as easy as adding WG_PORT=40000 to the enviornment variables, and remapping the UDP port to be listening on the host on port 40000. The WG_PORT tells the website to generate the client configurations such that it tells the client to connect on that port, and we did the port remapping at the Docker level. Below is the updated docker-compose.yml file:

services:

  wg-easy:
    image: ghcr.io/wg-easy/wg-easy:${WG_VERSION}
    container_name: wg-easy
    ports:
      - "40000:40000/udp"
      - "51821:51821/tcp"
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
    volumes:
      - ./volumes/wireguard:/etc/wireguard
    environment:
      - WG_HOST
      - PASSWORD
      - WG_PORT=40000

If you have already downloaded client configuration files, you will need to either re-download them to get the updated version, or manually update the existing ones to have :4000 on the end of the Endpoint value (assuming you also used port 4000).

Creating A Client

Now that we have deployed our Wireguard server, we need to create a client connection, and download the configuration file in order to use it. Each client will have a fixed IP on this network, meaning that client's can communicate with each other as well.

If you are not using a proxy, then one should be able to connect to the server in one's browser with HTTP on port 51821. If you are using a proxy, then you have probably configured it so that you can connect on HTTPS without specifying a port number.

http://192.168.0.18:51821/


There is no username, and the password is whatever you set in the environment file earlier.


Click on the New button in the top-right corner in order to create a new client connection configuration.


Give it a name to remember it by.

Download it's configuration by clicking on this icon.

The client configuration file will be named after the name you gave the client connection (1). If you didn't change the DNS through the environment variables, you may wish to just manaully change it here (2). A value of 1.1.1.1 is for Cloudflare's DNS. The server's FQDN or IP address should show up as the Endpoint (3). Here I have port 4000 because I manually changed the server configuration to not clash with my TURN server's UDP port range.

Using Wireguard

Install Required Packages

In order to use Wireguard on Ubuntu, you need to install wireguard and the openresolv packages.

sudo apt update \
  && sudo apt install -y wireguard openresolv

You need to install the openresolv otherwise later you will get:

/usr/bin/wg-quick: line 32: resolvconf: command not found

Move Configuration File Into Place

Move the downloaded configuration file to /etc/wireguard/$NAME_FOR_CONNECTION.conf and make sure that the permissions are set to 700 (that file contains your key/secret).

Connect

wg-quick up $NAME_FOR_CONNECTION

If you're used to using OpenVPN, you may find it unusual that the output just stops. Nothing went wrong and you are still connected.

Alternatively, if you don't wish to rely on sticking the configuration file in the expected location, you can simply specify the absolute path to the configuration file like so:

wg-quick up /home/username/vpns/my-wireguard-config.conf

Disconnect

To disconnect, run:

wg-quick down $NAME_FOR_CONNECTION

... or if you used an absolute path to a config file:

wg-quick down /home/username/vpns/my-wireguard-config.conf

Get Status

If you wish to get the status of the wireguard connections (such as seeing if any are up, and how much traffic has flowed through them), you can run:

sudo wg show

BASH Script

I prefer to run my VPN connection in a tmux/byobu session, and leave it running, knowing that I can press ctrl-c to close it when I am done. This also helps prevent me from forgetting that I have a VPN connection open, since it is tying up one of my first tmux session in byobu. To do this, I have a start-vpn command that runs the following BASH lines:

#!/bin/bash
wg-quick up /path/to/vpns/connection-name/client.conf
watch -n 1 sudo wg show
wg-quick down /path/to/vpns/connection-name/client.conf

By doing this, the watch -n 1 sudo wg show line "ties up" the tmux session, outputting the connection statistics, until I am ready to quit by pressing ctrl-c. It will then quit out of the watch command and finish by running the command to shut the connection down, which is exactly what I want.

Using Wireguard On Windows / Mac / Android / iOS

You can download a client from the Wireguard website.

References

Last updated: 8th September 2024
First published: 30th April 2024