Docker image to run a WireGuard VPN server, with simple setup and client management.
709
Docker image to run a WireGuard VPN server. Based on Alpine Linux with WireGuard. Designed to be simple, modern, and maintainable.
Features:
wg_manage)wireguard-go (userspace)linux/amd64, linux/arm64, linux/arm/v7Also available:
Step 1. Start the WireGuard server:
docker run \
--name wireguard \
--restart=always \
-v wireguard-data:/etc/wireguard \
-p 51820:51820/udp \
-d --cap-add=NET_ADMIN \
--cap-add=SYS_MODULE \
--device=/dev/net/tun \
--sysctl net.ipv4.ip_forward=1 \
--sysctl net.ipv6.conf.all.forwarding=1 \
hwdsl2/wireguard-server
On first start, the server automatically generates server keys, a wg0.conf, and a client configuration named client.conf. A QR code is also printed to the container logs for easy mobile setup.
Step 2. View the container logs to get the client QR code:
docker logs wireguard
Scan the QR code with the WireGuard app on your phone to connect instantly.
Step 3. Optionally, copy the client configuration to your local machine:
docker cp wireguard:/etc/wireguard/clients/client.conf .
Import client.conf into any WireGuard client to connect.
Alternatively, you may set up WireGuard VPN without Docker. To learn more about how to use this image, read the sections below.
wireguard-go userspace fallback (works on any kernel)Get the trusted build from the Docker Hub registry:
docker pull hwdsl2/wireguard-server
Alternatively, you may download from Quay.io:
docker pull quay.io/hwdsl2/wireguard-server
docker image tag quay.io/hwdsl2/wireguard-server hwdsl2/wireguard-server
Supported platforms: linux/amd64, linux/arm64 and linux/arm/v7.
All variables are optional. If not set, secure defaults are used automatically.
This Docker image uses the following variables, that can be declared in an env file (see example):
| Variable | Description | Default |
|---|---|---|
VPN_DNS_NAME | Fully qualified domain name (FQDN) of the server | Auto-detected public IP |
VPN_PUBLIC_IP | Public IPv4 address of the server | Auto-detected |
VPN_PUBLIC_IP6 | Public IPv6 address of the server | Auto-detected |
VPN_PORT | WireGuard UDP port (1–65535) | 51820 |
VPN_CLIENT_NAME | Name of the first client config generated | client |
VPN_DNS_SRV1 | Primary DNS server pushed to clients | 8.8.8.8 |
VPN_DNS_SRV2 | Secondary DNS server pushed to clients | 8.8.4.4 |
Note: In your env file, you may enclose values in single quotes, e.g. VAR='value'. Do not add spaces around =. If you change VPN_PORT, update the -p flag in the docker run command accordingly.
Example using an env file:
docker run \
--name wireguard \
--restart=always \
-v wireguard-data:/etc/wireguard \
-v ./vpn.env:/vpn.env:ro \
-p 51820:51820/udp \
-d --cap-add=NET_ADMIN \
--cap-add=SYS_MODULE \
--device=/dev/net/tun \
--sysctl net.ipv4.ip_forward=1 \
--sysctl net.ipv6.conf.all.forwarding=1 \
hwdsl2/wireguard-server
The env file is bind-mounted into the container, so changes are picked up on every restart without recreating the container.
Use docker exec to manage clients with the wg_manage helper script.
Add a new client:
docker exec wireguard wg_manage --addclient alice
docker cp wireguard:/etc/wireguard/clients/alice.conf .
Show QR code for a client (e.g. after restart):
docker exec wireguard wg_manage --showclientqr alice
Export a client config (prints to stdout):
docker exec wireguard wg_manage --showclientcfg alice > alice.conf
List clients:
docker exec wireguard wg_manage --listclients
Remove a client (will prompt for confirmation):
docker exec -it wireguard wg_manage --removeclient alice
# Or remove without confirmation prompt:
docker exec wireguard wg_manage --removeclient alice -y
This image supports two WireGuard backends, selected automatically at startup:
Kernel module (preferred) — available on Linux kernel 5.6+ (Ubuntu 20.04+, Debian 11+, and most modern distributions). Provides the best performance.
wireguard-go userspace (automatic fallback) — used when the kernel module is unavailable. Works on any host kernel. Performance is slightly lower, but fully functional.
When wireguard-go is active, a note is printed to the container logs. No extra configuration is needed — the fallback is transparent.
To use the kernel module, ensure it is available on the host:
# Check if the module is loaded on the host
lsmod | grep wireguard
# Load it if needed
sudo modprobe wireguard
The --cap-add=SYS_MODULE flag in the docker run command allows the container to load the kernel module. If the module is already loaded on the host, SYS_MODULE is not strictly required.
All server and client data is stored in the Docker volume (/etc/wireguard inside the container):
/etc/wireguard/
├── wg0.conf # WireGuard server configuration (interface + all clients)
└── clients/
├── client.conf # First client config
└── alice.conf # Additional clients
Back up the Docker volume to preserve your server keys and all client configurations.
If the Docker host has a public (global unicast) IPv6 address and the requirements below are met, IPv6 support is automatically enabled when the container starts. No manual configuration is needed.
Requirements:
2 or 3). Link-local (fe80::/10) addresses are not sufficient.To enable IPv6 for the Docker container, first enable IPv6 in the Docker daemon by adding the following to /etc/docker/daemon.json on the Docker host, then restart Docker:
{
"ipv6": true,
"fixed-cidr-v6": "fddd:1::/64"
}
After that, re-create the Docker container. To verify that IPv6 is working, connect to the VPN and check your IPv6 address, e.g. using test-ipv6.com.
cp vpn.env.example vpn.env
# Edit vpn.env if needed, then:
docker compose up -d
docker logs wireguard
Example docker-compose.yml (already included):
services:
wireguard:
image: hwdsl2/wireguard-server
container_name: wireguard
restart: always
ports:
- "51820:51820/udp"
volumes:
- wireguard-data:/etc/wireguard
- ./vpn.env:/vpn.env:ro
cap_add:
- NET_ADMIN
- SYS_MODULE
devices:
- /dev/net/tun:/dev/net/tun
sysctls:
- net.ipv4.ip_forward=1
- net.ipv6.conf.all.forwarding=1
volumes:
wireguard-data:
To update the Docker image and container, first download the latest version:
docker pull hwdsl2/wireguard-server
If the Docker image is already up to date, you should see:
Status: Image is up to date for hwdsl2/wireguard-server:latest
Otherwise, it will download the latest version. Remove and re-create the container using instructions from Quick start. Your data is preserved in the wireguard-data volume.
alpine:3.23wireguard-tools from Alpine packageswireguard-go from Alpine packages10.7.0.0/24 (server: 10.7.0.1, clients: 10.7.0.2+)fddd:2c4:2c4:2c4::/64 (when server has IPv6)Note: The software components inside the pre-built image (such as WireGuard tools and wireguard-go) are under the respective licenses chosen by their respective copyright holders. As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within.
Copyright (C) 2026 Lin Song
This work is licensed under the MIT License.
This project is based in part on the work of Nyr and contributors, licensed under the MIT License.
Content type
Image
Digest
sha256:22bbfc207…
Size
11.5 MB
Last updated
2 days ago
Requires Docker Desktop 4.37.1 or later.