A Docker-based WireGuard VPN server for restricting access to a backend server. Clients connect via WireGuard and can only access specific services - they cannot communicate with each other or access anything outside the allowed subnet.
You have a backend server you want to restrict access to. Instead of opening ports to the internet:
- Deploy this Docker container on your host
- Create WireGuard peers for each person who needs access
- Each peer lives on subnet
10.100.128.0/17and can only reach your backend server on10.100.0.0/24 - Peers cannot communicate with each other or access anything outside their allowed network
- Docker host: Modern Debian-based system (recommended for simplicity)
- Only tested with Debian as a Docker host
- Requires kernel with WireGuard support
- Docker runtime with capability to run privileged containers
- Environment:
API_KEYandPUBLIC_HOSTvariables (required),ALLOWED_TARGET_SUBNET(optional)
Create a .env file with your configuration:
# .env
# Generate with: openssl rand -hex 32
API_KEY=
# IP or hostname that can reach the server
PUBLIC_HOST=
# (Optional) Subnet that WireGuard clients can access
# Default: 10.100.0.0/24
ALLOWED_TARGET_SUBNET=10.100.0.0/24Create docker-compose.yml:
services:
wireguard:
image: ghcr.io/wgsvr:main
restart: unless-stopped
environment:
- TZ=Asia/Bangkok
- API_KEY=${API_KEY:?"Please set the API_KEY environment variable"}
- PUBLIC_HOST=${PUBLIC_HOST:?"Please set the PUBLIC_HOST environment variable"}
- ALLOWED_TARGET_SUBNET
volumes:
- etc_wireguard:/etc/wireguard
- /lib/modules:/lib/modules:ro
cap_add:
- NET_ADMIN
- SYS_MODULE
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
ports:
- "22111:22111/tcp"
- "51820:51820/udp"
networks:
mynet:
ipv4_address: 10.100.0.2
volumes:
etc_wireguard:
networks:
mynet:
driver: bridge
ipam:
config:
- subnet: 10.100.0.0/24
gateway: 10.100.0.250docker build -t wgsvr .
docker compose up -dOn first run, the container generates a WireGuard keypair and saves it to the etc_wireguard volume. This persists across restarts.
All API calls require the x-api-key: $API_KEY header.
curl -H "x-api-key: $API_KEY" \
http://localhost:22111/api/configReturns the current /etc/wireguard/wg0.conf file.
curl -H "x-api-key: $API_KEY" \
"http://localhost:22111/api/generatePeerConfig?ip=10.100.128.10&clientName=alice"Returns:
{
"serverConfig": "[Peer]\n# alice\nPublicKey = ...\n...",
"clientConfig": "[Interface]\nPrivateKey = ...\n..."
}This generates a new keypair but does not save it. You need to manually add the server config to the WireGuard configuration.
Append the serverConfig from step 2 to the configuration, then update the server:
curl -X PUT -H "x-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"config": "<entire updated wg0.conf content>"}' \
http://localhost:22111/api/configThe new peer is live immediately.
curl -H "x-api-key: $API_KEY" \
http://localhost:22111/api/statsShows WireGuard interface statistics including transferred bytes per peer.
-
WireGuard peers:
10.100.128.0/17subnet- Server interface IP:
10.100.128.1 - Each peer assigned a
/32address (e.g.,10.100.128.10) - Example:
alicemight be10.100.128.10
- Server interface IP:
-
Backend services: Configurable subnet (default
10.100.0.0/24)- Controlled by
ALLOWED_TARGET_SUBNETenvironment variable - Clients can reach anything on this subnet
- Cannot reach anything outside this range
- Cannot reach each other
- Controlled by
-
WireGuard port:
51820/udp(mapped from container) -
API port:
22111/tcp(mapped from container)
- Generate peer configuration via API/CLI
- Assign an unused IP from
10.100.128.0/17(excluding10.100.128.1) - Add server config section to WireGuard configuration
- Apply configuration via API
- Share client config with the peer (contains their private key, server public key, etc.)
- Peer isolation: Clients cannot communicate with each other (firewall rules block peer-to-peer traffic)
- Network restriction: Clients can only access services on the configured subnet (default
10.100.0.0/24, customizable viaALLOWED_TARGET_SUBNET) - API authentication: All management API calls require
x-api-keyheader - Key persistence: Server keypair persists across restarts via volume mount
All endpoints require x-api-key header.
| Method | Endpoint | Description |
|---|---|---|
| GET | / |
Health check |
| GET | /api/config |
Retrieve current WireGuard configuration |
| PUT | /api/config |
Update WireGuard configuration |
| GET | /api/stats |
Display interface statistics and peer usage |
| GET | /api/generatePeerConfig |
Generate new peer keypair and config |
ip(required) - Peer IP address (must be in10.100.128.0/17)clientName(required) - Name for the peer (used in comments)publicHost(optional) - Override the public hostname (defaults to$PUBLIC_HOST)