diff --git a/northd/inc-proc-northd.c b/northd/inc-proc-northd.c
index 4a3699106..cdfd39cc6 100644
--- a/northd/inc-proc-northd.c
+++ b/northd/inc-proc-northd.c
@@ -215,6 +215,7 @@ void inc_proc_northd_init(struct ovsdb_idl_loop *nb,
     engine_add_input(&en_northd, &en_sb_load_balancer, NULL);
     engine_add_input(&en_northd, &en_sb_fdb, NULL);
     engine_add_input(&en_northd, &en_sb_static_mac_binding, NULL);
+    engine_add_input(&en_mac_binding_aging, &en_nb_nb_global, NULL);
     engine_add_input(&en_mac_binding_aging, &en_sb_mac_binding, NULL);
     engine_add_input(&en_mac_binding_aging, &en_northd, NULL);
     engine_add_input(&en_mac_binding_aging, &en_mac_binding_aging_waker, NULL);
diff --git a/northd/mac-binding-aging.c b/northd/mac-binding-aging.c
index e8217e8bc..10e995f63 100644
--- a/northd/mac-binding-aging.c
+++ b/northd/mac-binding-aging.c
@@ -28,6 +28,8 @@
 
 VLOG_DEFINE_THIS_MODULE(mac_binding_aging);
 
+#define MAC_BINDING_BULK_REMOVAL_DELAY_MSEC 10
+
 struct mac_binding_waker {
     bool should_schedule;
     long long next_wake_msec;
@@ -39,7 +41,8 @@ static void
 mac_binding_aging_run_for_datapath(const struct sbrec_datapath_binding *dp,
                                    const struct nbrec_logical_router *nbr,
                                    struct ovsdb_idl_index *mb_by_datapath,
-                                   int64_t now, int64_t *wake_delay)
+                                   int64_t now, int64_t *wake_delay,
+                                   uint32_t removal_limit, uint32_t *removed_n)
 {
     uint64_t threshold = smap_get_uint(&nbr->options,
                                          "mac_binding_age_threshold",
@@ -60,6 +63,10 @@ mac_binding_aging_run_for_datapath(const struct sbrec_datapath_binding *dp,
             continue;
         } else if (elapsed >= threshold) {
             sbrec_mac_binding_delete(mb);
+            (*removed_n)++;
+            if (removal_limit && *removed_n == removal_limit) {
+                break;
+            }
         } else {
             *wake_delay = MIN(*wake_delay, threshold - elapsed);
         }
@@ -67,6 +74,20 @@ mac_binding_aging_run_for_datapath(const struct sbrec_datapath_binding *dp,
     sbrec_mac_binding_index_destroy_row(mb_index_row);
 }
 
+static uint32_t
+get_removal_limit(struct engine_node *node)
+{
+    const struct nbrec_nb_global_table *nb_global_table =
+        EN_OVSDB_GET(engine_get_input("NB_nb_global", node));
+    const struct nbrec_nb_global *nb =
+        nbrec_nb_global_table_first(nb_global_table);
+    if (!nb) {
+       return 0;
+    }
+
+    return smap_get_uint(&nb->options, "mac_binding_removal_limit", 0);
+}
+
 void
 en_mac_binding_aging_run(struct engine_node *node, void *data OVS_UNUSED)
 {
@@ -78,6 +99,8 @@ en_mac_binding_aging_run(struct engine_node *node, void *data OVS_UNUSED)
 
     int64_t next_expire_msec = INT64_MAX;
     int64_t now = time_wall_msec();
+    uint32_t removal_limit = get_removal_limit(node);
+    uint32_t removed_n = 0;
     struct northd_data *northd_data = engine_get_input_data("northd", node);
     struct ovsdb_idl_index *sbrec_mac_binding_by_datapath =
         engine_ovsdb_node_get_index(engine_get_input("SB_mac_binding", node),
@@ -88,7 +111,13 @@ en_mac_binding_aging_run(struct engine_node *node, void *data OVS_UNUSED)
         if (od->sb && od->nbr) {
             mac_binding_aging_run_for_datapath(od->sb, od->nbr,
                                                sbrec_mac_binding_by_datapath,
-                                               now, &next_expire_msec);
+                                               now, &next_expire_msec,
+                                               removal_limit, &removed_n);
+            if (removal_limit && removed_n == removal_limit) {
+                /* Schedule the next run after specified delay. */
+                next_expire_msec = MAC_BINDING_BULK_REMOVAL_DELAY_MSEC;
+                break;
+            }
         }
     }
 
diff --git a/ovn-nb.xml b/ovn-nb.xml
index d3fba9bdc..83f60265f 100644
--- a/ovn-nb.xml
+++ b/ovn-nb.xml
@@ -162,6 +162,13 @@
         dynamically assigned, e.g. <code>00:11:22</code>
       </column>
 
+      <column name="options" key="mac_binding_removal_limit"
+              type='{"type": "integer", "minInteger": 0, "maxInteger": 4294967295}'>
+        MAC binding aging bulk removal limit. This limits how many rows
+        can expire in a single transaction. Default value is 0 which
+        is unlimited.
+      </column>
+
       <column name="options" key="controller_event" type='{"type": "boolean"}'>
         Value set by the CMS to enable/disable ovn-controller event reporting.
         Traffic into OVS can raise a 'controller' event that results in a
