How to Set Up WireGuard VPN and WireGuard-UI with Docker

Learn how to set up your own WireGuard VPN server and easily manage it via the web-based WireGuard-UI using Docker Compose.

Protecting online privacy and security has become increasingly important in the digital age. A virtual private network (VPN) can help you achieve this by encrypting your internet traffic and routing it through a private network, making it more difficult for anyone to intercept your data.

WireGuard is a relatively new VPN protocol that has gained popularity for its reliability, simplicity, speed, and security. However, setting it up can often be a complicated process. For example, here’s how to do it on Ubuntu.

That’s where WireGuard-UI, a web-based user interface for WireGuard that makes it easier to configure and manage your VPN server, comes in. And to make things even easier, Docker Compose gives you an easy way to integrate both.

This article will explore how to set up a WireGuard VPN and WireGuard-UI with Docker Compose, making it simpler for you to configure and manage your VPN server and clients. By the end of this guide, you’ll have a fully functional VPN running on Docker containers with an easy-to-use web-based management interface.

So without further ado, let’s get to work.

Step 1: Ensure You Have Docker and Docker Compose Installed

Docker is a platform for creating and running applications in lightweight containers, which makes deploying and managing software much more effortless.

At the same time, Docker Compose, which you can think of as an add-on to Docker, is a tool used for defining and running multi-container Docker applications, which is precisely our case.

In other words, we will deploy the WireGuard VPN server and WireGuard-UI, a web-based user interface for WireGuard, “packaged” as a single application using Docker Compose.

The first step is to ensure you have Docker installed on your machine. The easiest way to do this is by running:

docker --version
Verify that Docker is installed.
Verify that Docker is installed.

If you receive a message like the one above, all is well. You have Docker installed on your system.

However, if you get something like “bash: docker: command not found…” in response, there’s no room for worry. We’ve got you covered, and our detailed guides on installing Docker on Ubuntu, Debian, Linux Mint, Fedora, AlmaLinux, and Rocky Linux will help you get it up and running on your system quickly and easily.

The next step is to ensure that Docker Compose is also installed on your system. Similar to the above example, run:

docker-compose --version
Verify that Docker Compose is installed.
Verify that Docker Compose is installed.

If the command output is similar, congratulations! You have everything you need to install the WireGuard VPN server with the WireGuard-UI web interface by using Docker Compose.

However, if the message is something like “bash: docker-compose: command not found…” just run the two commands below, and you will have Docker Compose installed on your system in seconds.

sudo curl -L "https://github.com/docker/compose/releases/latest/download/docker-compose-linux-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

In addition, if you want an in-depth introduction to Docker Compose, check out our comprehensive guide.

Step 2: Set Up WireGuard VPN Server & WireGuard-UI with Docker Compose

As we already said, Docker Compose allows you to build and run stacks of multiple containers. To use it, you must first create a “docker-compose.yaml” file that configures the containers for your application.

Create a “wireguard” directory and switch to it.

mkdir wireguard
cd wireguard

Then, using your preferred text editor, create a file named “docker-compose.yaml”:

vim docker-compose.yaml

Add the following content to it, then save and exit the file.

version: "3"

services:

  # WireGuard VPN service
  wireguard:
    image: linuxserver/wireguard:latest
    container_name: wireguard
    cap_add:
      - NET_ADMIN
    volumes:
      - ./config:/config
    ports:
      # Port for WireGuard-UI
      - "5000:5000"
      # Port of the WireGuard VPN server
      - "51820:51820/udp"
    restart: unless-stopped

  # WireGuard-UI service
  wireguard-ui:
    image: ngoduykhanh/wireguard-ui:latest
    container_name: wireguard-ui
    depends_on:
      - wireguard
    cap_add:
      - NET_ADMIN
    # Use the network of the 'wireguard' service
    # This enables to show active clients in the status page
    network_mode: service:wireguard
    environment:
      - SENDGRID_API_KEY
      - EMAIL_FROM_ADDRESS
      - EMAIL_FROM_NAME
      - SESSION_SECRET
      - WGUI_USERNAME=admin
      - WGUI_PASSWORD=password
      - WG_CONF_TEMPLATE
      - WGUI_MANAGE_START=true
      - WGUI_MANAGE_RESTART=true
    restart: unless-stopped
    logging:
      driver: json-file
      options:
        max-size: 50m
    volumes:
      - ./db:/app/db
      - ./config:/etc/wireguard

Remember to change the values of the “WGUI_USERNAME” and “WGUI_PASSWORD” environment variables in the file above to the username and password you will use to access WireGuard-UI.

In addition, it is essential to note that the Docker volumes containing the WireGurard configuration settings (“./db” and “./config “) will be created and made available in the current “wireguard” directory. Modify the YAML file if you decide you want them somewhere else. For example, “/srv/my-wireguard-conf:/etc/wireguard.”

However, if you do this, change the Docker volumes in both the “wireguard” and “wireguard-ui” service parts. This is a must, as the WireGuard-UI must have access to the WireGuard VPN server configurations to manage it.

Step 3: Run the Containers with Docker Compose

Finally, we are ready to run WireGuard VPN Server and WireGuard-UI using Docker Compose. To do this, execute the below-given command from the current “wireguard” directory where the “docker-compose.yaml” file is located.

Start and run the containers in the background:

docker-compose up -d

The images will start downloading, and Docker Compose will begin to make its magic. The entire procedure will take less than a minute. In the end, you should see a screen similar to the one below, informing you that all containers have been successfully created and started.

Running WireGuard VPN Server & WireGuard-UI with Docker Compose.
Running WireGuard VPN Server & WireGuard-UI with Docker Compose.

Of course, the initial delay before the start of the WireGuard and WireGuard-UI containers will be a one-off, as Docker needs to download the images from the internet first.

All subsequent runs will take seconds since the required Docker images will already be available locally on your Linux system.

Step 4: Configure the WireGuard VPN Server Using WireGuard-UI

You can now open your browser and navigate to “http://server-ip-address.” The WireGuard-UI login page will greet you.

Enter the username and password you set in the “docker-compose.yaml” file via the “WGUI_USERNAME” and “WGUI_PASSWORD” environment variables, then hit the “Sign In” button.

WireGuard-UI login page.
WireGuard-UI login page.

And here is the moment to provide an essential, if not critical, clarification. This guide uses an unencrypted HTTP connection to access the WireGuard-UI. We only do this because it is a virtual machine in our testing lab that we created exclusively for this tutorial for demonstration purposes, after which we will delete it.

Always use only a secured HTTPS connection in your production environment to access the WireGuard-UI management interface. You can ensure this by putting a reverse proxy service in front of the WireGuard-UI with the ability to automatically generate for you free Let’s Encrypt SSL certificates, such as Caddy.

On top of that, if you want to make things even more “techy,” you can bet on using Traefik, an open-source container-focused reverse proxy service that also automatically issues SSL certificates. We use it frequently in our production systems and can confidently say it performs flawlessly.

Step 5: Configure the WireGuard VPN Server

The fun begins here, but don’t worry; we’ll walk you through it smoothly.

To start, switch to the “Wireguard Server” tab. Then in the “Post Up Script” field, put:

iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

And respectively in “Post Down Script“:

iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

In short, this enables packets to and from your WireGuard server to be routed using Network Address Translation (NAT) inside the Docker’s WireGuard container. We won’t go into detail about it because it is outside the scope of this guide.

Then click the “Save” button to save your changes, and finally, hit “Apply Config” to apply them to the WireGuard VPN server.

Configure the WireGuard VPN Server via WireGuard-UI.
Configure the WireGuard VPN Server via WireGuard-UI.

You will be asked if you want the changes to be applied and the WireGuard server restarted. Confirm by clicking the “Apply” button.

Applying settings to the WireGuard VPN Server.
Applying settings to the WireGuard VPN Server.

Go to the “Global Settings” tab and carefully review the information. Your public IP address should be entered in the “Endpoint Address” section. WireGuard-UI will try to find it and fill it in automatically. However, if it fails, you must enter the correct one.

If you need help figuring out your public IP address, our guide on the subject will greatly help.

By default, the system sets the DNS server to “1.1.1.1,” which works great. However, change it in the “DNS Server” field if you want to use a different one.

Set the WireGuard VPN Server global settings.
Set the WireGuard VPN Server global settings.

Of course, if you make any changes, remember to apply them by clicking on the “Save” button and then hitting “Apply Config.”

Step 6: Create a New WireGuard Client

To create a new client that will connect to the WireGuard server to establish a VPN connection, click on the “New Client” button in the upper right corner of the WireGuard-UI.

This opens a modal window, where I recommend you enter only the client name and email and leave the other options as they are by default. I’m convinced you understand what you’re doing if you change them.

Finally, click the “Submit” button to add the client to the WireGuard VPN server.

Add a new WireGuard client.
Add a new WireGuard client.

The WireGuard-UI will do all of the work in the background to generate the necessary public and private keys to enable the VPN connection for the client, as well as automatically set some options related to routing its traffic, the DNS server used, and the internal private IP address it will receive when connecting to the WireGuard server.

If you’re nevertheless curious about what things look like “from the source,” you can look at the final result, which is stored as a JSON file in the “clients” directory of the Docker “db” volume.

WireGuard's client configuration.
WireGuard’s client configuration.

Attention! Never edit this file directly; only through the WireGuard-UI interface provided.

Finally, we want to emphasize something significant. It is essential to remember that whatever changes you make to users, always complete the operations with the “Apply Config” button. Otherwise, your changes will be saved but not applied to the WireGuard VPN server, which may lead you to believe that things aren’t working correctly.

Step 7: Setting Up WireGuard Client

We come to the most exciting part, where we will see the results of our efforts so far – configuring the client part and connecting it to the WireGuard VPN server.

Switch to the “Wireguard Clients” to see currently available ones.

View WireGuard's clients.
View WireGuard’s clients.

From here on, the approach is now specific as to whether we will make an encrypted VPN connection to the server from a mobile device such as a phone or tablet or a desktop workstation, laptop, etc. We will consider both cases.

Connecting to WireGuard from Linux PC using Network Manager

To get the client’s WireGuard configuration file, click the “Download” button on the respective profile. This action will download a file with the name you assigned to the account through WireGuard-UI and the extension “.conf.”

Download the client's WireGuard config file.
Download the client’s WireGuard config file.

Rename the file to a short and recognizable name without spaces or special characters in it. For example, “vpn-home.conf.” Then import it using nmcli, a NetworkManager CLI tool, by running the following:

nmcli connection import type wireguard file vpn-home.conf
Importing Network Manager WireGuard profile.
Importing Network Manager WireGuard profile.

If you now go and look at the available network connections on your Linux system, you will see that our new WireGuard VPN connection is added and ready to use.

Network Manager connections.
Network Manager connections.

Click on it to activate it.

The WireGuard VPN connection is up and running.
The WireGuard VPN connection is up and running.

Send a ping from your desktop (WireGuard client) machine to the WireGuard server’s private network address to ensure everything works properly.

ping -c 3 10.252.1.0
Test connectivity to the WireGuard VPN server.
Test connectivity to the WireGuard VPN server.

And this is where you have every reason to congratulate yourself because your WireGuard VPN tunnel works as expected!

If you return to the WureGuard-UI and select the “Status” tab, you should see your current active VPN connection there.

List the WireGuard's connected peers.
List the WireGuard’s connected peers.

Connecting to WireGuard from Mobile Device

Things are considerably easier when you need to connect your mobile device to the WireGuard VPN server. Then, you need to download the official WireGuard client for your operating system: iOS users should go to Apple’s App Store, while Android users should go to Google’s Play Store.

After installing the app on your mobile device, from the WireGuard-UI, click on the corresponding user’s “QR Code” button, which will display the QR code containing all of the information required to import the WireGuard’s VPN profile.

Preview WireGuard's QR profile code.
Preview WireGuard’s QR profile code.

Next, open the WireGuard app on your mobile device, choose “Add a tunnel,” then “Create from QR code,” scan the code from the monitor screen, and that’s it. The profile will be imported automatically.

Import WireGuard's VPN profile on a mobile device.
Import WireGuard’s VPN profile on a mobile device.

Then, switch on the profile, and your device will connect to the WireGuard VPN server.

Conclusion

With its modern cryptography, simplicity, and high-performance capabilities, WireGuard has quickly become a popular choice for VPN solutions. Learning how to set up your own WireGuard VPN server and easily manage it via the web-based WireGuard-UI using Docker Compose can benefit individuals and businesses.

First, this can improve your online security and privacy by adding an extra layer of protection when browsing the internet. On top of that, this way, you can also access geographically restricted content and protect your personal and confidential information from potential hackers and cybercriminals.

Setting up may initially seem daunting, but following our detailed guide makes the process smooth and hassle-free. Thank you for using it! Any opinions and comments are most welcome in the section below.

Bobby Borisov

Bobby Borisov

Bobby, an editor-in-chief at Linuxiac, is a Linux professional with over 20 years of experience. With a strong focus on Linux and open-source software, he has worked as a Senior Linux System Administrator, Software Developer, and DevOps Engineer for small and large multinational companies.

Think You're an Ubuntu Expert? Let's Find Out!

Put your knowledge to the test in our lightning-fast Ubuntu quiz!
Ten questions to challenge yourself to see if you're a Linux legend or just a penguin in the making.

1 / 10

Ubuntu is an ancient African word that means:

2 / 10

Who is the Ubuntu's founder?

3 / 10

What year was the first official Ubuntu release?

4 / 10

What does the Ubuntu logo symbolize?

5 / 10

What package format does Ubuntu use for installing software?

6 / 10

When are Ubuntu's LTS versions released?

7 / 10

What is Unity?

8 / 10

What are Ubuntu versions named after?

9 / 10

What's Ubuntu Core?

10 / 10

Which Ubuntu version is Snap introduced?

The average score is 68%

36 Comments

  1. Hi, I followed your guide and installed the server on an Azure Ubuntu virtual machine and it seems to be working. I downloaded the client configuration to my android phone with the official WireGuard app and it all works.
    Instead when I download the configuration of another client on my windows pc the connection does not work. Windows can go to the internet but not connect to the VPN.
    Do you have any idea of the solution?

  2. Oops, sorry, I forget to update the interface of my server in the IPTABLES rules UP/DOWN, It works ! Thanks for your tutorial

    • Thank you for this note! I thought the interface on these rules was referring to the container interface, and was having the same issue. We have to configure the actual server interface.

  3. Hello, I followed your tutorial, wireguard is working fine but I have no access to the gui. myipadress:5000 gives me ERR_CONNECTION_REFUSED
    any clue on what is wrong ?

  4. I would like to add the following log:

    wg-quick: `/etc/wireguard/wg0.conf’ does not exist

    I receive this log back even if the config folder mounted on the container is the same as the one of the wireguard container.

    • If this guide would better explain what volumes are supposed to be shared between the services trouble shooting would be much easier.
      Could you please add that information?

  5. I have solved it using named volumes like this. Tewh config above to me seems extremely ducttapish

    volumes:
    – Wdb:/app/db
    – Wconfig:/etc/wireguard
    – Wconfig2:/config

    volumes:
    Wconfig:
    Wconfig2:
    Wdb:

  6. It all works until you make the first client.
    After that the gui on port 5000 is not accessible anymore.

    the wireguard-ui container is listening on port tcp 5000 but somehow does not answer syn packets

    docker logs wireguard-ui
    Wireguard UI
    App Version : dev
    Git Commit : 6bbe230
    Git Ref : N/A
    Build Time : 07-14-2023 20:01:11
    Git Repo : https://github.com/ngoduykhanh/wireguard-ui
    Authentication : true
    Bind address : 0.0.0.0:5000
    Email from :
    Email from name : WireGuard UI
    Custom wg.conf :
    Base path : /
    ⇨ http server started on [::]:5000
    wg-quick: `/etc/wireguard/wg0.conf’ does not exist
    Warning: `/config/wg0.conf’ is world accessible
    [#] ip link add wg0 type wireguard
    [#] wg setconf wg0 /dev/fd/63
    [#] ip -4 address add 10.252.1.0/24 dev wg0
    [#] ip link set mtu 1450 up dev wg0
    [#] ip -4 route add 192.168.0.0/24 dev wg0
    [#] iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

    root@vpn2023 [ ~/containers/wireguard ]# docker exec -it wireguard-ui bash
    bash-5.1# netstat -ltup
    Active Internet connections (only servers)
    Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
    tcp 0 0 localhost:41889 0.0.0.0:* LISTEN –
    tcp 0 0 :::5000 :::* LISTEN 10/wg-ui
    udp 0 0 localhost:34118 0.0.0.0:* –
    udp 0 0 0.0.0.0:51820 0.0.0.0:* –
    udp 0 0 :::51820 :::* –
    bash-5.1#

  7. OK. the guide works as long as you don’t use named volumes. Named volumes breaks stuff in strange and unexpeted ways because of the scripting in the individual projects.
    Also if you want docker to restore after a reboot you need to add “restart:always” twice.

  8. Please help. it doesnt work for me. i can’t browse the internet.
    i downloaded wireguard app, scan the QR code, it appears on Status: Connected Peers but Received and Transmitted data is 0B.

    Please help

  9. Great Instructions, everything worked out fine. But one Question, how can I configure the Docker to be started automatically after Reboot of the RaspberryPi? At the Moment I always have to start the Docker Container manually with docker-compose up -d…

    • Hi Tobi,

      To get the containers to start automatically, you only have to add each service’s “restart: unless-stopped” directive in the YAML file. The guide has now been updated.

      Best,
      Bobby

  10. Hi Bobby,

    Thanks for your effort.

    I have a question regarding network configuration “network_mode: service:wireguard”. Wireguard-UI couldn’t access under this network configuration.

    Once separated network for Wireguard-UI, then I can access.

    May I have your suggestion?

    Thanks

  11. Is this documentation based on a ubuntu server installation or another linux dist? I’ve tried it with debian 12 and I’ve got problems with that documentation. Maybe because I only have docker version “Docker version 20.10.24+dfsg1, build 297e128”.

    • Hi,

      In practice, the distribution used has little to do with the WireGuard implementation itself, as it relies on Docker, making it kind of distro-agnostic. As for Docker itself, its current, up-to-date version is 24.0.7. If you’re using Debian 12, please follow this guide, “How to Install Docker on Debian 12 (Bookworm),” to get it. Of course, you need to remove the version of Docker you are currently using first.

      Best,
      Bobby

  12. Reposting because I didn’t put my email in above accidentally (sigh, long day).
    Hi there, followed all the instructions and I have WG and WG-UI setup. One thing though, while I can get my client to connect and authenticate, I cannot hit any resources locally. Doesn’t seem to matter what I do. I cannot ping the server (the IP assigned under server interface addresses), cannot ping any endpoints on my network. I do have allowed IPs to be the range that’s specified for the VPN + the IPs of my local network (192.168.1.0/24). I did setup the post up and down scripts, followed the instructions, but I’m pretty stuck. Any ideas?

  13. Attention! The tutorial is not correct due to the latest update of wiregurad projects form 03.10.23:

    Potentially Breaking Change: Support for multiple interfaces added. Wireguard confs moved to /config/wg_confs/. Any file with a .conf extension in that folder will be treated as a live tunnel config and will be attempted to start. If any of the tunnels fail, all tunnels will be stopped. Tunnels are started in alphabetical order. Managed server conf will continue to be hardcoded to wg0.conf.

    If you want follow the turtorial above change the image from linuxserver/wireguard:latest to linuxserver/wireguard:v1.0.20210914-ls6 (in the docker-compose.yaml file).

  14. Hey, I found, that dockers IP is 172.18.0.2 but
    I can’t connect with UI, i think because I didn’t understood last part,
    –> where I have to add “iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE” ?
    if I put it directly in my console, I have : bash: iptables: command not found (even if I know – newest version is installed)

    Thanks a lot for help.

    • You should be able to connect with the UI if you use the IP address of the machine hosting the docker container. Check the IP of the host where your docker container is running and browse to that IP, on port 5000 (e.g. http://192.168.1.5:5000, if 192.168.1.5 is the IP of the host where docker containers are running)

  15. Many thanks Bobby for this tutorial!
    In my case, I had manually include a static route ‘inside’ the wireguard container, pointing to the local LAN (e.g. 192.168.1.0/24) so that it would know how to get out. Connections from outside are only successful after configuring this route. Would you expect this?

  16. I use the Linux container LXC Debian 12. I installed everything according to the plan. Now when I want to log in to the wireguard-ui website, nothing happens. (IP: http://address_where docker_is_running:5000)
    I use several dockers and they all work, e.g. portainer drawio etc…

  17. when this doesnt work the fix is to add

    environment:
    – PUID=1000
    – PGID=1000
    – PEERS=1

    underneath

    cap_add:
    – NET_ADMIN

  18. Nice article! Docker on Openwrt on X86 (and Pi4), wouldn’t work. Errors per wg0 issues. Suspected the O’rt busybox config. Updated yaml (above) substituting Linuxserver’s recommend – via nano or Portainers ‘App Template’ editor. Add SYS_MODULE (mentioned in ngoduykhanh/wireguard-ui troubleshoot), UID and GID settings (per ‘derk’ above). Changed server port to choice. REM’d out ‘PEERS=1’ (you’ll do this later). Mapped internal to external ‘ports:’, per server port. UI looks to ‘server configs for GUI port (5000:5000) so retain. Start. Append iptables rules per recommend. Restart. Works fine…

Leave a Reply

Your email address will not be published. Required fields are marked *