An Ansible role to deploy a full WireGuard VPN server on Debian-based hosts. This role is designed for on-premises, self-hosted deployments. It is idempotent and can safely be re-run at any time.
Stack components:
- WireGuard kernel module and user tools
- SystemD for service management
- UFW firewall integration
- Optional support for vault-managed private keys
- NAT / IP forwarding configuration
- Configurable peers via Ansible variables
The role aims for simplicity, security, and maintainability - no manual configuration steps required after initial deployment.
- Debian 12+ (Bookworm) or compatible.
- Sudo access.
- Ports open on UDP
{{ wg_vpn_port }}(default 51820).
- Installs WireGuard package.
- Ensures
/etc/wireguarddirectory exists with proper permissions. - Checks for existing private key file.
- Generates a new key pair if no key exists and no vault-provided key is defined.
- Writes vault-provided private key if defined.
- Enables IPv4 forwarding in the kernel and UFW.
- Configures UFW NAT postrouting and routing rules.
- Enables incoming VPN traffic on the configured UDP port.
- Reads the private key and deploys a WireGuard configuration from a Jinja2 template.
- Enables and starts the WireGuard service via SystemD.
The following variables must be defined, typically in group_vars or your inventory:
| Variable | Description |
|---|---|
wg_vpn_network | VPN network/subnet for WireGuard (e.g. 10.200.200.1/24). |
wg_vpn_interface | WireGuard interface name (default: wg0). |
wg_vpn_port | UDP port WireGuard listens on (default: 51820). |
wg_network_interface | Physical interface for NAT (e.g. enp1s0). |
vault_wg_private_key | Optional SOPS/vault-provided server private key. |
wg_peers | Dictionary of peers with keys public_key and allowed_ips. |
Example peer definition:
wg_peers:
iron:
public_key: e2V40zdPiX43lqOamcoEI8J10uKaXWBeKwf+spWDWgc=
allowed_ips: 10.200.200.2/32- name: Deploy WireGuard VPN server
hosts: vpn-servers
become: true
roles:
- role: ds-wireguard
vars:
wg_vpn_network: 10.200.200.1/24
wg_vpn_interface: wg0
wg_vpn_port: 51820
wg_network_interface: enp1s0
vault_wg_private_key: "{{ vault_wg_private_key }}" # Optional
wg_peers:
iron:
public_key: e2V40zdPiX43lqOamcoEI8J10uKaXWBeKwf+spWDWgc=
allowed_ips: 10.200.200.2/32The role is fully idempotent.
Re-running it will:
- Skip key generation if a private key exists.
- Skip firewall rules if already applied.
- Update WireGuard configuration only if variables or template change.
- Restart WireGuard only when the configuration changes.
- Ensure the physical network interface for NAT (
wg_network_interface) is correct. - UFW must be installed and enabled on the host.
- Vault-managed keys provide deterministic server identity across deployments.
- Peer definitions automatically generate the WireGuard [Peer] blocks in the configuration.
- IPv4 forwarding must be enabled for routing between VPN and LAN.
Restart_WireGuardRestart_UFW
MIT License
[ Fear the Silence. Fear the Switch. ]