WireGuard client as a Docker container using wireguard-go (userspace implementation). Requires no WireGuard kernel module — ideal for Synology NAS systems (DSM 7.x) whose kernel does not support WireGuard.
Tested on: Synology DS218+ (Intel Celeron J3355, DSM 7.3)
Synology DSM uses an older Linux kernel that does not include a WireGuard kernel module.
Classic WireGuard containers (e.g. linuxserver/wireguard) fail because of this.
This container uses wireguard-go, the official userspace implementation of WireGuard,
which works without a kernel module.
| Kernel WireGuard | wireguard-go (this container) | |
|---|---|---|
| Kernel module required | Yes | No |
CAP_NET_ADMIN required |
Yes | Yes |
SYS_MODULE required |
Yes | No |
| Performance | ~1 Gbit/s+ | ~200-400 Mbit/s |
| Synology compatible | No (without SPK) | Yes |
- Synology NAS with DSM 7.x
- Container Manager installed (via Package Center)
- A WireGuard client configuration file (
wireguard.conf) from your WireGuard server
The image must be built on a separate machine and then transferred to the Synology.
# Clone the repository
git clone https://github.com/CallMeTechie/docker-wireguard-go.git
cd docker-wireguard-go
# Build the image
docker build -t wireguard-go:latest .
# Export as .tar
docker save wireguard-go:latest -o wireguard-go.tarCopy the file wireguard-go.tar (~8.5 MB) to your Synology (e.g. via SMB share or Synology Drive).
- Open Container Manager
Image>Add>Add from file- Select the
wireguard-go.tarfile - Wait for the import to complete
Create the following folders in File Station:
docker > wireguard-go > config
Resulting structure:
/volume1/docker/wireguard-go/
├── docker-compose.yml
└── config/
└── wireguard.conf <-- Your WireGuard client configuration
Copy your client configuration file via File Station to docker/wireguard-go/config/wireguard.conf.
Example of a typical client configuration:
[Interface]
PrivateKey = <YOUR_PRIVATE_KEY>
Address = 10.8.0.3/32
DNS = 1.1.1.1,8.8.8.8
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
PresharedKey = <OPTIONAL_PRESHARED_KEY>
Endpoint = <SERVER_IP>:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25The startup script processes wg-quick directives (Address, DNS, etc.) automatically.
The file can be used directly as generated by common WireGuard managers (GateControl, wg-easy, Pi-VPN, etc.).
Create the file docker/wireguard-go/docker-compose.yml via File Station with the following content:
version: '3.9'
services:
wireguard:
image: wireguard-go:latest
container_name: wireguard-client
cap_add:
- NET_ADMIN
volumes:
- ./config:/config
network_mode: host
restart: always- Open Container Manager
Project>Create- Project name:
wireguard-go - Path:
/volume1/docker/wireguard-go - Select
Use existing docker-compose.yml - Click
Next>Create
The container starts automatically.
In Container Manager under Container > wireguard-client > Terminal:
wg show
Expected output:
interface: wg0
public key: <PUBLIC_KEY>
private key: (hidden)
listening port: <PORT>
peer: <SERVER_PUBLIC_KEY>
endpoint: <SERVER_IP>:51820
allowed ips: 10.8.0.0/24
latest handshake: X seconds ago
transfer: X.XX KiB received, X.XX KiB sent
persistent keepalive: every 25 seconds
To test the connection in the terminal:
ping -c 3 10.8.0.1
| Variable | Default | Description |
|---|---|---|
IP_WG_ENV |
(from config) | VPN IP address, automatically read from Address in wireguard.conf. Only needed if no Address field is present in the config. |
| Container path | Description |
|---|---|
/config |
Configuration directory. Must contain wireguard.conf. |
| File | Description |
|---|---|
wireguard.conf |
(Required) WireGuard client configuration |
iptables.sh |
(Optional) Custom iptables rules executed at startup |
The container runs with network_mode: host. This means:
- The
wg0interface is created directly on the NAS - All services on the NAS are accessible through the tunnel
- No separate port mapping required
The startup script handles AllowedIPs intelligently:
AllowedIPs = 0.0.0.0/0: Instead of redirecting all traffic (which would break NAS networking), only a route for the VPN subnet (e.g.10.8.0.0/24) is created.AllowedIPs = 10.8.0.0/24: Route is applied directly as specified.- Multiple entries: Each entry is added as a separate route.
Check the logs in Container Manager under Container > wireguard-client > Log.
| Error message | Cause | Solution |
|---|---|---|
wireguard.conf file is missing |
No config file found | Copy wireguard.conf to the config/ folder |
wireguard-go binary not found |
Image problem | Rebuild and reimport the image |
wireguard-go failed to initialize |
TUN device cannot be created | Container needs CAP_NET_ADMIN (check docker-compose.yml) |
In Container Manager under Container > wireguard-client > Terminal:
ip route | grep 10.8
Expected output: 10.8.0.0/24 dev wg0 scope link
If the route is missing, check that AllowedIPs is set correctly in wireguard.conf.
In Container Manager under Container > wireguard-client > Terminal:
wg show wg0
ip addr show wg0
Make sure that:
- The
Addressin the config matches the peer configuration on the server - The server has the peer configured with the correct
AllowedIPs
- Import new image in Container Manager:
Image>Add>Add from file - Stop the project:
Project>wireguard-go>Stop - Recreate the project:
Project>wireguard-go>Create(to use the new image)
Tested with iperf3 (original benchmark from upstream project):
| Connection | Speed |
|---|---|
| Direct (no VPN) | 9.42 Gbit/s |
| Boringtun v0.6.0 | 1.51 Gbit/s |
| wireguard-go | 2.92 Gbit/s |
For typical NAS use cases (file access, reverse proxy) the performance is more than sufficient.
- Base image: Alpine Linux (latest)
- WireGuard implementation: wireguard-go (official userspace implementation)
- Included tools:
wg,wg-quick,iproute2,iptables - Image size: ~8.5 MB (compressed)
- Multi-stage build: Go compiler only in build step, not in final image
- Capabilities: Only
NET_ADMINrequired (noSYS_MODULE, no--privileged)
See LICENSE.
Originally based on matthewchng/docker-wireguard-go.
Extended with automatic processing of wg-quick configurations and intelligent routing.