ansible-role-wireguard builds a fully meshed VPN across your cluster nodes
Ansible role for installing WireGuard VPN. Supports Ubuntu, Debian, Archlinx, Fedora, openSUSE Leap and some Redhat ES variants.
At a glance
- What is it?
- The githubixx WireGuard Ansible role installs and configures WireGuard on Linux, generates a wg0.conf on macOS instead of managing a service, and supports a wide list of distributions with a best effort tier behind them. Version 19.0.0 changed how an empty endpoint is treated, which is the kind of detail that decides whether an upgrade is safe.
- Who is it for?
- This role suits a platform engineer who wants WireGuard configured the same way across a mixed fleet of Linux distributions and wants the same file rendered on macOS, and it does not suit a setup where each node needs a different topology or where the role should also harden the host, since firewalling and IP forwarding are deliberately left out.
- Can I use it commercially?
- Not without permission. GitHub finds no licence file in the repository, and without a licence all rights are reserved by default: you may read the code but not reuse it. Check the README, or ask the authors, before using it.
- Is it still maintained?
- Yes. The repository last received commits 130 days ago.
- What is it written in?
- Mainly Jinja, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
A role that came out of a Kubernetes blog series
The role was written for a blog series, Kubernetes the not so hard way with Ansible, and the README says plainly that it can also be used standalone. The use case that produced it is a fully meshed VPN between all the nodes of a small Kubernetes cluster.
That origin explains the design. A mesh means every node is a peer of every other node, so the configuration is symmetric and the interesting part is the rendering of the same peer list into a wg0.conf on each host. The role is written in Jinja, and the template layer is where most of its behaviour lives.
The background on WireGuard itself is brief: it is a network tunnel for IPv4 and IPv6 that uses UDP. The README assumes you know that and links to a general introduction rather than explaining the protocol.
The homepage of the project is the blog post that introduced it, which is worth knowing before you start, because it means the documented narrative and the role are the same artefact. A role that is derived from a worked example is easier to understand than a role that grew by accretion, and the variables in the defaults below read that way, each one with a comment stating what it does.
A long support list with a best effort tier behind it
The list of supported operating systems is unusually long for a role, and it is split into two tiers that are easy to skim past.
The first tier is Linux distributions the role should work with: Ubuntu 22.04, 24.04 and 26.04, Archlinux, Debian 12 and 13, Fedora 42 and 43, AlmaLinux 9 and 10, Rocky Linux 9 and 10, openSUSE Leap 15.6 and 16.0, and Oracle Linux 9. The second tier is Linux, best effort: Debian 11, AlmaLinux 8, Rocky Linux 8 and elementary OS 6.
The distinction is not cosmetic. A supported distribution is one the maintainer expects to work, and the best effort tier is one that may work but is not tested. The changelog shows the list being actively pruned and extended, with version 19.1.0 removing EL 7 and 8, Fedora 39 and 40 and Ubuntu 20.04 from meta/main.yml while adding Debian 13 and Fedora 43, and version 19.2.0 adding support for Ubuntu 26.04 together with Ubuntu 26.04 test VMs in the Molecule scenarios.
That last detail is the interesting one. Ubuntu 26.04 support did not arrive as a version bump alone. It arrived with test machines for it, which means the role is verified by running it rather than by maintaining a list by hand.
macOS gets a config file and a manual command
The behaviour on Linux and on macOS is different in a way the README is upfront about. On Linux the playbook configures, enables and starts a systemd service in such a way that no additional action is needed. On macOS it installs the required packages and generates the correct wg0.conf file, which is then placed in the specified wireguard_remote_directory, which defaults to /opt/local/etc/wireguard.
On macOS, running the VPN is a manual step.
sudo wg-quick up wg0and deactivating it is the matching command.
sudo wg-quick down wg0There is a second route as well. You can install the official WireGuard app for macOS and import the wg0.conf file the role produced.
This split is not laziness. The Linux path relies on systemd, which does not exist on macOS, and the macOS path deliberately stops at generating a correct file rather than pretending it can manage a service that is not there. The consequence for a user is that a playbook run is not the whole story on macOS, and the remote directory default on that platform is /opt/local/etc/wireguard rather than the usual /etc/wireguard, which is worth remembering if you consume those files from another machine.
The port is yours, and IP forwarding is deliberately not handled
Two operational requirements sit outside the role, and the README is direct about both.
By default port 51820, over UDP, should be accessible from the outside, and the port can be adjusted by changing the variable wireguard_port. The other requirement is IP forwarding, which has to be enabled separately, for example by writing 1 into /proc/sys/net/ipv4/ip_forward. The author decided not to implement that task in the role, and the stated reasoning is that it should be handled elsewhere, where it belongs to the node's overall configuration rather than to a VPN role.
The README points at another role, ansible-role-harden-linux, for the job, noting that besides changing sysctl entries it also manages firewall settings among other things. The role is happy to point you at another role rather than take on the responsibility, and the alternative is named too: the PreUp, PreDown, PostUp and PostDown hooks may be a good place to do some network related work before a WireGuard interface comes up or goes down.
That is a sensible division. The hooks are in the generated configuration, so a rule that has to be in place before the tunnel is up has a natural home, and a sysctl that has to be in place before anything else happens does not.
Version 19.0.0 changed what an empty endpoint means
The changelog entry for 19.0.0 is the most instructive part of the documentation, because it describes a behaviour change that could silently break a working setup.
The new behaviour is that an empty wireguard_endpoint is treated as no endpoint, with no hostname fallback. If a peer explicitly sets wireguard_endpoint to an empty string, the template no longer falls back to inventory_hostname for the Endpoint line. Instead it emits a comment saying there is no endpoint.
The reason is a real bug. Because wireguard_port is always defined through the role defaults, the old logic almost always took the wireguard_port is defined branch and would generate Endpoint = hostname:51820 even when the peer had set an empty endpoint. That contradicted the documented contract in the README, which says that setting wireguard_endpoint to an empty string means this peer has no endpoint, and it broke setups where the inventory hostnames are not resolvable from the peers.
The affected users are narrow, since they would have had to rely on the old incorrect behaviour, and the fix is to omit wireguard_endpoint to get the hostname fallback or to set it to a real hostname or address. The change came with two pull requests, one for the template and one for Netplan, and with a Molecule scenario for the empty endpoint case.
This is what a well maintained role looks like when a default changes: the reasoning is written down, the affected population is identified, and the test suite grows a case to match.
The variables that decide where the config lands and who owns it
The defaults file is where the role's behaviour is actually specified, and the variables are well commented, which means you can read the contract without reading the tasks.
The remote directory is computed rather than hardcoded.
wireguard_remote_directory: >-
{%- if wireguard_ubuntu_use_netplan -%}
/etc/netplan
{%- elif ansible_facts['os_family'] == 'Darwin' -%}
/opt/local/etc/wireguard
{%- else -%}
/etc/wireguard
{%- endif %}On Ubuntu with netplan enabled the configuration goes to /etc/netplan, on Darwin it goes to /opt/local/etc/wireguard, and everywhere else it goes to /etc/wireguard. The rest of the defaults are simpler values.
wireguard_port: "51820"
wireguard_interface: "wg0"
wireguard_conf_owner: root
wireguard_conf_mode: 0600
wireguard_service_enabled: "yes"
wireguard_service_state: "started"The file ownership has one conditional worth noting. The group is root on everything except Darwin, where it is wheel, and the mode is 0600, which is correct for a file containing private keys. Whether a change to the configuration is backed up is a separate boolean, and it defaults to false.
The service defaults are enabled and started, which is the right default for a node that should be on the mesh without further action. One detail in the comments is worth flagging: by default wg syncconf is used to apply interface settings when they have changed, and older WireGuard tools do not provide that option. If you pin an old toolchain, the mechanism for applying changes is different.
Molecule scenarios, semantic versions, and a licence header
The repository layout shows how the role is tested and released. There are molecule/ scenarios, a meta/ directory, defaults/, handlers/, tasks/ and templates/, plus a CHANGELOG.md and a LICENSES/ directory alongside a .reuse/ directory, which is the SPDX reuse tooling used to track licensing metadata for individual files. A .yamllint configuration keeps the YAML style consistent.
The changelog entries show the testing practice directly. Adding Ubuntu 26.04 support came with Ubuntu 26.04 test VMs in the Molecule scenarios, and the 19.1.0 release switched to the maintainer's own Vagrant boxes where possible. The scenario suite is not a smoke test, it is the mechanism that makes a new distribution entry meaningful.
On versioning, the README says every release is tagged and the author tries to stay with semantic versioning, and recommends checking out the latest tag if you want to use the role. The master branch is described as development while the tags mark stable releases, with the caveat that the author tries to keep master in good shape too. The tag history supports that: 19.0.0 in January 2026, then 19.1.0 and 19.2.0 both on 2026-07-15, twenty minutes apart, and the last push to the branch was on 2026-06-15.
The licence is stated in the file headers as GPL-3.0-or-later, with copyright lines for Robert Wimmer and for fbourqui, while the repository metadata does not classify the licence at all. If that matters to your organisation, read the LICENSES/ directory rather than the metadata field.
Editorial conclusion
This role suits a platform engineer who wants WireGuard configured the same way across a mixed fleet of Linux distributions and wants the same file rendered on macOS, and it does not suit a setup where each node needs a different topology or where the role should also harden the host, since firewalling and IP forwarding are deliberately left out. Before upgrading, read the 19.0.0 entry in CHANGELOG.md, because treating an empty wireguard_endpoint as no endpoint changes the generated configuration for every peer that used the old hostname fallback.
Frequently asked questions
How do I install the githubixx WireGuard Ansible role?
You can clone the repository into the Ansible role directory, install it with ansible-galaxy role install githubixx.ansible_role_wireguard, or add it to a requirements.yml file and install that with ansible-galaxy role install -r requirements.yml, as the README shows with a pinned version of 19.0.0.
Which operating systems does the WireGuard Ansible role support?
The supported list covers Ubuntu 22.04, 24.04 and 26.04, Archlinux, Debian 12 and 13, Fedora 42 and 43, AlmaLinux 9 and 10, Rocky Linux 9 and 10, openSUSE Leap 15.6 and 16.0 and Oracle Linux 9, with a best effort tier for Debian 11, AlmaLinux 8, Rocky Linux 8 and elementary OS 6.
What changed in version 19.0.0 of the WireGuard role?
An empty wireguard_endpoint is now treated as no endpoint, with no fallback to the inventory hostname, and the template emits a comment instead of an Endpoint line. The change fixes a case where the generated configuration produced an endpoint for a peer that had explicitly asked for none.
Does the role set up the WireGuard service on macOS?
No. On macOS the role installs the required packages and generates the wg0.conf file in the remote directory, which defaults to /opt/local/etc/wireguard, and you then run sudo wg-quick up wg0 yourself or import the file into the official WireGuard app. On Linux the systemd service is configured, enabled and started by the playbook.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/githubixx-ansible-role-wireguard)Was this useful?