diff --git a/contrib/terraform/openstack/README.md b/contrib/terraform/openstack/README.md
index a8e7cb8f26381cb9ab9943f7d70cfe4fd37e9b6e..8162a9f949d65b47d20f28bd492565ec54944d36 100644
--- a/contrib/terraform/openstack/README.md
+++ b/contrib/terraform/openstack/README.md
@@ -300,10 +300,10 @@ For your cluster, edit `inventory/$CLUSTER/cluster.tfvars`.
 |`force_null_port_security` | Set `null` instead of `true` or `false` for `port_security`. `false` by default |
 |`k8s_nodes` | Map containing worker node definition, see explanation below |
 |`k8s_masters` | Map containing master node definition, see explanation for k8s_nodes and `sample-inventory/cluster.tfvars` |
-| `k8s_master_loadbalancer_enabled`| Enable and use an Octavia load balancer for the K8s master nodes |
-| `k8s_master_loadbalancer_listener_port` | Define via which port the K8s Api should be exposed. `6443` by default  |
-| `k8s_master_loadbalancer_server_port` | Define via which port the K8S api is available on the mas. `6443` by default |
-| `k8s_master_loadbalancer_public_ip` | Specify if an existing floating IP should be used for the load balancer. A new floating IP is assigned by default  |
+|`k8s_master_loadbalancer_enabled` | Enable and use an Octavia load balancer for the K8s master nodes |
+|`k8s_master_loadbalancer_listener_port` | Define via which port the K8s Api should be exposed. `6443` by default  |
+|`k8s_master_loadbalancer_server_port` | Define via which port the K8S api is available on the master nodes. `6443` by default |
+|`k8s_master_loadbalancer_public_ip` | Specify if an existing floating IP should be used for the load balancer. A new floating IP is assigned by default  |
 
 ##### k8s_nodes
 
@@ -318,7 +318,8 @@ k8s_nodes:
    node-name:
     az: string # Name of the AZ
     flavor: string # Flavor ID to use
-    floating_ip: bool # If floating IPs should be created or not
+    floating_ip: bool # If floating IPs should be used or not
+    reserved_floating_ip: string # If floating_ip is true use existing floating IP, if reserved_floating_ip is an empty string and floating_ip is true, a new floating IP will be created
     extra_groups: string # (optional) Additional groups to add for kubespray, defaults to no groups
     image_id: string # (optional) Image ID to use, defaults to var.image_id or var.image
     root_volume_size_in_gb: number # (optional) Size of the block storage to use as root disk, defaults to var.node_root_volume_size_in_gb or to use volume from flavor otherwise
diff --git a/contrib/terraform/openstack/modules/compute/variables.tf b/contrib/terraform/openstack/modules/compute/variables.tf
index 006cce9efe8979dab8efffc17eb4ae6bebf009be..ed478de3c2fbd86ac15a2d8f216ec38c313d08bb 100644
--- a/contrib/terraform/openstack/modules/compute/variables.tf
+++ b/contrib/terraform/openstack/modules/compute/variables.tf
@@ -89,11 +89,15 @@ variable "k8s_node_fips" {
 }
 
 variable "k8s_masters_fips" {
-  type = map
+  type = map(object({
+    address = string
+  }))
 }
 
 variable "k8s_nodes_fips" {
-  type = map
+  type = map(object({
+    address = string
+  }))
 }
 
 variable "bastion_fips" {
@@ -136,8 +140,9 @@ variable "k8s_masters" {
   type = map(object({
     az                     = string
     flavor                 = string
-    floating_ip            = bool
     etcd                   = bool
+    floating_ip            = bool
+    reserved_floating_ip   = optional(string)
     image_id               = optional(string)
     root_volume_size_in_gb = optional(number)
     volume_type            = optional(string)
@@ -150,6 +155,7 @@ variable "k8s_nodes" {
     az                     = string
     flavor                 = string
     floating_ip            = bool
+    reserved_floating_ip   = optional(string)
     extra_groups           = optional(string)
     image_id               = optional(string)
     root_volume_size_in_gb = optional(number)
diff --git a/contrib/terraform/openstack/modules/ips/main.tf b/contrib/terraform/openstack/modules/ips/main.tf
index b0989dcc2242f9348363b42d166b54ebba6b6761..55ef82673df417c3ff61d9cd264ed017ffb70d9a 100644
--- a/contrib/terraform/openstack/modules/ips/main.tf
+++ b/contrib/terraform/openstack/modules/ips/main.tf
@@ -15,7 +15,7 @@ resource "openstack_networking_floatingip_v2" "k8s_master" {
 }
 
 resource "openstack_networking_floatingip_v2" "k8s_masters" {
-  for_each   = var.number_of_k8s_masters == 0 && var.number_of_k8s_masters_no_etcd == 0 ? { for key, value in var.k8s_masters : key => value if value.floating_ip } : {}
+  for_each   = var.number_of_k8s_masters == 0 && var.number_of_k8s_masters_no_etcd == 0 ? { for key, value in var.k8s_masters : key => value if value.floating_ip && (lookup(value, "reserved_floating_ip", "") == "") } : {}
   pool       = var.floatingip_pool
   depends_on = [null_resource.dummy_dependency]
 }
@@ -40,7 +40,7 @@ resource "openstack_networking_floatingip_v2" "bastion" {
 }
 
 resource "openstack_networking_floatingip_v2" "k8s_nodes" {
-  for_each   = var.number_of_k8s_nodes == 0 ? { for key, value in var.k8s_nodes : key => value if value.floating_ip } : {}
+  for_each   = var.number_of_k8s_nodes == 0 ? { for key, value in var.k8s_nodes : key => value if value.floating_ip && (lookup(value, "reserved_floating_ip", "") == "") } : {}
   pool       = var.floatingip_pool
   depends_on = [null_resource.dummy_dependency]
 }
diff --git a/contrib/terraform/openstack/modules/ips/outputs.tf b/contrib/terraform/openstack/modules/ips/outputs.tf
index 3ff4622abf607bfd58afc6a4a010048f4e9a7e07..670481109af473e4e334e4cf57fd7ad345d8fa4c 100644
--- a/contrib/terraform/openstack/modules/ips/outputs.tf
+++ b/contrib/terraform/openstack/modules/ips/outputs.tf
@@ -1,10 +1,33 @@
+locals {
+  k8s_masters_reserved_fips = {
+    for key, value in var.k8s_masters : key => {
+      address = value.reserved_floating_ip
+    } if value.floating_ip && (lookup(value, "reserved_floating_ip", "") != "")
+  }
+  k8s_masters_create_fips = {
+    for key, value in openstack_networking_floatingip_v2.k8s_masters : key => {
+      address = value.address
+    }
+  }
+  k8s_nodes_reserved_fips = {
+    for key, value in var.k8s_nodes : key => {
+      address = value.reserved_floating_ip
+    } if value.floating_ip && (lookup(value, "reserved_floating_ip", "") != "")
+  }
+  k8s_nodes_create_fips = {
+    for key, value in openstack_networking_floatingip_v2.k8s_nodes : key => {
+      address = value.address
+    }
+  }
+}
+
 # If k8s_master_fips is already defined as input, keep the same value since new FIPs have not been created.
 output "k8s_master_fips" {
   value = length(var.k8s_master_fips) > 0 ? var.k8s_master_fips : openstack_networking_floatingip_v2.k8s_master[*].address
 }
 
 output "k8s_masters_fips" {
-  value = openstack_networking_floatingip_v2.k8s_masters
+  value = merge(local.k8s_masters_create_fips, local.k8s_masters_reserved_fips)
 }
 
 # If k8s_master_fips is already defined as input, keep the same value since new FIPs have not been created.
@@ -17,7 +40,7 @@ output "k8s_node_fips" {
 }
 
 output "k8s_nodes_fips" {
-  value = openstack_networking_floatingip_v2.k8s_nodes
+  value = merge(local.k8s_nodes_create_fips, local.k8s_nodes_reserved_fips)
 }
 
 output "bastion_fips" {