How to Run WireGuard via LinuxServer.io’s Docker Image

What fun is it to have a relatively sophisticated homelab, but only be able to access it at home? Sometimes there’s a need — or simply a desire — to access the homelab from the office. That’s where a VPN solution, like WireGuard, comes in.

This is my twelfth post documenting images I use at home. You can also read about how I run the Unifi controller, how I run Plex, how I update DuckDNS, how I run Duplicacy., how I run Heimdall, how I run Librespeed, how I run Home Assistant, how I run NetBox, how I run Scrutiny, how I run OpenVSCode Server, and how I run QDirStat.

About WireGuard

WireGuard is a “fast, modern, secure VPN tunnel” solution. It can facilitate accessing a private network, such as a home network, from offsite, without giving away information to others who might be snooping on the network.

About LinuxServer.io

LinuxServer.io describes their organization as:

A group of like-minded enthusiasts from across the world who build and maintain the largest collection of Docker images on the web. At our core are the principles behind Free and Open Source Software. Our primary goal is to provide easy-to-use and streamlined Docker images with clear and concise documentation.

I’ve used LinuxServer.io images for several years. That’s because they’re secure, thoughtful images that are clearly and concisely documented. I tend to check with LinuxServer.io first when I need a new image.

Running via docker-compose

I run all of my containers via docker-copmose. Here are the relevant sections of my docker-compose.yml file:


networks:
  wireguard:
    name: wireguard

services:
  wireguard:
    container_name: wireguard
    image: lscr.io/linuxserver/wireguard:v1.0.20210914-ls75
    restart: unless-stopped

    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    env_file:
      - ./common.env
      - ./secret.env
    environment:
      - SERVERURL=example.com
      - SERVERPORT=51820
      - PEERS=mattLaptop,mattPhone
    networks:
      - wireguard
    ports:
      - 51820:51820/udp
    volumes:
      - /lib/modules:/lib/modules
      - ${SERVICE_DATA_DIR}/wireguard:/config

First, I create an explicitly-named network for WireGuard to use. This helps ensure services are isolated and avoids the automatic names docker-compose generates. The names aren’t bad, but I like the explicit names better.

Then, the first stanza of the services key specifies the basic container configuration: a name, image to use, and restart policy.

Next, the cap_add section grants two container capabilities that WireGuard needs to function effectively with the operating system’s networking layer.

The env_file section brings in environment variables used commonly across my containers. This includes the PUID and PGID variables for setting permissions, as well as the SERVICE_DATA_DIR variable used elsewhere in the file.

I then set three environment variables following LinuxServer’s Parameters instructions. SERVERURL and SERVERPORT specify, well, the URL and port WireGuard clients should connect to. I’ve obfuscated my SERVERURL here, but in reality, it points at my home’s IP address by way of a DuckDNS URL.

PEERS specifies which peers to create client configurations for. In my case, that’s my laptop and phone. If I wanted to add a configuration for a friend, I’d add their name here. Then, the container will automatically create WireGuard configuration files for them.

In terms of port mappings: there is only one here. In this case, it maps the 51820 UDP port externally to the 51820 port internally. If you’d prefer a different external port, you could change it here. It does need to be a UDP port since that is what WireGuard uses.

Lastly, I have two volume mappings: one for /lib/modules the image needs access to, and one for the directory the image uses for saving persistent configuration. In the latter, I use an environment variable SERVICE_DATA_DIR to specify where my persistent configuration lives. The environment variable is helpful in that it cuts down on a lot of duplication in my broader docker-compose.yml file.

Port Forwarding to This Container

Before booting a container, one more bit of configuration is required. I need to have my router/firewall forward UDP port 51820 to the system running this container. By default, of course, routers/firewalls block all unsolicited incoming traffic. Creating this port forwarding rule will make an explicit exception for incoming WireGuard traffic, and thereby allow a connection.

Final Setup Step

This container is now configured and ready to run via docker-compose up. Upon first boot, the container will generate the peer configuration files. I then copy the appropriate configuration from the ${SERVICE_DATA_DIR}/wireguard to my device, like my laptop, and import the configuration into the WireGuard application.

After the configuration has been imported locally, I can now connect to the WireGuard container and communicate with other systems within my home. The VPN connection is now established.

WireGuard – a Great VPN Solution

I’ve had great success using WireGuard in this fashion for the last year or so. It works as advertised — quick, easy to set up, and effective. Thanks to the WireGuard and LinuxServer.io teams for making this tool so readily available!