diff --git a/ansible/group_vars/all.yml b/ansible/group_vars/all.yml
index e412b264bc1f241e78f06b2ace39371fe10fccc1..fd481b83c8514810bce799d1bcdcf630e95b3318 100644
--- a/ansible/group_vars/all.yml
+++ b/ansible/group_vars/all.yml
@@ -661,6 +661,7 @@ enable_neutron_port_forwarding: "no"
 enable_nova_serialconsole_proxy: "no"
 enable_nova_ssh: "yes"
 enable_octavia: "no"
+enable_octavia_driver_agent: "{{ enable_octavia | bool and neutron_plugin_agent == 'ovn' }}"
 enable_openvswitch: "{{ enable_neutron | bool and neutron_plugin_agent != 'linuxbridge' }}"
 enable_ovn: "{{ enable_neutron | bool and neutron_plugin_agent == 'ovn' }}"
 enable_ovs_dpdk: "no"
diff --git a/ansible/inventory/all-in-one b/ansible/inventory/all-in-one
index ef29a05595306341dac815d2a23b4c68e0adb6ce..cc827a42e100eb694b31225c907a91b6e638f8f6 100644
--- a/ansible/inventory/all-in-one
+++ b/ansible/inventory/all-in-one
@@ -598,6 +598,9 @@ senlin
 [octavia-api:children]
 octavia
 
+[octavia-driver-agent:children]
+octavia
+
 [octavia-health-manager:children]
 octavia
 
diff --git a/ansible/inventory/multinode b/ansible/inventory/multinode
index 36731ca9f4374715b92b92b5e93ce45d9ee56aa0..e843bb7592a1d664adb5bf8ba654b83ae451c551 100644
--- a/ansible/inventory/multinode
+++ b/ansible/inventory/multinode
@@ -616,6 +616,9 @@ senlin
 [octavia-api:children]
 octavia
 
+[octavia-driver-agent:children]
+octavia
+
 [octavia-health-manager:children]
 octavia
 
diff --git a/ansible/roles/octavia/defaults/main.yml b/ansible/roles/octavia/defaults/main.yml
index c349a93946a9e830656ba64ed6f2b69603342cc4..9876bf62bdafe8e5c6639bfcf0dff479ac0ce6f3 100644
--- a/ansible/roles/octavia/defaults/main.yml
+++ b/ansible/roles/octavia/defaults/main.yml
@@ -21,6 +21,13 @@ octavia_services:
         mode: "http"
         external: true
         port: "{{ octavia_api_port }}"
+  octavia-driver-agent:
+    container_name: octavia_driver_agent
+    group: octavia-driver-agent
+    enabled: "{{ enable_octavia_driver_agent }}"
+    image: "{{ octavia_driver_agent_image_full }}"
+    volumes: "{{ octavia_driver_agent_default_volumes + octavia_driver_agent_extra_volumes }}"
+    dimensions: "{{ octavia_driver_agent_dimensions }}"
   octavia-health-manager:
     container_name: octavia_health_manager
     group: octavia-health-manager
@@ -72,6 +79,10 @@ octavia_api_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ doc
 octavia_api_tag: "{{ octavia_tag }}"
 octavia_api_image_full: "{{ octavia_api_image }}:{{ octavia_api_tag }}"
 
+octavia_driver_agent_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ octavia_install_type }}-octavia-driver-agent"
+octavia_driver_agent_tag: "{{ octavia_tag }}"
+octavia_driver_agent_image_full: "{{ octavia_driver_agent_image }}:{{ octavia_driver_agent_tag }}"
+
 octavia_health_manager_image: "{{ docker_registry ~ '/' if docker_registry else '' }}{{ docker_namespace }}/{{ kolla_base_distro }}-{{ octavia_install_type }}-octavia-health-manager"
 octavia_health_manager_tag: "{{ octavia_tag }}"
 octavia_health_manager_image_full: "{{ octavia_health_manager_image }}:{{ octavia_health_manager_tag }}"
@@ -85,6 +96,7 @@ octavia_worker_tag: "{{ octavia_tag }}"
 octavia_worker_image_full: "{{ octavia_worker_image }}:{{ octavia_worker_tag }}"
 
 octavia_api_dimensions: "{{ default_container_dimensions }}"
+octavia_driver_agent_dimensions: "{{ default_container_dimensions }}"
 octavia_health_manager_dimensions: "{{ default_container_dimensions }}"
 octavia_housekeeping_dimensions: "{{ default_container_dimensions }}"
 octavia_worker_dimensions: "{{ default_container_dimensions }}"
@@ -147,12 +159,20 @@ octavia_api_default_volumes:
   - "{{ '/etc/timezone:/etc/timezone:ro' if ansible_os_family == 'Debian' else '' }}"
   - "kolla_logs:/var/log/kolla/"
   - "{{ kolla_dev_repos_directory ~ '/octavia/octavia:/var/lib/kolla/venv/lib/python' ~ distro_python_version ~ '/site-packages/octavia' if octavia_dev_mode | bool else '' }}"
+  - "octavia_driver_agent:/var/run/octavia/"
 octavia_health_manager_default_volumes:
   - "{{ node_config_directory }}/octavia-health-manager/:{{ container_config_directory }}/:ro"
   - "/etc/localtime:/etc/localtime:ro"
   - "{{ '/etc/timezone:/etc/timezone:ro' if ansible_os_family == 'Debian' else '' }}"
   - "kolla_logs:/var/log/kolla/"
   - "{{ kolla_dev_repos_directory ~ '/octavia/octavia:/var/lib/kolla/venv/lib/python' ~ distro_python_version ~ '/site-packages/octavia' if octavia_dev_mode | bool else '' }}"
+octavia_driver_agent_default_volumes:
+  - "{{ node_config_directory }}/octavia-driver-agent/:{{ container_config_directory }}/:ro"
+  - "/etc/localtime:/etc/localtime:ro"
+  - "{{ '/etc/timezone:/etc/timezone:ro' if ansible_os_family == 'Debian' else '' }}"
+  - "kolla_logs:/var/log/kolla/"
+  - "{{ kolla_dev_repos_directory ~ '/octavia/octavia:/var/lib/kolla/venv/lib/python' ~ distro_python_version ~ '/site-packages/octavia' if octavia_dev_mode | bool else '' }}"
+  - "octavia_driver_agent:/var/run/octavia/"
 octavia_housekeeping_default_volumes:
   - "{{ node_config_directory }}/octavia-housekeeping/:{{ container_config_directory }}/:ro"
   - "/etc/localtime:/etc/localtime:ro"
@@ -168,6 +188,7 @@ octavia_worker_default_volumes:
 
 octavia_extra_volumes: "{{ default_extra_volumes }}"
 octavia_api_extra_volumes: "{{ octavia_extra_volumes }}"
+octavia_driver_agent_extra_volumes: "{{ octavia_extra_volumes }}"
 octavia_health_manager_extra_volumes: "{{ octavia_extra_volumes }}"
 octavia_housekeeping_extra_volumes: "{{ octavia_extra_volumes }}"
 octavia_worker_extra_volumes: "{{ octavia_extra_volumes }}"
@@ -302,3 +323,7 @@ octavia_amp_network:
 
 # Octavia management network subnet CIDR.
 octavia_amp_network_cidr: 10.1.0.0/24
+
+# Octavia provider drivers
+octavia_provider_drivers: "amphora:Amphora provider{% if neutron_plugin_agent == 'ovn'%}, ovn:OVN provider{% endif %}"
+octavia_provider_agents: "amphora_agent{% if neutron_plugin_agent == 'ovn'%}, ovn{% endif %}"
diff --git a/ansible/roles/octavia/handlers/main.yml b/ansible/roles/octavia/handlers/main.yml
index a8791b2e8bf39741b15c221c88a1d0fb164e89e7..139538ea915c66382a255287d2129ac2e3044a16 100644
--- a/ansible/roles/octavia/handlers/main.yml
+++ b/ansible/roles/octavia/handlers/main.yml
@@ -15,6 +15,21 @@
   when:
     - kolla_action != "config"
 
+- name: Restart octavia-driver-agent container
+  vars:
+    service_name: "octavia-driver-agent"
+    service: "{{ octavia_services[service_name] }}"
+  become: true
+  kolla_docker:
+    action: "recreate_or_restart_container"
+    common_options: "{{ docker_common_options }}"
+    name: "{{ service.container_name }}"
+    image: "{{ service.image }}"
+    volumes: "{{ service.volumes | reject('equalto', '') | list }}"
+    dimensions: "{{ service.dimensions }}"
+  when:
+    - kolla_action != "config"
+
 - name: Restart octavia-health-manager container
   vars:
     service_name: "octavia-health-manager"
diff --git a/ansible/roles/octavia/templates/octavia-api.json.j2 b/ansible/roles/octavia/templates/octavia-api.json.j2
index 0e315bebc88f352584a861a08c7e24c1b4c73cb7..e62a7dc68e25885374213f446249406131603859 100644
--- a/ansible/roles/octavia/templates/octavia-api.json.j2
+++ b/ansible/roles/octavia/templates/octavia-api.json.j2
@@ -13,5 +13,11 @@
             "owner": "octavia",
             "perm": "0600"
         }{% endif %}
+    ],
+    "permissions": [
+        {
+            "path": "/var/run/octavia",
+            "owner": "octavia:octavia"
+        }
     ]
 }
diff --git a/ansible/roles/octavia/templates/octavia-driver-agent.json.j2 b/ansible/roles/octavia/templates/octavia-driver-agent.json.j2
new file mode 100644
index 0000000000000000000000000000000000000000..cde7b33607a9093f67ed72538e54224b91c24ae9
--- /dev/null
+++ b/ansible/roles/octavia/templates/octavia-driver-agent.json.j2
@@ -0,0 +1,23 @@
+{
+    "command": "octavia-driver-agent --config-file /etc/octavia/octavia.conf",
+    "config_files": [
+        {
+            "source": "{{ container_config_directory }}/octavia.conf",
+            "dest": "/etc/octavia/octavia.conf",
+            "owner": "octavia",
+            "perm": "0600"
+        }{% if octavia_policy_file is defined %},
+        {
+            "source": "{{ container_config_directory }}/{{ octavia_policy_file }}",
+            "dest": "/etc/octavia/{{ octavia_policy_file }}",
+            "owner": "octavia",
+            "perm": "0600"
+        }{% endif %}
+    ],
+    "permissions": [
+        {
+            "path": "/var/run/octavia",
+            "owner": "octavia:octavia"
+        }
+    ]
+}
diff --git a/ansible/roles/octavia/templates/octavia.conf.j2 b/ansible/roles/octavia/templates/octavia.conf.j2
index d09e3cdc4f859a8cb4c242b1623a6d86daf391aa..190dcc75d7b815ace5ff47cb0f0f95c5812acfdd 100644
--- a/ansible/roles/octavia/templates/octavia.conf.j2
+++ b/ansible/roles/octavia/templates/octavia.conf.j2
@@ -19,6 +19,17 @@ endpoint_type = internal
 ca_certificates_file = {{ openstack_cacert }}
 {% endif %}
 
+[api_settings]
+enabled_provider_drivers = '{{ octavia_provider_drivers }}'
+
+[driver_agent]
+enabled_provider_agents = {{ octavia_provider_agents }}
+
+{% if neutron_plugin_agent == 'ovn' %}
+[ovn]
+ovn_nb_connection = {{ ovn_nb_connection }}
+{% endif %}
+
 [haproxy_amphora]
 server_ca = /etc/octavia/certs/server_ca.cert.pem
 client_cert = /etc/octavia/certs/client.cert-and-key.pem
diff --git a/etc/kolla/globals.yml b/etc/kolla/globals.yml
index 2b10d29f3f7e22096d94ba3c3c168ec4a3996e81..069c96701046bea470a132240c2b1456bbe2cc94 100644
--- a/etc/kolla/globals.yml
+++ b/etc/kolla/globals.yml
@@ -358,6 +358,7 @@
 #enable_nova_serialconsole_proxy: "no"
 #enable_nova_ssh: "yes"
 #enable_octavia: "no"
+#enable_octavia_driver_agent: "{{ enable_octavia | bool and neutron_plugin_agent == 'ovn' }}"
 #enable_openvswitch: "{{ enable_neutron | bool and neutron_plugin_agent != 'linuxbridge' }}"
 #enable_ovn: "{{ enable_neutron | bool and neutron_plugin_agent == 'ovn' }}"
 #enable_ovs_dpdk: "no"
diff --git a/releasenotes/notes/bug-1903506-12ae72c114bede72.yaml b/releasenotes/notes/bug-1903506-12ae72c114bede72.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..23e9a5f50af4c7eb59fa57d84f9a6be8d573f2f1
--- /dev/null
+++ b/releasenotes/notes/bug-1903506-12ae72c114bede72.yaml
@@ -0,0 +1,8 @@
+---
+features:
+  - |
+    Add ``octavia-driver-agent`` to ``Octavia`` deployments to allow for
+    additional providers, e.g. ``ovn-octavia-provider``. It is automatically
+    deployed when ``Octavia`` is enabled and ``neutron_plugin_agent`` is set
+    to ``ovn``. It can be also enabled by setting
+    ``enable_octavia_driver_agent`` to ``yes``.
diff --git a/tests/run.yml b/tests/run.yml
index 143f2f406009b03c73776e14a1b1d67593913ffa..2e51fa97d30963d345c822d17fa69958fb4f1f65 100644
--- a/tests/run.yml
+++ b/tests/run.yml
@@ -269,7 +269,7 @@
     # same host being used by both)
     - name: create TLS certificates for octavia
       command: kolla-ansible octavia-certificates
-      when: scenario == 'octavia'
+      when: scenario in ['octavia', 'ovn']
 
     # NOTE(mgoddard): We are using the script module here and later to ensure
     # we use the local copy of these scripts, rather than the one on the remote
diff --git a/tests/setup_gate.sh b/tests/setup_gate.sh
index 87bf2d1e150bc784303a85657fd44755d3e738b5..5e75f6b59921ee620a5a843d520c52d27ae763f1 100755
--- a/tests/setup_gate.sh
+++ b/tests/setup_gate.sh
@@ -32,6 +32,9 @@ function setup_openstack_clients {
     if [[ $SCENARIO == monasca ]]; then
         packages+=(python-monascaclient)
     fi
+    if [[ $SCENARIO == ovn ]]; then
+        packages+=(python-octaviaclient)
+    fi
     if [[ "debian" == $BASE_DISTRO ]]; then
         sudo apt -y install python3-venv
     fi
@@ -83,7 +86,7 @@ function prepare_images {
     fi
 
     if [[ $SCENARIO == "ovn" ]]; then
-        GATE_IMAGES+=",^ovn"
+        GATE_IMAGES+=",^octavia,^ovn"
     fi
 
     if [[ $SCENARIO == "mariadb" ]]; then
diff --git a/tests/templates/globals-default.j2 b/tests/templates/globals-default.j2
index 7298c462f6a2c7a97ff351911d4a47cb669c018f..91e8aed1953a8fe5d062bfa56c6f67d3c758e20f 100644
--- a/tests/templates/globals-default.j2
+++ b/tests/templates/globals-default.j2
@@ -149,6 +149,8 @@ neutron_plugin_agent: "linuxbridge"
 
 {% if scenario == "ovn" %}
 neutron_plugin_agent: "ovn"
+neutron_ovn_distributed_fip: "yes"
+enable_octavia: "yes"
 {% endif %}
 
 {% if scenario == "prometheus-efk" %}
diff --git a/tests/templates/inventory.j2 b/tests/templates/inventory.j2
index f8156ba34135cf79de4e09537d62a963464c62d0..27797ae990f029d597cc8f2e1473a8832015eeb0 100644
--- a/tests/templates/inventory.j2
+++ b/tests/templates/inventory.j2
@@ -648,6 +648,9 @@ senlin
 [octavia-api:children]
 octavia
 
+[octavia-driver-agent:children]
+octavia
+
 [octavia-health-manager:children]
 octavia
 
diff --git a/tests/test-ovn.sh b/tests/test-ovn.sh
index bf47f3de50f0fda212b914ac05d2ecd1c497b01a..9ed19061955a23e4c4c3cb0c0a6d4cfc94ab00fc 100755
--- a/tests/test-ovn.sh
+++ b/tests/test-ovn.sh
@@ -7,7 +7,7 @@ set -o pipefail
 # Enable unbuffered output
 export PYTHONUNBUFFERED=1
 
-function test_ovn_logged {
+function test_ovn {
     # NOTE(yoctozepto): could use real ini parsing but this is fine for now
     local neutron_ml2_conf_path=/etc/kolla/neutron-server/ml2_conf.ini
     ovn_nb_connection=$(sudo grep -P -o -e "(?<=^ovn_nb_connection = ).*" "$neutron_ml2_conf_path")
@@ -42,9 +42,87 @@ function test_ovn_logged {
     fi
 }
 
-function test_ovn {
-    echo "Testing OVN"
-    test_ovn_logged > /tmp/logs/ansible/test-ovn 2>&1
+function test_octavia {
+    . /etc/kolla/admin-openrc.sh
+    . ~/openstackclient-venv/bin/activate
+    echo "Testing OVN Octavia provider"
+    echo "Smoke test"
+    openstack loadbalancer list
+
+    # Create a server to act as a backend
+    openstack server create --wait --image cirros --flavor m1.tiny --key-name mykey --network demo-net lb_member --wait
+    member_fip=$(openstack floating ip create public1 -f value -c floating_ip_address)
+    openstack server add floating ip lb_member ${member_fip}
+    member_ip=$(openstack floating ip show ${member_fip} -f value -c fixed_ip_address)
+
+    # Dummy HTTP server.
+    attempts=12
+    for i in $(seq 1 ${attempts}); do
+        if ssh -v -o BatchMode=yes -o StrictHostKeyChecking=no cirros@${member_fip} 'nohup sh -c "while true; do echo -e \"HTTP/1.1 200 OK\n\n $(date)\" | sudo nc -l -p 8000; done &"'; then
+            break
+        elif [[ $i -eq ${attempts} ]]; then
+            echo "Failed to access server via SSH after ${attempts} attempts"
+            echo "Console log:"
+            openstack console log show lb_member
+            return 1
+        else
+            echo "Cannot access server - retrying"
+        fi
+        sleep 10
+    done
+
+    echo "Creating Octavia OVN LB:"
+    openstack loadbalancer create --vip-network-id demo-net --provider ovn --name test_ovn_lb --wait
+    openstack loadbalancer listener create --protocol TCP --protocol-port 8000 --name test_ovn_lb_listener --wait test_ovn_lb
+    openstack loadbalancer pool create --protocol TCP --lb-algorithm SOURCE_IP_PORT --listener test_ovn_lb_listener --name test_ovn_lb_pool --wait
+    subnet_id=$(openstack subnet list -c ID -f value --name demo-subnet)
+    openstack loadbalancer member create --address ${member_ip} --subnet-id ${subnet_id} --protocol-port 8000 --wait test_ovn_lb_pool
+    echo "Add a floating IP to the load balancer."
+    lb_fip=$(openstack floating ip create public1 -f value -c name)
+    lb_vip=$(openstack loadbalancer show test_ovn_lb -f value -c vip_address)
+    lb_port_id=$(openstack port list --fixed-ip ip-address=$lb_vip -f value -c ID)
+    openstack floating ip set $lb_fip --port $lb_port_id
+
+    echo "OVN NB entries for LB:"
+    sudo docker exec ovn_northd ovn-nbctl --db "$ovn_nb_connection" list load_balancer
+    echo "OVN NB entries for NAT:"
+    sudo docker exec ovn_northd ovn-nbctl --db "$ovn_nb_connection" list nat
+
+    echo "Attempt to access the load balanced HTTP server."
+    attempts=12
+    curl_args=(
+        --include
+        --location
+        --fail
+    )
+    for i in $(seq 1 ${attempts}); do
+        if curl "${curl_args[@]}" $lb_fip:8000; then
+            break
+        elif [[ $i -eq ${attempts} ]]; then
+            echo "Failed to access load balanced service after ${attempts} attempts"
+            return 1
+        else
+            echo "Cannot access load balancer - retrying"
+        fi
+        sleep 10
+    done
+
+    echo "Cleaning up"
+    openstack loadbalancer delete test_ovn_lb --cascade --wait
+    openstack floating ip delete ${lb_fip}
+    openstack server remove floating ip lb_member ${member_fip}
+    openstack floating ip delete ${member_fip}
+    openstack server delete --wait lb_member
+}
+
+function test_ovn_logged {
+    test_ovn
+    test_octavia
+}
+
+function test_ovn_setup {
+    echo "Testing OVN and Octavia OVN provider"
+    test_ovn_logged &> /tmp/logs/ansible/test-ovn
     result=$?
     if [[ $result != 0 ]]; then
         echo "Testing OVN failed. See ansible/test-ovn for details"
@@ -54,4 +132,6 @@ function test_ovn {
     return $result
 }
 
-test_ovn
+
+
+test_ovn_setup
diff --git a/zuul.d/base.yaml b/zuul.d/base.yaml
index f315d25afc4572306585976d608f3dbf816855a5..a580da819beca10f3fae07c17cab8d689a59fabf 100644
--- a/zuul.d/base.yaml
+++ b/zuul.d/base.yaml
@@ -209,7 +209,7 @@
     parent: kolla-ansible-base
     voting: false
     files:
-      - ^ansible/roles/(neutron|openvswitch|ovn)/
+      - ^ansible/roles/(neutron|octavia|openvswitch|ovn)/
       - ^tests/test-ovn.sh
       - ^tests/test-core-openstack.sh
     vars: