diff --git a/inventory/sample/group_vars/k8s_cluster/addons.yml b/inventory/sample/group_vars/k8s_cluster/addons.yml
index 6c5ed80cb834aecbd15f4d8a4dcfd1bbfe98ef9f..2c6069cef941a259d947712ed968776a5f04bde6 100644
--- a/inventory/sample/group_vars/k8s_cluster/addons.yml
+++ b/inventory/sample/group_vars/k8s_cluster/addons.yml
@@ -157,11 +157,10 @@ metallb_speaker_enabled: true
 #     operator: "Equal"
 #     value: ""
 #     effect: "NoSchedule"
-# metallb_version: v0.9.6
+# metallb_version: v0.10.2
 # metallb_protocol: "layer2"
 # metallb_port: "7472"
-# metallb_limits_cpu: "100m"
-# metallb_limits_mem: "100Mi"
+# metallb_memberlist_port: "7946"
 # metallb_additional_address_pools:
 #   kube_service_pool:
 #     ip_range:
diff --git a/roles/kubernetes-apps/metallb/defaults/main.yml b/roles/kubernetes-apps/metallb/defaults/main.yml
index b98106c418096a3aa1a15f862913f1735a5b10fc..211bbb17690e6285196035dfa08162ca0c52584c 100644
--- a/roles/kubernetes-apps/metallb/defaults/main.yml
+++ b/roles/kubernetes-apps/metallb/defaults/main.yml
@@ -1,10 +1,9 @@
 ---
 metallb_enabled: false
-metallb_version: v0.9.6
+metallb_version: v0.10.2
 metallb_protocol: "layer2"
 metallb_port: "7472"
-metallb_limits_cpu: "100m"
-metallb_limits_mem: "100Mi"
+metallb_memberlist_port: "7946"
 metallb_peers: []
 metallb_speaker_enabled: true
 metallb_speaker_nodeselector: {}
@@ -12,6 +11,8 @@ metallb_controller_nodeselector: {}
 metallb_speaker_tolerations:
   - effect: NoSchedule
     key: node-role.kubernetes.io/master
+    operator: Exists
   - effect: NoSchedule
     key: node-role.kubernetes.io/control-plane
+    operator: Exists
 metallb_controller_tolerations: []
diff --git a/roles/kubernetes-apps/metallb/tasks/main.yml b/roles/kubernetes-apps/metallb/tasks/main.yml
index 551f2f28ae34b4d18c115db141cc35be1e792c6f..b24752ab8382df8d8f8df5e767b5f8c43ddb55ec 100644
--- a/roles/kubernetes-apps/metallb/tasks/main.yml
+++ b/roles/kubernetes-apps/metallb/tasks/main.yml
@@ -50,25 +50,3 @@
   with_items: "{{ rendering.results }}"
   when:
     - "inventory_hostname == groups['kube_control_plane'][0]"
-
-- name: Kubernetes Apps | Check existing secret of MetalLB
-  command: "{{ bin_dir }}/kubectl --kubeconfig /etc/kubernetes/admin.conf -n metallb-system get secret memberlist"
-  register: metallb_secret
-  become: true
-  ignore_errors: true  # noqa ignore-errors
-  when:
-    - inventory_hostname == groups['kube_control_plane'][0]
-
-- name: Kubernetes Apps | Create random bytes for MetalLB
-  command: "openssl rand -base64 32"
-  register: metallb_rand
-  when:
-    - inventory_hostname == groups['kube_control_plane'][0]
-    - metallb_secret.rc != 0
-
-- name: Kubernetes Apps | Install secret of MetalLB if not existing
-  command: "{{ bin_dir }}/kubectl --kubeconfig /etc/kubernetes/admin.conf -n metallb-system create secret generic memberlist --from-literal=secretkey={{ metallb_rand.stdout }}"
-  become: true
-  when:
-    - inventory_hostname == groups['kube_control_plane'][0]
-    - metallb_secret.rc != 0
diff --git a/roles/kubernetes-apps/metallb/templates/metallb.yml.j2 b/roles/kubernetes-apps/metallb/templates/metallb.yml.j2
index 5da5d9bfabc3a0fe744ba2825f706c6f5fd90f4a..cde041549aec1cd447cd08e8c4da587e89108aa5 100644
--- a/roles/kubernetes-apps/metallb/templates/metallb.yml.j2
+++ b/roles/kubernetes-apps/metallb/templates/metallb.yml.j2
@@ -58,9 +58,7 @@ metadata:
 spec:
   allowPrivilegeEscalation: false
   allowedCapabilities:
-  - NET_ADMIN
   - NET_RAW
-  - SYS_ADMIN
   allowedHostPaths: []
   defaultAddCapabilities: []
   defaultAllowPrivilegeEscalation: false
@@ -72,6 +70,8 @@ spec:
   hostPorts:
   - max: {{ metallb_port }}
     min: {{ metallb_port }}
+  - max: {{ metallb_memberlist_port }}
+    min: {{ metallb_memberlist_port }}
   privileged: true
   readOnlyRootFilesystem: true
   requiredDropCapabilities:
@@ -121,7 +121,6 @@ rules:
   - get
   - list
   - watch
-  - update
 - apiGroups:
   - ''
   resources:
@@ -162,6 +161,13 @@ rules:
   - get
   - list
   - watch
+- apiGroups: ["discovery.k8s.io"]
+  resources:
+  - endpointslices
+  verbs:
+  - get
+  - list
+  - watch
 - apiGroups:
   - ''
   resources:
@@ -212,6 +218,37 @@ rules:
   - list
 ---
 apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  labels:
+    app: metallb
+  name: controller
+  namespace: metallb-system
+rules:
+- apiGroups:
+  - ''
+  resources:
+  - secrets
+  verbs:
+  - create
+- apiGroups:
+  - ''
+  resources:
+  - secrets
+  resourceNames:
+  - memberlist
+  verbs:
+  - list
+- apiGroups:
+  - apps
+  resources:
+  - deployments
+  resourceNames:
+  - controller
+  verbs:
+  - get
+---
+apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRoleBinding
 metadata:
   labels:
@@ -275,6 +312,21 @@ subjects:
 - kind: ServiceAccount
   name: speaker
 ---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  labels:
+    app: metallb
+  name: controller
+  namespace: metallb-system
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: controller
+subjects:
+- kind: ServiceAccount
+  name: controller
+---
 {% if metallb_speaker_enabled %}
 apiVersion: apps/v1
 kind: DaemonSet
@@ -316,36 +368,32 @@ spec:
             fieldRef:
               fieldPath: status.podIP
         # needed when another software is also using memberlist / port 7946
+        # when changing this default you also need to update the container ports definition
+        # and the PodSecurityPolicy hostPorts definition
         #- name: METALLB_ML_BIND_PORT
-        #  value: "7946"
+        #  value: "{{ metallb_memberlist_port }}"
         - name: METALLB_ML_LABELS
           value: "app=metallb,component=speaker"
-        - name: METALLB_ML_NAMESPACE
-          valueFrom:
-            fieldRef:
-              fieldPath: metadata.namespace
         - name: METALLB_ML_SECRET_KEY
           valueFrom:
             secretKeyRef:
               name: memberlist
               key: secretkey
         image: {{ metallb_speaker_image_repo }}:{{ metallb_version }}
-        imagePullPolicy: {{ k8s_image_pull_policy }}
         name: speaker
         ports:
         - containerPort: {{ metallb_port }}
           name: monitoring
-        resources:
-          limits:
-            cpu: {{ metallb_limits_cpu }}
-            memory: {{ metallb_limits_mem }}
+        - containerPort: {{ metallb_memberlist_port }}
+          name: memberlist-tcp
+        - containerPort: {{ metallb_memberlist_port }}
+          name: memberlist-udp
+          protocol: UDP
         securityContext:
           allowPrivilegeEscalation: false
           capabilities:
             add:
-            - NET_ADMIN
             - NET_RAW
-            - SYS_ADMIN
             drop:
             - ALL
           readOnlyRootFilesystem: true
@@ -399,16 +447,16 @@ spec:
       - args:
         - --port={{ metallb_port }}
         - --config=config
+        env:
+        - name: METALLB_ML_SECRET_NAME
+          value: memberlist
+        - name: METALLB_DEPLOYMENT
+          value: controller
         image: {{ metallb_controller_image_repo }}:{{ metallb_version }}
-        imagePullPolicy: {{ k8s_image_pull_policy }}
         name: controller
         ports:
         - containerPort: {{ metallb_port }}
           name: monitoring
-        resources:
-          limits:
-            cpu: {{ metallb_limits_cpu }}
-            memory: {{ metallb_limits_mem }}
         securityContext:
           allowPrivilegeEscalation: false
           capabilities: