diff --git a/doc/source/configuration/reference/network.rst b/doc/source/configuration/reference/network.rst
index 7a73deafb6d14024fd9a6a7a7ec0ffab94ed4e78..f3d37da7457b235ec2546321f9419dda21df3f5c 100644
--- a/doc/source/configuration/reference/network.rst
+++ b/doc/source/configuration/reference/network.rst
@@ -359,6 +359,15 @@ The following attributes are supported:
 ``bridge_ports``
     For bridge interfaces, a list of names of network interfaces to add to the
     bridge.
+``bridge_stp``
+    .. note::
+
+       For Rocky Linux 9, the ``bridge_stp`` attribute is set to false to preserve
+       backwards compatibility with network scripts. This is because the Network
+       Manager sets STP to true by default on bridges.
+
+    Enable or disable the Spanning Tree Protocol (STP) on this bridge. Should be
+    set to a boolean value. The default is not set on Ubuntu systems.
 ``bond_mode``
     For bond interfaces, the bond's mode, e.g. 802.3ad.
 ``bond_ad_select``
diff --git a/kayobe/plugins/filter/networkd.py b/kayobe/plugins/filter/networkd.py
index 221272239d6b5705963e6732e91f9bbd512feb04..191c5ecc3194aead1c643987acdd9df2e650e1cc 100644
--- a/kayobe/plugins/filter/networkd.py
+++ b/kayobe/plugins/filter/networkd.py
@@ -117,6 +117,7 @@ def _bridge_netdev(context, name, inventory_hostname):
     """
     device = networks.net_interface(context, name, inventory_hostname)
     mtu = networks.net_mtu(context, name, inventory_hostname)
+    stp = networks.net_bridge_stp(context, name, inventory_hostname)
     config = [
         {
             'NetDev': [
@@ -126,6 +127,8 @@ def _bridge_netdev(context, name, inventory_hostname):
             ]
         }
     ]
+    if stp is not None:
+        config[0]['Bridge'] = [{'STP': stp}]
     return _filter_options(config)
 
 
diff --git a/kayobe/plugins/filter/networks.py b/kayobe/plugins/filter/networks.py
index 1d54a8c7bd9dba516c46c7013f1cf3ef48acdd27..a89462eba9a81d6301962068166bdb81f9191e40 100644
--- a/kayobe/plugins/filter/networks.py
+++ b/kayobe/plugins/filter/networks.py
@@ -274,6 +274,30 @@ def net_mtu(context, name, inventory_hostname=None):
     return mtu
 
 
+@jinja2.pass_context
+def net_bridge_stp(context, name, inventory_hostname=None):
+    """Return the Spanning Tree Protocol (STP) state for a bridge.
+
+    On RL9 if STP is not defined, default it to 'false' to preserve
+    compatibility with network scripts. STP is 'true' in NetworkManager
+    by default, so we set it to 'false' here.
+
+    :param context: Jinja2 Context object.
+    :param name: The name of the network.
+    :param inventory_hostname: Ansible inventory hostname.
+    :returns: A string "true" or "false" representing the STP state.
+    """
+    bridge_stp = net_attr(context, name, 'bridge_stp', inventory_hostname)
+    os_family = context['ansible_facts']['os_family']
+    if bridge_stp is None:
+        if os_family == 'RedHat':
+            return 'false'
+        else:
+            return None
+    bridge_stp = str(utils.call_bool_filter(context, bridge_stp)).lower()
+    return bridge_stp
+
+
 net_routes = _make_attr_filter('routes')
 net_rules = _make_attr_filter('rules')
 net_physical_network = _make_attr_filter('physical_network')
@@ -431,6 +455,7 @@ def net_bridge_obj(context, name, inventory_hostname=None):
     defroute = net_defroute(context, name, inventory_hostname)
     ethtool_opts = net_ethtool_opts(context, name, inventory_hostname)
     zone = net_zone(context, name, inventory_hostname)
+    stp = net_bridge_stp(context, name, inventory_hostname)
     vip_address = net_vip_address(context, name, inventory_hostname)
     allowed_addresses = [vip_address] if vip_address else None
     _validate_rules(rules)
@@ -450,6 +475,7 @@ def net_bridge_obj(context, name, inventory_hostname=None):
         'zone': zone,
         'allowed_addresses': allowed_addresses,
         'onboot': 'yes',
+        'stp': stp,
     }
     interface = {k: v for k, v in interface.items() if v is not None}
     return interface
@@ -734,6 +760,7 @@ def get_filters():
         'net_defroute': net_defroute,
         'net_ethtool_opts': net_ethtool_opts,
         'net_zone': net_zone,
+        'net_bridge_stp': net_bridge_stp,
         'net_interface_obj': net_interface_obj,
         'net_bridge_obj': net_bridge_obj,
         'net_bond_obj': net_bond_obj,
diff --git a/kayobe/tests/unit/plugins/filter/test_networkd.py b/kayobe/tests/unit/plugins/filter/test_networkd.py
index f2253877cbddd94ebd86a8c221a4a03b1dc39ff9..37fb272c403bc588fe0eb3b4dde0077e14c50aa0 100644
--- a/kayobe/tests/unit/plugins/filter/test_networkd.py
+++ b/kayobe/tests/unit/plugins/filter/test_networkd.py
@@ -65,6 +65,7 @@ class BaseNetworkdTest(unittest.TestCase):
         # Bandit complains about Jinja2 autoescaping without nosec.
         self.env = jinja2.Environment()  # nosec
         self.env.filters['bool'] = to_bool
+        self.variables.update({'ansible_facts': {'os_family': 'Debian'}})
         self.context = self._make_context(self.variables)
 
     def _make_context(self, parent):
@@ -202,7 +203,7 @@ class TestNetworkdNetDevs(BaseNetworkdTest):
         self.assertEqual(expected, devs)
 
     def test_bridge_all_options(self):
-        self._update_context({"net3_mtu": 1400})
+        self._update_context({"net3_mtu": 1400, "net3_bridge_stp": True})
         devs = networkd.networkd_netdevs(self.context, ["net3"])
         expected = {
             "50-kayobe-br0": [
@@ -211,6 +212,9 @@ class TestNetworkdNetDevs(BaseNetworkdTest):
                         {"Name": "br0"},
                         {"Kind": "bridge"},
                         {"MTUBytes": 1400},
+                    ],
+                    "Bridge": [
+                        {"STP": "true"},
                     ]
                 },
             ]
diff --git a/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2 b/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2
index a88e0ef6ea122fa0d1d0996b5e65ab1fad86030f..da0ea7765895e83e74900129405432a3f3a9ad17 100644
--- a/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2
+++ b/playbooks/kayobe-overcloud-host-configure-base/overrides.yml.j2
@@ -56,6 +56,7 @@ test_net_eth_vlan_zone: test-zone1
 test_net_bridge_cidr: 192.168.36.0/24
 test_net_bridge_interface: br0
 test_net_bridge_bridge_ports: [dummy3, dummy4]
+test_net_bridge_bridge_stp: false
 test_net_bridge_zone: test-zone2
 
 # br0.43: VLAN subinterface of br0.
@@ -80,6 +81,7 @@ test_net_bond_vlan_zone: public
 test_net_bridge_noip_cidr: 192.168.40.0/24
 test_net_bridge_noip_interface: br1
 test_net_bridge_noip_bridge_ports: [dummy7]
+test_net_bridge_noip_bridge_stp: true
 test_net_bridge_noip_no_ip: true
 
 {% if ansible_os_family == "Debian" %}
diff --git a/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py b/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py
index 896adae1490310cc390ac6b002bb70f090e5c98b..70c33ca7ad694b94d7228b52dcbc624a4ceea932 100644
--- a/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py
+++ b/playbooks/kayobe-overcloud-host-configure-base/tests/test_overcloud_host_configure.py
@@ -56,6 +56,8 @@ def test_network_bridge(host):
     interface = host.interface('br0')
     assert interface.exists
     assert '192.168.36.1' in interface.addresses
+    stp_status = host.file('/sys/class/net/br0/bridge/stp_state').content_string.strip()
+    assert '0' == stp_status
     ports = ['dummy3', 'dummy4']
     sys_ports = host.check_output('ls -1 /sys/class/net/br0/brif')
     assert sys_ports == "\n".join(ports)
@@ -100,6 +102,8 @@ def test_network_bridge_no_ip(host):
     interface = host.interface('br1')
     assert interface.exists
     assert not '192.168.40.1' in interface.addresses
+    stp_status = host.file('/sys/class/net/br1/bridge/stp_state').content_string.strip()
+    assert '1' == stp_status
 
 
 @pytest.mark.skipif(not _is_apt(),
diff --git a/releasenotes/notes/add-bridge-stp-option-9be6bf35f6425548.yaml b/releasenotes/notes/add-bridge-stp-option-9be6bf35f6425548.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..067d32acb62953e42d08ec27547e5170ebf96977
--- /dev/null
+++ b/releasenotes/notes/add-bridge-stp-option-9be6bf35f6425548.yaml
@@ -0,0 +1,12 @@
+features:
+  - |
+    The Spanning Tree Protocol (STP) can now be configured on bridge interfaces.
+    Enable or disable STP by setting the ``bridge_stp`` attribute for a network.
+    Note that STP is not set by default on Ubuntu, but it is disabled on Rocky
+    Linux 9 for compatibility with network scripts, as NetworkManager enables
+    STP on all bridges by default.
+upgrade:
+  - |
+    For Rocky Linux 9, Kayobe now disables STP on a bridge by default. This
+    action will cause the bridge interface to restart during the host
+    configuration process.