diff --git a/controller/binding.c b/controller/binding.c
index 52eb47b79..743809a21 100644
--- a/controller/binding.c
+++ b/controller/binding.c
@@ -600,6 +600,9 @@ static struct binding_lport *binding_lport_check_and_cleanup(
     struct binding_lport *, struct shash *b_lports);
 
 static char *get_lport_type_str(enum en_lport_type lport_type);
+static bool is_ovs_iface_matches_lport_vif_id(
+    const struct ovsrec_interface *,
+    const struct sbrec_port_binding *);
 
 void
 related_lports_init(struct related_lports *rp)
@@ -1165,9 +1168,30 @@ consider_vif_lport(const struct sbrec_port_binding *pb,
 
     struct binding_lport *b_lport = NULL;
     if (lbinding) {
-        struct shash *binding_lports =
-            &b_ctx_out->lbinding_data->lports;
-        b_lport = local_binding_add_lport(binding_lports, lbinding, pb, LP_VIF);
+        bool add_b_lport = true;
+
+        /* Make sure that the pb's vif-id if set matches with the
+         * lbinding's vif-id. */
+        if (lbinding->iface &&
+                !is_ovs_iface_matches_lport_vif_id(lbinding->iface, pb)) {
+            /* We can't associate the b_lport for this local_binding
+             * because the vif-id doesn't match. */
+            add_b_lport = false;
+
+            b_lport = local_binding_get_primary_lport(lbinding);
+            if (b_lport) {
+                binding_lport_delete(&b_ctx_out->lbinding_data->lports,
+                                     b_lport);
+                b_lport = NULL;
+            }
+        }
+
+        if (add_b_lport) {
+            struct shash *binding_lports =
+                &b_ctx_out->lbinding_data->lports;
+            b_lport = local_binding_add_lport(binding_lports, lbinding, pb,
+                                              LP_VIF);
+        }
     }
 
     return consider_vif_lport_(pb, can_bind, vif_chassis, b_ctx_in,
@@ -2850,3 +2874,20 @@ cleanup:
 
     return b_lport;
 }
+
+
+static bool
+is_ovs_iface_matches_lport_vif_id(const struct ovsrec_interface *iface,
+                                  const struct sbrec_port_binding *pb)
+{
+    const char *pb_vif_id = smap_get(&pb->options, "vif-id");
+    const char *vif_id = smap_get(&iface->external_ids, "vif-id");
+
+    if (pb_vif_id) {
+        if (!vif_id || strcmp(pb_vif_id, vif_id)) {
+            return false;
+        }
+    }
+
+    return true;
+}
diff --git a/ovn-nb.xml b/ovn-nb.xml
index 8a942b54c..4848dbfe0 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -1002,6 +1002,14 @@
           one chassis.
         </column>
 
+        <column name="options" key="vif-id">
+          If set, this port will be bound by <code>ovn-controller</code>
+          only if this same key and value is configured in the
+          <ref table="Interface" column="external_ids" db="Open_vSwitch"/>
+          column in the Open_vSwitch database's
+          <ref table="Interface" db="Open_vSwitch"/> table.
+        </column>
+
         <column name="options" key="qos_max_rate">
           If set, indicates the maximum rate for data sent from this interface,
           in bit/s. The traffic will be shaped according to this limit.
diff --git a/ovn-sb.xml b/ovn-sb.xml
index 687555c47..2163162b8 100644
--- a/ovn-sb.xml
+++ b/ovn-sb.xml
@@ -3149,6 +3149,14 @@ tcp.flags = RST;
         one chassis.
       </column>
 
+      <column name="options" key="vif-id">
+        If set, this port will be bound by <code>ovn-controller</code>
+        only if this same key and value is configured in the
+        <ref table="Interface" column="external_ids" db="Open_vSwitch"/>
+        column in the Open_vSwitch database's
+        <ref table="Interface" db="Open_vSwitch"/> table.
+      </column>
+
       <column name="options" key="qos_max_rate">
         If set, indicates the maximum rate for data sent from this interface,
         in bit/s. The traffic will be shaped according to this limit.
diff --git a/tests/ovn.at b/tests/ovn.at
index f2882d1ad..17ac2b78a 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -27900,3 +27900,144 @@ AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=24 | grep 10.0.1.2], [0], [ig
 OVN_CLEANUP([hv1],[hv2])
 AT_CLEANUP
 ])
+
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn-controller port binding with vif-id])
+ovn_start
+
+net_add n1
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+ovs-vsctl -- add-port br-int hv1-vm1
+
+ovn-nbctl ls-add sw0
+ovn-nbctl lsp-add sw0 sw0-port1
+ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01 10.0.0.3"
+
+ovn-nbctl lsp-set-options sw0-port1 vif-id=foo
+
+ovn-nbctl lsp-add sw0 sw0-port2
+ovn-nbctl --wait=hv lsp-set-addresses sw0-port2 "50:54:00:00:00:02 10.0.0.4"
+
+hv1_uuid=$(fetch_column Chassis _uuid name=hv1)
+hv2_uuid=$(fetch_column Chassis _uuid name=hv2)
+
+check as hv1 ovs-vsctl add-port br-int vif11 \
+    -- set interface vif11 external_ids:iface-id=sw0-port1
+
+# sw0-port1 should not be claimed.
+ovn-nbctl --wait=hv sync
+check_column "" Port_Binding chassis logical_port=sw0-port1
+
+AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-bindings], [0], [dnl
+Local bindings:
+name: [[sw0-port1]], OVS interface name : [[vif11]], num binding lports : [[0]]
+----------------------------------------
+])
+
+# Set vif-id on vif11. hv1 ovn-controller should claim it now.
+check as hv1 ovs-vsctl set interface vif11 external_ids:vif-id=foo
+
+wait_for_ports_up sw0-port1
+
+AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-bindings], [0], [dnl
+Local bindings:
+name: [[sw0-port1]], OVS interface name : [[vif11]], num binding lports : [[1]]
+primary lport : [[sw0-port1]]
+----------------------------------------
+])
+
+# Clear the vif-id from vif11 and hv1 ovn-controller should release it.
+check as hv1 ovs-vsctl remove interface vif11 external_ids vif-id
+ovn-nbctl --wait=hv sync
+check_column "" Port_Binding chassis logical_port=sw0-port1
+
+AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-bindings], [0], [dnl
+Local bindings:
+name: [[sw0-port1]], OVS interface name : [[vif11]], num binding lports : [[0]]
+----------------------------------------
+])
+
+# Clear the sw0-port1 vif-id options and sw0-port1 should be claimed.
+check ovn-nbctl clear logical_switch_port sw0-port1 options
+
+wait_for_ports_up sw0-port1
+
+AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-bindings], [0], [dnl
+Local bindings:
+name: [[sw0-port1]], OVS interface name : [[vif11]], num binding lports : [[1]]
+primary lport : [[sw0-port1]]
+----------------------------------------
+])
+
+# Set the options:vif-id to sw0-port1 with different value.
+check ovn-nbctl lsp-set-options sw0-port1 vif-id=bar
+
+ovn-nbctl --wait=hv sync
+check_column "" Port_Binding chassis logical_port=sw0-port1
+
+AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-bindings], [0], [dnl
+Local bindings:
+name: [[sw0-port1]], OVS interface name : [[vif11]], num binding lports : [[0]]
+----------------------------------------
+])
+
+# Set vif-id on vif11. hv1 ovn-controller should claim it now.
+check as hv1 ovs-vsctl set interface vif11 external_ids:vif-id=bar
+wait_for_ports_up sw0-port1
+
+AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-bindings], [0], [dnl
+Local bindings:
+name: [[sw0-port1]], OVS interface name : [[vif11]], num binding lports : [[1]]
+primary lport : [[sw0-port1]]
+----------------------------------------
+])
+
+# Set a different vif-id on vif11.
+check as hv1 ovs-vsctl set interface vif11 external_ids:vif-id=bar2
+
+ovn-nbctl --wait=hv sync
+check_column "" Port_Binding chassis logical_port=sw0-port1
+
+AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-bindings], [0], [dnl
+Local bindings:
+name: [[sw0-port1]], OVS interface name : [[vif11]], num binding lports : [[0]]
+----------------------------------------
+])
+
+# Set the type of sw0-port1 to localport. vif-id is ignored for localports.
+# So sw0-port1 should be internally claimed without setting sw0-port1 to up.
+check ovn-nbctl lsp-set-type sw0-port1 localport
+
+ovn-nbctl --wait=hv sync
+check_column "" Port_Binding chassis logical_port=sw0-port1
+
+AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-bindings], [0], [dnl
+Local bindings:
+name: [[sw0-port1]], OVS interface name : [[vif11]], num binding lports : [[1]]
+localport lport : [[sw0-port1]]
+----------------------------------------
+])
+
+check as hv1 ovs-vsctl add-port br-int vif12 \
+    -- set interface vif12 external_ids:iface-id=sw0-port2
+
+wait_for_ports_up sw0-port2
+
+AT_CHECK([as hv1 ovn-appctl -t ovn-controller debug/dump-local-bindings], [0], [dnl
+Local bindings:
+name: [[sw0-port1]], OVS interface name : [[vif11]], num binding lports : [[1]]
+localport lport : [[sw0-port1]]
+----------------------------------------
+name: [[sw0-port2]], OVS interface name : [[vif12]], num binding lports : [[1]]
+primary lport : [[sw0-port2]]
+----------------------------------------
+])
+
+OVN_CLEANUP([hv1])
+AT_CLEANUP
+])
