Introduction
This DIY guide explains WireGuard on Docker with a practical setup path, validation steps, and the details needed to build it safely.
Introduction
If you want to create a secure, private network connection using WireGuard but prefer managing it with Docker Compose, this guide is for you. We’ll walk through building a WireGuard VPN server stack that runs inside Docker containers, orchestrated by Docker Compose. This approach is popular for developers and sysadmins who want an isolated, reproducible VPN environment that’s easy to deploy and update.
You don’t need to be a networking expert to follow along, but some familiarity with Docker basics will help. We’ll start with a high-level explanation of what you’re building, the tools involved, and then move into detailed setup and configuration steps. By the end, you’ll have a working WireGuard VPN server running in Docker, ready to connect your devices securely.
What You Are Building
This setup uses three main components:
- WireGuard VPN server: The core VPN service that encrypts and routes your network traffic.
- Docker container: A lightweight, isolated environment that runs the WireGuard server.
- Docker Compose: A tool to define and manage multi-container Docker applications using a simple YAML file.
By combining these, you get a portable WireGuard server that can be started, stopped, and configured easily. The server handles encrypted tunnels using WireGuard’s modern cryptographic protocols, while Docker Compose manages the container lifecycle and networking.
This stack is ideal if you want a VPN server that runs on your own hardware or cloud instance with minimal fuss, supports easy updates, and keeps your VPN service isolated from other system processes.
Prerequisites
Before starting, ensure you have:
- A Linux server or VM with Docker and Docker Compose installed. (Ubuntu 20.04+ or similar is recommended.)
- Basic command line knowledge.
- Root or sudo privileges on the server.
- Familiarity with editing text files in Linux.
- A public IP address or domain name pointing to your server if you want remote access.
- WireGuard client devices ready to connect (e.g., laptops, phones).
You should also have Docker Compose version 1.27 or newer for compatibility.
Step-by-Step Setup
1. Create a Project Directory
Start by creating a directory to hold your Docker Compose files and WireGuard configuration:
mkdir wireguard-docker
cd wireguard-docker
2. Write the docker-compose.yml
Create a docker-compose.yml file describing the WireGuard container:
version: "3.8"
services:
wireguard:
image: linuxserver/wireguard
container_name: wireguard
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
- SERVERURL=your.domain.or.ip
- SERVERPORT=51820
- PEERS=1
- PEERDNS=auto
- INTERNAL_SUBNET=10.13.13.0
volumes:
- ./config:/config
- /lib/modules:/lib/modules
ports:
- 51820:51820/udp
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped
Replace your.domain.or.ip with your server’s public IP or domain name.
3. Launch the WireGuard Container
Run the following command to start the container:
docker-compose up -d
The container will initialize WireGuard, generate keys, and create configuration files inside the ./config directory.
4. Retrieve Client Configuration
Inside ./config/peerX/peerX.conf (where X is the peer number), you will find the WireGuard client configuration file. Transfer this file securely to your client device and import it into your WireGuard client app.
5. Connect Your Client
Use the WireGuard client on your device to activate the VPN connection using the imported config. You should now be connected securely to your WireGuard server running inside Docker.
Configuration Details
The docker-compose.yml sets several important parameters:
- PUID/PGID: User and group IDs to run the container processes, matching your host user for file permission consistency.
- SERVERURL: The public IP or domain clients use to connect.
- SERVERPORT: UDP port WireGuard listens on (default 51820).
- PEERS: Number of client configurations to generate automatically.
- INTERNAL_SUBNET: The VPN subnet used for client IPs.
- Volumes: Persist configuration and kernel modules for WireGuard.
- Capabilities:
NET_ADMINandSYS_MODULEare required for network and kernel module operations.
WireGuard uses modern cryptography (Curve25519 for key exchange, ChaCha20-Poly1305 for encryption) to secure traffic. The container handles the control plane (key exchange, authentication) and data plane (encrypted packet forwarding) transparently.
Validation and Testing
After starting the container and connecting a client:
- Check container logs for errors:
docker logs wireguard
- Verify WireGuard interface is up inside the container:
docker exec wireguard wg show
- On the client, confirm you have an IP address from the VPN subnet and can ping the server’s internal VPN IP (usually
10.13.13.1).
- Test internet access and routing through the VPN if configured.
If connectivity fails, check firewall rules on the host to ensure UDP port 51820 is open.
Common Mistakes
- Incorrect SERVERURL: Using a local IP instead of a public IP or domain will prevent clients from connecting remotely.
- Firewall blocking UDP port: WireGuard requires UDP traffic; ensure your firewall allows inbound UDP 51820.
- Volume permissions: The container must have read/write access to the
./configdirectory. - Missing kernel modules: WireGuard requires kernel support; mapping
/lib/moduleshelps but verify your host supports WireGuard. - Not restarting container after config changes: Some config updates require container restart to take effect.
Hardening Tips
- Use a non-default port to reduce automated scanning.
- Limit the number of peers and remove unused configs.
- Regularly update the Docker image for security patches.
- Enable logging and monitoring on the WireGuard interface.
- Use firewall rules to restrict access to the WireGuard port.
- Consider running the container with minimal privileges and resource limits.
Related Reading
Related protocol articles:
- WireGuard Cryptography Explained
- WireGuard Protocol Deep Dive
- WireGuard vs OpenVPN Performance Benchmark
Troubleshooting articles:
Foundational article:
Conclusion
Running WireGuard inside Docker Compose combines the security and speed of WireGuard with the convenience and isolation of containers. This setup is ideal for advanced users who want a portable VPN server that’s easy to deploy, update, and manage.
By following this guide, you’ve built a WireGuard VPN server stack that can be extended with multiple clients, customized routing, and integrated into larger Docker-based infrastructure.
For further learning, explore WireGuard’s cryptographic foundations, protocol details, and performance tuning to optimize your VPN deployment.
Diagram: WireGuard Docker Compose Architecture and Traffic Flow
graph LR Client[WireGuard Client Device] Internet[Internet] Host[Docker Host] Container[WireGuard Docker Container] VPNSubnet[VPN Internal Subnet (10.13.13.0/24)] Client -- Encrypted UDP Traffic --> Internet Internet -- UDP 51820 --> Host Host -- Forwards UDP 51820 --> Container Container -- Decrypts & Routes --> VPNSubnet VPNSubnet -- Replies --> Container Container -- Encrypts & Sends --> Host Host -- Sends --> Internet Internet -- Sends --> Client
This diagram shows how encrypted traffic flows from your client device over the internet to your Docker host, into the WireGuard container, and then routes through the VPN subnet.
References
- RFC 4301: Security Architecture for IP
- RFC 7296: Internet Key Exchange Protocol Version 2
- RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3
- RFC 8439: ChaCha20 and Poly1305 for IETF Protocols
- NIST SP 800-207: Zero Trust Architecture
- WireGuard Protocol Overview
- Docker Compose File Reference