diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4732ebf..0ac2cae 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -2,7 +2,7 @@
 
 repos:
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v3.3.0
+    rev: v3.4.0
     hooks:
       - id: check-added-large-files
       - id: check-byte-order-marker
diff --git a/playbooks/multinode-setup.yaml b/playbooks/multinode-setup.yaml
new file mode 100644
index 0000000..8afd8a5
--- /dev/null
+++ b/playbooks/multinode-setup.yaml
@@ -0,0 +1,3 @@
+- hosts: all
+  roles:
+    - multi-node-setup
diff --git a/roles/multi-node-setup/README.rst b/roles/multi-node-setup/README.rst
new file mode 100644
index 0000000..3580d3f
--- /dev/null
+++ b/roles/multi-node-setup/README.rst
@@ -0,0 +1,23 @@
+Set up connection between infra bridge and Neutron external bridge
+
+Network topology used in CI multinode jobs is described In `Devstack documention
+<https://opendev.org/openstack/devstack-gate/src/branch/master/multinode_setup_info.txt#L81>`_
+
+In case when DVR is used, there is also additional bridge ``br-infra`` added
+on each node to provide connectivity to floating IPs from main node.
+
+This bridge needs to be connected with bridge used by Neutron as
+external bridge. Typically it is ``br-ex`` and this role adds patch ports
+between those bridges.
+
+**Role Variables**
+
+.. zuul:rolevar:: neutron_external_bridge_name
+   :default: br-ex
+
+   Name of the Neutron external bridge.
+
+.. zuul:rolevar:: infra_bridge_name
+   :default: br-infra
+
+   Name of the infra bridge.
diff --git a/roles/multi-node-setup/defaults/main.yaml b/roles/multi-node-setup/defaults/main.yaml
new file mode 100644
index 0000000..bba1866
--- /dev/null
+++ b/roles/multi-node-setup/defaults/main.yaml
@@ -0,0 +1,4 @@
+---
+
+infra_bridge_name: br-infra
+neutron_external_bridge_name: br-ex
diff --git a/roles/multi-node-setup/tasks/main.yaml b/roles/multi-node-setup/tasks/main.yaml
new file mode 100644
index 0000000..f4de2af
--- /dev/null
+++ b/roles/multi-node-setup/tasks/main.yaml
@@ -0,0 +1,32 @@
+---
+
+- name: Configure Neutron bridge for multi node setup
+  when: "( ansible_play_hosts | length ) > 1"
+  block:
+
+  - name: Ensure the infra bridge exists
+    become: true
+    openvswitch_bridge:
+      bridge: "{{ infra_bridge_name }}"
+      state: present
+      fail_mode: standalone
+
+  - name: Ensure the Neutron external bridge exists
+    become: true
+    openvswitch_bridge:
+      bridge: "{{ neutron_external_bridge_name }}"
+      state: present
+      fail_mode: standalone
+
+  - name: Create patch port between bridges
+    become: true
+    command: >-
+      ovs-vsctl
+        --may-exist add-port {{ infra_bridge_name }}
+          patch-{{ neutron_external_bridge_name }} --
+        set interface patch-{{ neutron_external_bridge_name }}
+          type=patch options:peer=patch-{{ infra_bridge_name }} --
+        --may-exist add-port {{ neutron_external_bridge_name }}
+          patch-{{ infra_bridge_name }} --
+        set interface patch-{{ infra_bridge_name }}
+          type=patch options:peer=patch-{{ neutron_external_bridge_name }}
diff --git a/zuul.d/jobs.yaml b/zuul.d/jobs.yaml
index 2d843fe..2f0c86b 100644
--- a/zuul.d/jobs.yaml
+++ b/zuul.d/jobs.yaml
@@ -228,6 +228,7 @@
     abstract: true
     description: Base DevStack Tobiko job with multinode.
     timeout: 10800
+    pre-run: playbooks/multinode-setup.yaml
     vars:
       topology: multinode
       devstack_localrc: