diff --git a/docs/vars.md b/docs/vars.md
index 959260e3138f8ef2d061a3b851b3fa1a05d47225..9c9f003998db9c89f9f84231e5545b058341d300 100644
--- a/docs/vars.md
+++ b/docs/vars.md
@@ -245,7 +245,7 @@ node_labels:
   label2_name: label2_value
 ```
 
-* *node_taints* - Taints applied to nodes via kubelet --register-with-taints parameter.
+* *node_taints* - Taints applied to nodes via `kubectl taint node`.
   For example, taints can be set in the inventory as variables or more widely in group_vars.
   *node_taints* has to be defined as a list of strings in format `key=value:effect`, e.g.:
 
diff --git a/playbooks/cluster.yml b/playbooks/cluster.yml
index a6fd770b9573f8355ff25b4071fb7580e0642a49..c433a8c69284e167cf6912c80e9af434edaf8461 100644
--- a/playbooks/cluster.yml
+++ b/playbooks/cluster.yml
@@ -48,6 +48,7 @@
     - { role: kubespray-defaults }
     - { role: kubernetes/kubeadm, tags: kubeadm}
     - { role: kubernetes/node-label, tags: node-label }
+    - { role: kubernetes/node-taint, tags: node-taint }
     - { role: network_plugin, tags: network }
     - { role: kubernetes-apps/kubelet-csr-approver, tags: kubelet-csr-approver }
 
diff --git a/playbooks/scale.yml b/playbooks/scale.yml
index b8f87f484e82ff1c4c8f4186a7f9a163e21188fd..171e378328d8f59e60cce91d0379fd5f6e5faf1c 100644
--- a/playbooks/scale.yml
+++ b/playbooks/scale.yml
@@ -91,6 +91,7 @@
     - { role: kubespray-defaults }
     - { role: kubernetes/kubeadm, tags: kubeadm }
     - { role: kubernetes/node-label, tags: node-label }
+    - { role: kubernetes/node-taint, tags: node-taint }
     - { role: network_plugin, tags: network }
 
 - name: Apply resolv.conf changes now that cluster DNS is up
diff --git a/playbooks/upgrade_cluster.yml b/playbooks/upgrade_cluster.yml
index a79cf0aa76364f821f613a53fb7fb72cb6726a79..3180fec9310e309d9c97e6858631e11738eb864e 100644
--- a/playbooks/upgrade_cluster.yml
+++ b/playbooks/upgrade_cluster.yml
@@ -55,6 +55,7 @@
     - { role: kubernetes/control-plane, tags: master, upgrade_cluster_setup: true }
     - { role: kubernetes/client, tags: client }
     - { role: kubernetes/node-label, tags: node-label }
+    - { role: kubernetes/node-taint, tags: node-taint }
     - { role: kubernetes-apps/cluster_roles, tags: cluster-roles }
     - { role: kubernetes-apps, tags: csi-driver }
     - { role: upgrade/post-upgrade, tags: post-upgrade }
@@ -87,6 +88,7 @@
     - { role: kubernetes/node, tags: node }
     - { role: kubernetes/kubeadm, tags: kubeadm }
     - { role: kubernetes/node-label, tags: node-label }
+    - { role: kubernetes/node-taint, tags: node-taint }
     - { role: upgrade/post-upgrade, tags: post-upgrade }
 
 - name: Patch Kubernetes for Windows
diff --git a/roles/kubernetes/node-taint/tasks/main.yml b/roles/kubernetes/node-taint/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0766dc4396c854ae2da221030185d187b24aab52
--- /dev/null
+++ b/roles/kubernetes/node-taint/tasks/main.yml
@@ -0,0 +1,35 @@
+---
+- name: Set role and inventory node taint to empty list
+  set_fact:
+    role_node_taints: []
+    inventory_node_taints: []
+
+- name: Node taint for nvidia GPU nodes
+  set_fact:
+    role_node_taints: "{{ role_node_taints + ['nvidia.com/gpu=:NoSchedule'] }}"
+  when:
+    - nvidia_gpu_nodes is defined
+    - nvidia_accelerator_enabled | bool
+    - inventory_hostname in nvidia_gpu_nodes
+
+- name: Populate inventory node taint
+  set_fact:
+    inventory_node_taints: "{{ inventory_node_taints + ['%s' | format(item)] }}"
+  loop: "{{ node_taints | d([]) }}"
+  when:
+    - node_taints is defined
+    - node_taints is not string
+    - node_taints is not mapping
+    - node_taints is iterable
+- debug:  # noqa name[missing]
+    var: role_node_taints
+- debug:  # noqa name[missing]
+    var: inventory_node_taints
+
+- name: Set taint to node
+  command: >-
+      {{ kubectl }} taint node {{ kube_override_hostname | default(inventory_hostname) }} {{ (role_node_taints + inventory_node_taints) | join(' ') }} --overwrite=true
+  delegate_to: "{{ groups['kube_control_plane'][0] }}"
+  changed_when: false
+  when:
+    - (role_node_taints + inventory_node_taints) | length > 0
diff --git a/roles/kubernetes/node/templates/kubelet.env.v1beta1.j2 b/roles/kubernetes/node/templates/kubelet.env.v1beta1.j2
index b8a22fd1ecb70fde8a050289fcdb957e500a527b..a5aa369df577c858dcfe9b93ad09b75c656a9e6b 100644
--- a/roles/kubernetes/node/templates/kubelet.env.v1beta1.j2
+++ b/roles/kubernetes/node/templates/kubelet.env.v1beta1.j2
@@ -15,17 +15,7 @@ KUBELET_HOSTNAME="--hostname-override={{ kube_override_hostname }}"
 --runtime-cgroups={{ kubelet_runtime_cgroups }} \
 {% endset %}
 
-{# Kubelet node taints for gpu #}
-{% if nvidia_gpu_nodes is defined and nvidia_accelerator_enabled|bool %}
-{%   if inventory_hostname in nvidia_gpu_nodes and node_taints is defined %}
-{%       set dummy = node_taints.append('nvidia.com/gpu=:NoSchedule') %}
-{%   elif inventory_hostname in nvidia_gpu_nodes and node_taints is not defined %}
-{%       set node_taints = [] %}
-{%       set dummy = node_taints.append('nvidia.com/gpu=:NoSchedule') %}
-{%   endif %}
-{% endif %}
-
-KUBELET_ARGS="{{ kubelet_args_base }} {% if node_taints|default([]) %}--register-with-taints={{ node_taints | join(',') }} {% endif %} {% if kubelet_custom_flags is string %} {{kubelet_custom_flags}} {% else %}{% for flag in kubelet_custom_flags %} {{flag}} {% endfor %}{% endif %}{% if inventory_hostname in groups['kube_node'] %}{% if kubelet_node_custom_flags is string %} {{kubelet_node_custom_flags}} {% else %}{% for flag in kubelet_node_custom_flags %} {{flag}} {% endfor %}{% endif %}{% endif %}"
+KUBELET_ARGS="{{ kubelet_args_base }} {% if kubelet_custom_flags is string %} {{kubelet_custom_flags}} {% else %}{% for flag in kubelet_custom_flags %} {{flag}} {% endfor %}{% endif %}{% if inventory_hostname in groups['kube_node'] %}{% if kubelet_node_custom_flags is string %} {{kubelet_node_custom_flags}} {% else %}{% for flag in kubelet_node_custom_flags %} {{flag}} {% endfor %}{% endif %}{% endif %}"
 {% if kubelet_flexvolumes_plugins_dir is defined %}
 KUBELET_VOLUME_PLUGIN="--volume-plugin-dir={{ kubelet_flexvolumes_plugins_dir }}"
 {% endif %}