English | 简体中文 | 繁體中文 | Русский
A Docker image to run a WireGuard VPN server. Based on Alpine Linux with WireGuard. Designed to be simple, modern, and maintainable.
- Automatically generates server keys and a client config on first start
- Client management via a helper script (
wg_manage) - QR code displayed on first start for easy mobile setup
- Supports kernel WireGuard (5.6+) with automatic fallback to
wireguard-go(userspace) - IPv6 support when the server has a public IPv6 address (see requirements)
- Persistent data via a Docker volume
- Multi-arch:
linux/amd64,linux/arm64,linux/arm/v7
Also available: Docker images for OpenVPN, IPsec VPN, and Headscale.
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-serverOn 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 wireguardScan 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.
- A Linux server with a public IP address or DNS name
- Docker installed
- WireGuard UDP port open in your firewall (UDP 51820 by default, or your configured port)
- Host kernel 5.6+ with WireGuard support (recommended), or use the built-in
wireguard-gouserspace fallback (works on any kernel)
Get the trusted build from the Docker Hub registry:
docker pull hwdsl2/wireguard-serverAlternatively, you may download from Quay.io:
docker pull quay.io/hwdsl2/wireguard-server
docker image tag quay.io/hwdsl2/wireguard-server hwdsl2/wireguard-serverSupported platforms: linux/amd64, linux/arm64 and linux/arm/v7.
To update the Docker image and container, first download the latest version:
docker pull hwdsl2/wireguard-serverIf 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.
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, DO NOT put "" or '' around values, or add space 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 \
--env-file ./vpn.env \
--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-serverUse 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 aliceExport a client config (prints to stdout):
docker exec wireguard wg_manage --showclientcfg alice > alice.confList clients:
docker exec wireguard wg_manage --listclientsRemove 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 -yThis 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-gouserspace (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 wireguardThe --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:
- The Docker host must have a routable global unicast IPv6 address (starting with
2or3). Link-local (fe80::/10) addresses are not sufficient. - IPv6 must be enabled for the Docker container. See Enable IPv6 support in Docker.
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- Base image:
alpine:3.23 - WireGuard: latest
wireguard-toolsfrom Alpine packages - Userspace fallback:
wireguard-gofrom Alpine packages - VPN subnet:
10.7.0.0/24(server:10.7.0.1, clients:10.7.0.2+) - IPv6 VPN subnet:
fddd:2c4:2c4:2c4::/64(when server has IPv6) - Preshared keys: generated per client for additional post-quantum resistance
- Default keepalive: 25 seconds (ensures NAT traversal for mobile clients)
- Cipher: ChaCha20-Poly1305 (WireGuard default, not configurable)
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 Creative Commons Attribution-ShareAlike 4.0 International License.
This project is based in part on the work of Nyr and contributors, licensed under the MIT License.
