diff --git a/networkd/defaults/main.yml b/networkd/defaults/main.yml
index da5c013aebf3745785ce2840c43bc4197e59bfeb..680ab8521d17a44350fb0528fc97921f3b3796e9 100644
--- a/networkd/defaults/main.yml
+++ b/networkd/defaults/main.yml
@@ -1,6 +1,52 @@
 ---
 
-networkd_type: 'dhcp'  # or: 'static', 'bond'
+# networkd:
+#   - type: 'dhcp'
+#   - type: 'static'
+#     address: '10.10.10.10/24'
+#     gateway: '10.10.10.1'
+#   - type: 'routed'
+#     interfaces:
+#       - 'eth0'
+#     addresses:
+#       - '10.10.10.10/24'
+#     nameservers:
+#       - '8.8.8.8'
+#     routes:
+#       - gateway: '10.10.10.1'
+#         destination: '10.12.0.0/24'
+#         source: '10.10.10.10'
+#         metric: '100'
+#   - type: 'vlan'
+#     interface: 'eth0'
+#     vlans:
+#       - id: 23
+#         name: storage
+#         bridge: false
+#         address: 10.10.10.10/24
+#   - type: 'bond'
+#     bond: 'bond1'
+#     interfaces:
+#       - eth0
+#       - eth1
+#     vlans:
+#       - id: 23
+#         name: storage
+#         bridge: false
+#         address: 10.10.10.10/24
+#       - id: 42
+#         name: public
+#         bridge: true
+#         address: 10.10.12.22/24
+#         gateway: 10.10.12.1
+#       - id: 69
+#         name: transport
+#         bridge: true
+
+networkd_type: 'dhcp'  # if networkd not defined
+networkd_ipfwd: false  # works only globally anyway
+
+### old style config follows, still supported
 
 # for static type only
 networkd_address: 10.10.10.10/24
diff --git a/networkd/tasks/bond.yml b/networkd/tasks/bond.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ed7904ee74cf036a2fc5a14b7c2af0900b046bb6
--- /dev/null
+++ b/networkd/tasks/bond.yml
@@ -0,0 +1,76 @@
+---
+
+- name: ensure the network packages are installed
+  apt:
+    name:
+      - vlan
+      - bridge-utils
+    state: present
+- name: ensure bond module is loaded
+  modprobe:
+    name: 8021q
+    state: present
+- name: ensure bond module is loaded after a reboot
+  copy:
+    content: "8021q"
+    dest: /etc/modules-load.d/bond.conf
+- name: ensure bond netdev is configured
+  template:
+    src: bond.netdev.j2
+    dest: /etc/systemd/network/{{ bond.bond|default(networkd_bond) }}.netdev
+    owner: root
+    group: root
+    mode: '0644'
+  notify:
+    - restart networkd
+- name: ensure bond network is configured
+  template:
+    src: bond.network.j2
+    dest: /etc/systemd/network/{{ bond.bond|default(networkd_bond) }}.network
+    owner: root
+    group: root
+    mode: '0644'
+  notify:
+    - restart networkd
+- name: ensure vlan netdevs are configured
+  template:
+    src: vlan.netdev.j2
+    dest: /etc/systemd/network/vl-{{ item.name }}.netdev
+    owner: root
+    group: root
+    mode: '0644'
+  with_items: "{{ bond.vlans|default(networkd_bond_vlans) }}"
+  notify:
+    - restart networkd
+- name: ensure vlan networks are configured
+  template:
+    src: vlan.network.j2
+    dest: /etc/systemd/network/vl-{{ item.name }}.network
+    owner: root
+    group: root
+    mode: '0644'
+  with_items: "{{ bond.vlans|default(networkd_bond_vlans) }}"
+  notify:
+    - restart networkd
+- name: ensure bridge netdevs are configured
+  template:
+    src: bridge.netdev.j2
+    dest: /etc/systemd/network/vmbr-{{ item.name }}.netdev
+    owner: root
+    group: root
+    mode: '0644'
+  with_items: "{{ bond.vlans|default(networkd_bond_vlans) }}"
+  when: item.bridge == True
+  notify:
+    - restart networkd
+- name: ensure bridge networks are configured
+  template:
+    src: bridge.network.j2
+    dest: /etc/systemd/network/vmbr-{{ item.name }}.network
+    owner: root
+    group: root
+    mode: '0644'
+  with_items: "{{ bond.vlans|default(networkd_bond_vlans) }}"
+  when: item.bridge == True
+  notify:
+    - restart networkd
diff --git a/networkd/tasks/main.yml b/networkd/tasks/main.yml
index a52b1346521464b408502d85c3130464fc622a71..173578c3b9ae70fed2e010e7d851b6a1d28757c4 100644
--- a/networkd/tasks/main.yml
+++ b/networkd/tasks/main.yml
@@ -2,102 +2,38 @@
 
 - name: ensure networkd has a valid configuration
   template:
-    src: "20-wired-{{ networkd_type }}.network.j2"
-    dest: /etc/systemd/network/20-wired.network
+    src: "20-wired-{{ item.1.type }}.network.j2"
+    dest: /etc/systemd/network/{{ item.0 }}-wired.network
     owner: root
     group: root
     mode: '0644'
+  with_indexed_items: "{{ networkd|default([{'type': networkd_type}]) }}"
   notify:
     - restart networkd
 
-- name: ensure networkd has an additional valid configuration
-  template:
-    src: "20-wired-{{ networkd_additional_type }}.network.j2"
-    dest: /etc/systemd/network/30-wired.network
-    owner: root
-    group: root
-    mode: 0644
-  when: networkd_additional_type is defined
+- name: ensure networkd has no stale configuration
+  file:
+    path: "/etc/systemd/network/{{ item }}"
+    state: absent
+  with_items:
+    - 20-wired.network
+    - 30-wired.network
   notify:
     - restart networkd
 
 - name: ensure bonding works correctly
-  block:
-    - name: ensure the network packages are installed
-      apt:
-        name:
-          - vlan
-          - bridge-utils
-        state: present
-    - name: ensure bond module is loaded
-      modprobe:
-        name: 8021q
-        state: present
-    - name: ensure bond module is loaded after a reboot
-      copy:
-        content: "8021q"
-        dest: /etc/modules-load.d/bond.conf
-    - name: ensure bond netdev is configured
-      template:
-        src: bond.netdev.j2
-        dest: /etc/systemd/network/{{ networkd_bond }}.netdev
-        owner: root
-        group: root
-        mode: '0644'
-      notify:
-        - restart networkd
-    - name: ensure bond network is configured
-      template:
-        src: bond.network.j2
-        dest: /etc/systemd/network/{{ networkd_bond }}.network
-        owner: root
-        group: root
-        mode: '0644'
-      notify:
-        - restart networkd
-    - name: ensure vlan netdevs are configured
-      template:
-        src: vlan.netdev.j2
-        dest: /etc/systemd/network/vl-{{ item.name }}.netdev
-        owner: root
-        group: root
-        mode: '0644'
-      with_items: "{{ networkd_bond_vlans }}"
-      notify:
-        - restart networkd
-    - name: ensure vlan networks are configured
-      template:
-        src: vlan.network.j2
-        dest: /etc/systemd/network/vl-{{ item.name }}.network
-        owner: root
-        group: root
-        mode: '0644'
-      with_items: "{{ networkd_bond_vlans }}"
-      notify:
-        - restart networkd
-    - name: ensure bridge netdevs are configured
-      template:
-        src: bridge.netdev.j2
-        dest: /etc/systemd/network/vmbr-{{ item.name }}.netdev
-        owner: root
-        group: root
-        mode: '0644'
-      with_items: "{{ networkd_bond_vlans }}"
-      when: item.bridge == True
-      notify:
-        - restart networkd
-    - name: ensure bridge networks are configured
-      template:
-        src: bridge.network.j2
-        dest: /etc/systemd/network/vmbr-{{ item.name }}.network
-        owner: root
-        group: root
-        mode: '0644'
-      with_items: "{{ networkd_bond_vlans }}"
-      when: item.bridge == True
-      notify:
-        - restart networkd
-  when: networkd_type == 'bond'
+  include_tasks: bond.yml
+  loop: "{{ networkd|default([{'type': networkd_type}])|flatten(levels=1) }}"
+  loop_control:
+    loop_var: bond
+  when: bond.type == 'bond'
+
+- name: ensure vlaning works correctly
+  include_tasks: vlan.yml
+  loop: "{{ networkd|default([{'type': networkd_type}])|flatten(levels=1) }}"
+  loop_control:
+    loop_var: vlan
+  when: vlan.type == 'vlan'
 
 - name: ensure networkd is enabled and running
   service:
diff --git a/networkd/tasks/vlan.yml b/networkd/tasks/vlan.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9205ba4a3266d1c8a4f76b6a210c1734d9b08f5a
--- /dev/null
+++ b/networkd/tasks/vlan.yml
@@ -0,0 +1,50 @@
+---
+
+- name: ensure the network packages are installed
+  apt:
+    name:
+      - vlan
+      - bridge-utils
+    state: present
+- name: ensure vlan netdevs are configured
+  template:
+    src: vlan.netdev.j2
+    dest: /etc/systemd/network/vl-{{ item.name }}.netdev
+    owner: root
+    group: root
+    mode: '0644'
+  with_items: "{{ vlan.vlans }}"
+  notify:
+    - restart networkd
+- name: ensure vlan networks are configured
+  template:
+    src: vlan.network.j2
+    dest: /etc/systemd/network/vl-{{ item.name }}.network
+    owner: root
+    group: root
+    mode: '0644'
+  with_items: "{{ vlan.vlans }}"
+  notify:
+    - restart networkd
+- name: ensure bridge netdevs are configured
+  template:
+    src: bridge.netdev.j2
+    dest: /etc/systemd/network/vmbr-{{ item.name }}.netdev
+    owner: root
+    group: root
+    mode: '0644'
+  with_items: "{{ vlan.vlans }}"
+  when: item.bridge == True
+  notify:
+    - restart networkd
+- name: ensure bridge networks are configured
+  template:
+    src: bridge.network.j2
+    dest: /etc/systemd/network/vmbr-{{ item.name }}.network
+    owner: root
+    group: root
+    mode: '0644'
+  with_items: "{{ vlan.vlans }}"
+  when: item.bridge == True
+  notify:
+    - restart networkd
diff --git a/networkd/templates/20-wired-bond.network.j2 b/networkd/templates/20-wired-bond.network.j2
index 7ff16874453cf639d8a4bdc647910d9f432bb17a..376ff643bc928304ebd6e55028b1db9d416e533f 100644
--- a/networkd/templates/20-wired-bond.network.j2
+++ b/networkd/templates/20-wired-bond.network.j2
@@ -1,5 +1,8 @@
 [Match]
-Name={{ networkd_bond_devices|join(' ') }}
+Name={{ item.1.interfaces|default(networkd_bond_devices)|join(' ') }}
 
 [Network]
-Bond={{ networkd_bond }}
+Bond={{ item.1.bond|default(networkd_bond) }}
+{% if networkd_ipfwd %}
+IPForward=yes
+{% endif %}
diff --git a/networkd/templates/20-wired-dhcp.network.j2 b/networkd/templates/20-wired-dhcp.network.j2
index 020f88170fac52615c749786f8683c6f2a61fdb2..a780e7dd48cff00d4afa8bb272a5629a8b758b6d 100644
--- a/networkd/templates/20-wired-dhcp.network.j2
+++ b/networkd/templates/20-wired-dhcp.network.j2
@@ -1,5 +1,8 @@
 [Match]
-Name={{ ansible_default_ipv4.interface }}
+Name={{ item.1.interface|default(ansible_default_ipv4.interface) }}
 
 [Network]
 DHCP=ipv4
+{% if networkd_ipfwd %}
+IPForward=yes
+{% endif %}
diff --git a/networkd/templates/20-wired-routed.network.j2 b/networkd/templates/20-wired-routed.network.j2
new file mode 100644
index 0000000000000000000000000000000000000000..66437c22d4901484398f4f9dc9a91616432577f0
--- /dev/null
+++ b/networkd/templates/20-wired-routed.network.j2
@@ -0,0 +1,32 @@
+{% if not network is defined %}
+{% set network = item.1 %}
+{% endif %}
+[Match]
+Name={{ network.interfaces|default([network.interface])|join(' ') }}
+
+[Network]
+{% if network.addresses is defined or network.address is defined %}
+{% for addr in network.addresses|default([network.address]) %}
+Address={{ addr }}
+{% endfor %}
+{% for server in network.nameservers|default(nameservers) %}
+DNS={{ server }}
+{% endfor %}
+{% if networkd_ipfwd %}
+IPForward=yes
+{% endif %}
+{% endif %}
+
+{% for route in network.routes|default([{'gateway': network.gateway}]) %}
+[Route]
+Gateway={{ route.gateway }}
+{% if route.destination is defined %}
+Destination={{ route.destination }}
+{% endif %}
+{% if route.source is defined %}
+PreferredSource={{ route.source }}
+{% endif %}
+{% if route.metric is defined %}
+Metric={{ route.metric }}
+{% endif %}
+{% endfor %}
diff --git a/networkd/templates/20-wired-static.network.j2 b/networkd/templates/20-wired-static.network.j2
index db0a7b2a1283da4c56dbbe54524493920e2e4e78..020c5573715e3214a4724031093f2e8168811aa2 100644
--- a/networkd/templates/20-wired-static.network.j2
+++ b/networkd/templates/20-wired-static.network.j2
@@ -1,10 +1,6 @@
-[Match]
-Name={{ ansible_default_ipv4.interface }}
-
-[Network]
-Address={{ networkd_address }}
-Gateway={{ networkd_gateway }}
-{% for server in nameservers %}
-DNS={{ server }}
-{% endfor %}
-
+{% set network = {
+	'interface': item.1.interface|default(ansible_default_ipv4.interface),
+	'addresses': item.1.addresses|default([item.1.adress|default(networkd_address)]),
+	'gateway': item.1.gateway|default(networkd_gateway),
+	} %}
+{% include "20-wired-routed.network.j2" %}
diff --git a/networkd/templates/20-wired-vlan.network.j2 b/networkd/templates/20-wired-vlan.network.j2
new file mode 100644
index 0000000000000000000000000000000000000000..4b7052153a9bae40c7c8ddcdfa1065e05c218e6a
--- /dev/null
+++ b/networkd/templates/20-wired-vlan.network.j2
@@ -0,0 +1,11 @@
+[Match]
+Name={{ item.1.interface }}
+
+[Network]
+{% for vlan in item.1.vlans %}
+VLAN=vl-{{ vlan.name }}
+{% endfor %}
+BindCarrier={{ item.1.interface }}
+{% if networkd_ipfwd %}
+IPForward=yes
+{% endif %}
diff --git a/networkd/templates/bond.netdev.j2 b/networkd/templates/bond.netdev.j2
index 44c8fddd96893c69ec01c50ebe10945a81310a32..83d94088c987fc8ddb290b4e354e034e3aeb8f9c 100644
--- a/networkd/templates/bond.netdev.j2
+++ b/networkd/templates/bond.netdev.j2
@@ -1,5 +1,5 @@
 [NetDev]
-Name={{ networkd_bond }}
+Name={{ bond.bond|default(networkd_bond) }}
 Kind=bond
 
 [Bond]
diff --git a/networkd/templates/bond.network.j2 b/networkd/templates/bond.network.j2
index 0677510d7b9bbb535131c86bcc1271568d8ce2f9..fa6b046650cd0db5935c04f273e979d2bd283b9c 100644
--- a/networkd/templates/bond.network.j2
+++ b/networkd/templates/bond.network.j2
@@ -1,8 +1,8 @@
 [Match]
-Name={{ networkd_bond }}
+Name={{ bond.bond|default(networkd_bond) }}
 
 [Network]
-{% for vlan in networkd_bond_vlans %}
+{% for vlan in bond.vlans|default(networkd_bond_vlans) %}
 VLAN=vl-{{ vlan.name }}
 {% endfor %}
-BindCarrier={{ networkd_bond_devices|join(' ') }}
+BindCarrier={{ bond.interfaces|default(networkd_bond_devices)|join(' ') }}
diff --git a/networkd/templates/bridge.network.j2 b/networkd/templates/bridge.network.j2
index 8fc4ae337ea28a8c96cfa2d58e376c56afe5b448..2fbc73c95121008eee961753415642348274c02f 100644
--- a/networkd/templates/bridge.network.j2
+++ b/networkd/templates/bridge.network.j2
@@ -1,10 +1,7 @@
-[Match]
-Name=vmbr-{{ item.name }}
-
-[Network]
-{% if 'address' in item %}
-Address={{ item.address }}
-{% if 'gateway' in item %}
-Gateway={{ item.gateway }}
-{% endif %}
-{% endif %}
+{% set network = {
+	'interface': 'vmbr-' + item.name,
+	'addresses': [item.address] if item.address is defined else [],
+	'nameservers': [],
+	'routes': [{'gateway': item.gateway}] if item.gateway is defined else []
+	} %}
+{% include "20-wired-routed.network.j2" %}
diff --git a/networkd/templates/vlan.network.j2 b/networkd/templates/vlan.network.j2
index 79f5a81f7e6671b25df5bc7688be7239b65cd759..fda8a92cc8bf56887f7d6dab62ca09bda9d3c441 100644
--- a/networkd/templates/vlan.network.j2
+++ b/networkd/templates/vlan.network.j2
@@ -1,14 +1,15 @@
+{% if item.bridge %}
 [Match]
 Name=vl-{{ item.name }}
 
 [Network]
-{% if item.bridge %}
 Bridge=vmbr-{{ item.name }}
 {% else %}
-{% if 'address' in item %}
-Address={{ item.address }}
-{% if 'gateway' in item %}
-Gateway={{ item.gateway }}
-{% endif %}
-{% endif %}
+{% set network = {
+	'interface': 'vl-' + item.name,
+	'addresses': item.addresses|default([item.address] if item.address is defined else []),
+	'nameservers': item.nameservers|default([]),
+	'routes': item.routes|default([{'gateway': item.gateway}] if item.gateway is defined else [])
+	} %}
+{% include "20-wired-routed.network.j2" %}
 {% endif %}