diff --git a/wireguard/defaults/main.yml b/wireguard/defaults/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..922d9919d178fa421882fade08ab5a9c0b46cdbe
--- /dev/null
+++ b/wireguard/defaults/main.yml
@@ -0,0 +1,8 @@
+---
+
+wireguard_interfaces: {}
+
+# wg0:
+#   addresses: ["10.0.0.1", "fd00::1"]
+#   peers: hostname pattern (as in inventory)
+#   port: 40000 (optional, set to make reachable by peers)
diff --git a/wireguard/handlers/main.yml b/wireguard/handlers/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..71b65f1111ba88c7be0b38aa16a1efd9e212dfc1
--- /dev/null
+++ b/wireguard/handlers/main.yml
@@ -0,0 +1,7 @@
+---
+
+- name: restart wireguard
+  systemd:
+    name: "wg-quick@{{item.key}}.service"
+    state: restarted
+  with_dict: "{{wireguard_interfaces}}"
diff --git a/wireguard/tasks/install-Archlinux.yml b/wireguard/tasks/install-Archlinux.yml
new file mode 100644
index 0000000000000000000000000000000000000000..61b77c5d61465dca5c68347b7f908d241652ad77
--- /dev/null
+++ b/wireguard/tasks/install-Archlinux.yml
@@ -0,0 +1,6 @@
+---
+
+- name: install wireguard on Arch
+  pacman:
+    name: wireguard-tools
+    state: present
diff --git a/wireguard/tasks/install-Debian.yml b/wireguard/tasks/install-Debian.yml
new file mode 100644
index 0000000000000000000000000000000000000000..96acad365b71b830c92af0e533716cc1e82f4a24
--- /dev/null
+++ b/wireguard/tasks/install-Debian.yml
@@ -0,0 +1,6 @@
+---
+
+- name: install wireguard on Debian
+  apt:
+    name: wireguard
+    state: present
diff --git a/wireguard/tasks/main.yml b/wireguard/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e57bfdf30c822b63223f53a177c9ce77c40a91ff
--- /dev/null
+++ b/wireguard/tasks/main.yml
@@ -0,0 +1,43 @@
+---
+
+- name: ensure wireguard is installed
+  include_tasks: "install-{{ansible_facts['os_family']}}.yml"
+
+- name: ensure we have a private key
+  shell:
+    cmd: "wg genkey | tee {{item.key}}.key | wg pubkey > {{item.key}}.pub"
+    chdir: /etc/wireguard
+    creates: "/etc/wireguard/{{item.key}}.key"
+  with_dict: "{{wireguard_interfaces}}"
+  notify:
+    - restart wireguard
+  no_log: true
+
+- name: get the pubkey
+  slurp:
+    src: "/etc/wireguard/{{item.key}}.pub"
+  register: pubkeys
+  with_dict: "{{wireguard_interfaces}}"
+
+- name: store the pubkey in facts
+  set_fact:
+    wireguard_pubkeys: "{{dict(pubkeys.results|map(attribute='item')|map(attribute='key') | zip(pubkeys.results|map(attribute='content')|map('b64decode')|map('trim')))}}"
+    cacheable: true
+
+- name: configure wireguard
+  template:
+    src: wireguard.conf.j2
+    dest: /etc/wireguard/{{item.key}}.conf
+    owner: root
+    group: root
+    mode: 0600
+  with_dict: "{{wireguard_interfaces}}"
+  notify:
+    - restart wireguard
+
+- name: enable interface
+  systemd:
+    name: "wg-quick@{{item.key}}.service"
+    state: started
+    enabled: true
+  with_dict: "{{wireguard_interfaces}}"
diff --git a/wireguard/templates/wireguard.conf.j2 b/wireguard/templates/wireguard.conf.j2
new file mode 100644
index 0000000000000000000000000000000000000000..94ff14371046e78275a24cbc9c07185a3e471f16
--- /dev/null
+++ b/wireguard/templates/wireguard.conf.j2
@@ -0,0 +1,15 @@
+[Interface]
+Address = {{ item.value.addresses|join(", ") }}
+PostUp = wg set %i private-key /etc/wireguard/{{item.key}}.key
+{% if item.value.port is defined %}
+ListenPort = {{item.value.port}}
+{% endif %}
+
+{% for peer in lookup('inventory_hostnames', item.value.peers, wantlist=True) %}
+[Peer]
+PublicKey = {{hostvars[peer]['ansible_facts']['wireguard_pubkeys'][item.key]}}
+AllowedIPs = {{hostvars[peer]['wireguard_interfaces'][item.key]['addresses'] | join(', ')}}
+{% if hostvars[peer]['wireguard_interfaces'][item.key]['port'] is defined %}
+Endpoint = {{hostvars[peer]['ansible_facts']['fqdn']}}:{{hostvars[peer]['wireguard_interfaces'][item.key]['port']}}
+{% endif %}
+{% endfor %}