diff --git a/inventory/sample/group_vars/k8s-cluster/k8s-cluster.yml b/inventory/sample/group_vars/k8s-cluster/k8s-cluster.yml
index c3ec03e7ff2c6f8d843b267f9811db09f99366c7..fe1c184e60ec0768180823e9e5827f8b9e849c1b 100644
--- a/inventory/sample/group_vars/k8s-cluster/k8s-cluster.yml
+++ b/inventory/sample/group_vars/k8s-cluster/k8s-cluster.yml
@@ -329,5 +329,6 @@ persistent_volumes_enabled: false
 
 ## Amount of time to retain events. (default 1h0m0s)
 event_ttl_duration: "1h0m0s"
-##  Force regeneration of kubernetes control plane certificates without the need of bumping the cluster version
-force_certificate_regeneration: false
+
+## Automatically renew K8S control plane certificates on first Monday of each month
+auto_renew_certificates: false
diff --git a/roles/kubernetes/control-plane/defaults/main/main.yml b/roles/kubernetes/control-plane/defaults/main/main.yml
index 24c1ddff0c8f56cb4a9f11e975cbed4061f9f0aa..c671326dd7c23da89e6d5795ce73b378f6dc709f 100644
--- a/roles/kubernetes/control-plane/defaults/main/main.yml
+++ b/roles/kubernetes/control-plane/defaults/main/main.yml
@@ -194,5 +194,6 @@ secrets_encryption_query: "resources[*].providers[0].{{kube_encryption_algorithm
 
 ## Amount of time to retain events. (default 1h0m0s)
 event_ttl_duration: "1h0m0s"
-##  Force regeneration of kubernetes control plane certificates without the need of bumping the cluster version
-force_certificate_regeneration: false
+
+## Automatically renew K8S control plane certificates on first Monday of each month
+auto_renew_certificates: false
diff --git a/roles/kubernetes/control-plane/tasks/kubeadm-setup.yml b/roles/kubernetes/control-plane/tasks/kubeadm-setup.yml
index eab2fff1a902425af860aec5e670b6d401e61f94..5a51a24be83737942c35ebbcb190a41019c1954d 100644
--- a/roles/kubernetes/control-plane/tasks/kubeadm-setup.yml
+++ b/roles/kubernetes/control-plane/tasks/kubeadm-setup.yml
@@ -99,7 +99,7 @@
   when:
     - inventory_hostname == groups['kube-master']|first
     - kubeadm_already_run.stat.exists
-    - apiserver_sans_check.changed or force_certificate_regeneration
+    - apiserver_sans_check.changed
 
 - name: kubeadm | regenerate apiserver cert 2/2
   command: >-
@@ -109,7 +109,7 @@
   when:
     - inventory_hostname == groups['kube-master']|first
     - kubeadm_already_run.stat.exists
-    - apiserver_sans_check.changed or force_certificate_regeneration
+    - apiserver_sans_check.changed
 
 - name: kubeadm | Initialize first master
   command: >-
diff --git a/roles/kubernetes/control-plane/tasks/main.yml b/roles/kubernetes/control-plane/tasks/main.yml
index 8bfc8d75d8dc0df29b1f3c1f8830e762427cef65..6fba951c28d5fe0d516aa241937ab8d7fd80b74a 100644
--- a/roles/kubernetes/control-plane/tasks/main.yml
+++ b/roles/kubernetes/control-plane/tasks/main.yml
@@ -66,3 +66,27 @@
 - name: Include kubelet client cert rotation fixes
   include_tasks: kubelet-fix-client-cert-rotation.yml
   when: kubelet_rotate_certificates
+
+- name: Install script to renew K8S control plane certificates
+  template:
+    src: k8s-certs-renew.sh.j2
+    dest: "{{ bin_dir }}/k8s-certs-renew.sh"
+    mode: '755'
+
+- name: Renew K8S control plane certificates monthly 1/2
+  template:
+    src: "{{ item }}.j2"
+    dest: "/etc/systemd/system/{{ item }}"
+  with_items:
+    - k8s-certs-renew.service
+    - k8s-certs-renew.timer
+  register: k8s_certs_units
+  when: auto_renew_certificates
+
+- name: Renew K8S control plane certificates monthly 2/2
+  systemd:
+    name: k8s-certs-renew.timer
+    enabled: yes
+    state: started
+    daemon-reload: "{{ k8s_certs_units is changed }}"
+  when: auto_renew_certificates
diff --git a/roles/kubernetes/control-plane/templates/k8s-certs-renew.service.j2 b/roles/kubernetes/control-plane/templates/k8s-certs-renew.service.j2
new file mode 100644
index 0000000000000000000000000000000000000000..64610c2bc0209d40066f53142bd42fa508d2cfc5
--- /dev/null
+++ b/roles/kubernetes/control-plane/templates/k8s-certs-renew.service.j2
@@ -0,0 +1,6 @@
+[Unit]
+Description=Renew K8S control plane certificates
+
+[Service]
+Type=oneshot
+ExecStart={{ bin_dir }}/k8s-certs-renew.sh
diff --git a/roles/kubernetes/control-plane/templates/k8s-certs-renew.sh.j2 b/roles/kubernetes/control-plane/templates/k8s-certs-renew.sh.j2
new file mode 100644
index 0000000000000000000000000000000000000000..41656e3bb2cbb216a18f3a3731c822c97b9651f8
--- /dev/null
+++ b/roles/kubernetes/control-plane/templates/k8s-certs-renew.sh.j2
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+echo "## Expiration before renewal ##"
+{{ bin_dir }}/kubeadm certs check-expiration
+
+echo "## Renewing certificates managed by kubeadm ##"
+{{ bin_dir }}/kubeadm certs renew all
+
+echo "## Restarting control plane pods managed by kubeadm ##"
+{% if container_manager == "docker" %}
+{{ docker_bin_dir }}/docker ps -af 'name=k8s_POD_(kube-apiserver|kube-controller-manager|kube-scheduler|etcd)-*' -q | /usr/bin/xargs {{ docker_bin_dir }}/docker rm -f"
+{% else %}
+{{ bin_dir }}/crictl pods --namespace kube-system --name 'kube-scheduler-*|kube-controller-manager-*|kube-apiserver-*|etcd-*' -q | /usr/bin/xargs {{ bin_dir }}/crictl rmp -f
+{% endif %}
+
+echo "## Updating /root/.kube/config ##"
+/usr/bin/cp {{ kube_config_dir }}/admin.conf /root/.kube/config
+
+echo "## Waiting for apiserver to be up again ##"
+until printf "" 2>>/dev/null >>/dev/tcp/127.0.0.1/6443; do sleep 1; done
+
+echo "## Expiration after renewal ##"
+{{ bin_dir }}/kubeadm certs check-expiration
diff --git a/roles/kubernetes/control-plane/templates/k8s-certs-renew.timer.j2 b/roles/kubernetes/control-plane/templates/k8s-certs-renew.timer.j2
new file mode 100644
index 0000000000000000000000000000000000000000..3c5e0c18f3d091ad63fbfa4bcbaf9467035a16bf
--- /dev/null
+++ b/roles/kubernetes/control-plane/templates/k8s-certs-renew.timer.j2
@@ -0,0 +1,9 @@
+[Unit]
+Description=Timer to renew K8S control plane certificates
+
+[Timer]
+# First Monday of each month
+OnCalendar=Mon *-*-1..7 03:{{ groups['kube-master'].index(inventory_hostname) }}0:00
+
+[Install]
+WantedBy=multi-user.target
diff --git a/roles/reset/tasks/main.yml b/roles/reset/tasks/main.yml
index 1a78788f7b29b981965fcbbf6d2ece56089d7922..9a65c113547268abacf651b9818706540379edfb 100644
--- a/roles/reset/tasks/main.yml
+++ b/roles/reset/tasks/main.yml
@@ -21,6 +21,8 @@
     - containerd.service.d/http-proxy.conf
     - crio.service.d/http-proxy.conf
     - vault.service.d/http-proxy.conf
+    - k8s-certs-renew.service
+    - k8s-certs-renew.timer
   register: services_removed
   tags:
     - services
@@ -292,6 +294,7 @@
     - "{{ bin_dir }}/weave"
     - "{{ bin_dir }}/crictl"
     - "{{ bin_dir }}/netctl"
+    - "{{ bin_dir }}/k8s-certs-renew.sh"
     - /var/lib/cni
     - /etc/openvswitch
     - /run/openvswitch