diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 0b0a96a3a..6cd607674 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -70,6 +70,8 @@ static const char *unixctl_path;
 static struct hmap macam = HMAP_INITIALIZER(&macam);
 static struct eth_addr mac_prefix;
 
+static bool controller_event_en;
+
 #define MAX_OVN_TAGS 4096
 
 /* Pipeline stages. */
@@ -3571,6 +3573,34 @@ build_pre_lb(struct ovn_datapath *od, struct hmap *lflows)
                 sset_add(&all_ips, ip_address);
             }
 
+            if (controller_event_en && !strlen(node->value)) {
+                struct ds match = DS_EMPTY_INITIALIZER;
+                char *action;
+
+                if (addr_family == AF_INET) {
+                    ds_put_format(&match, "ip && ip4.dst == %s && %s",
+                                  ip_address, lb->protocol);
+                } else {
+                    ds_put_format(&match, "ip && ip6.dst == %s && %s",
+                                  ip_address, lb->protocol);
+                }
+                if (port) {
+                    ds_put_format(&match, " && %s.dst == %u", lb->protocol,
+                                  port);
+                }
+                action = xasprintf("trigger_event(event = \"%s\", "
+                               "vip = \"%s\", protocol = \"%s\", "
+                               "load_balancer = \"" UUID_FMT "\");",
+                               event_to_string(OVN_EVENT_EMPTY_LB_BACKENDS),
+                               node->key, lb->protocol,
+                               UUID_ARGS(&lb->header_.uuid));
+                ovn_lflow_add(lflows, od, S_SWITCH_IN_PRE_LB, 120,
+                              ds_cstr(&match), action);
+                ds_destroy(&match);
+                free(action);
+                continue;
+            }
+
             free(ip_address);
 
             /* Ignore L4 port information in the key because fragmented packets
@@ -8044,6 +8074,9 @@ ovnnb_db_run(struct northd_context *ctx,
         smap_destroy(&options);
     }
 
+    controller_event_en = smap_get_bool(&nb->options,
+                                        "controller_event", false);
+
     cleanup_macam(&macam);
 }
 
diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
index 318379c1f..3d0249a73 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -107,6 +107,11 @@
         Configure a given OUI to be used as prefix when L2 address is
         dynamically assigned, e.g. <code>00:11:22</code>
       </column>
+
+      <column name="options" key="controller_event" type='{"type": "boolean"}'>
+        Value used to enable/disable ovn-controller event reporting to the CMS.
+        Please see the <ref table="Controller_Event"/> table in SBDB.
+      </column>
     </group>
 
     <group title="Connection Options">
diff --git a/tests/ovn.at b/tests/ovn.at
index cbfa702c7..aae92b5b3 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -14027,3 +14027,71 @@ ovn-hv4-0
 
 OVN_CLEANUP([hv1], [hv2], [hv3])
 AT_CLEANUP
+
+AT_SETUP([ovn -- controller event])
+AT_KEYWORDS([ovn_controller_event])
+ovn_start
+
+# Create hypervisors hv[12].
+# Add vif1[12] to hv1, vif2[12] to hv2
+# Add all of the vifs to a single logical switch sw0.
+
+net_add n1
+ovn-nbctl ls-add sw0
+for i in 1 2; do
+    sim_add hv$i
+    as hv$i
+    ovs-vsctl add-br br-phys
+    ovn_attach n1 br-phys 192.168.0.$i
+
+    for j in 1 2; do
+        ovn-nbctl lsp-add sw0 sw0-p$i$j -- \
+                lsp-set-addresses sw0-p$i$j "00:00:00:00:00:$i$j 192.168.1.$i$j"
+
+        ovs-vsctl -- add-port br-int vif$i$j -- \
+                set interface vif$i$j \
+                external-ids:iface-id=sw0-p$i$j \
+                options:tx_pcap=hv$i/vif$i$j-tx.pcap \
+                options:rxq_pcap=hv$i/vif$i$j-rx.pcap \
+                ofport-request=$i$j
+    done
+done
+
+ovn-nbctl --wait=hv set NB_Global . options:controller_event=true
+ovn-nbctl lb-add lb0 192.168.1.100:80 ""
+ovn-nbctl ls-lb-add sw0 lb0
+uuid_lb=$(ovn-nbctl --bare --columns=_uuid find load_balancer name=lb0)
+
+OVN_POPULATE_ARP
+ovn-nbctl --timeout=3 --wait=hv sync
+ovn-sbctl lflow-list
+as hv1 ovs-ofctl dump-flows br-int
+
+packet="inport==\"sw0-p11\" && eth.src==00:00:00:00:00:11 && eth.dst==00:00:00:00:00:21 &&
+       ip4 && ip.ttl==64 && ip4.src==192.168.1.11 && ip4.dst==192.168.1.100 &&
+       tcp && tcp.src==10000 && tcp.dst==80"
+as hv1 ovs-appctl -t ovn-controller inject-pkt "$packet"
+
+ovn-sbctl list controller_event
+uuid=$(ovn-sbctl list controller_event | awk '/_uuid/{print $3}')
+AT_CHECK([ovn-sbctl get controller_event $uuid event_type], [0], [dnl
+empty_lb_backends
+])
+AT_CHECK([ovn-sbctl get controller_event $uuid event_info:vip], [0], [dnl
+"192.168.1.100:80"
+])
+AT_CHECK([ovn-sbctl get controller_event $uuid event_info:protocol], [0], [dnl
+tcp
+])
+AT_CHECK_UNQUOTED([ovn-sbctl get controller_event $uuid event_info:load_balancer], [0], [dnl
+"$uuid_lb"
+])
+AT_CHECK([ovn-sbctl get controller_event $uuid handled], [0], [dnl
+false
+])
+AT_CHECK([ovn-sbctl get controller_event $uuid seq_num], [0], [dnl
+1
+])
+
+OVN_CLEANUP([hv1], [hv2])
+AT_CLEANUP
