diff --git a/roles/etcd/tasks/install_docker.yml b/roles/etcd/tasks/install_docker.yml
index 5ec124308584c8bb920b0adaa5b9ab251b12388d..cc2fdecf51e29b9feb64671365fc4d3c45f6b469 100644
--- a/roles/etcd/tasks/install_docker.yml
+++ b/roles/etcd/tasks/install_docker.yml
@@ -1,9 +1,4 @@
 ---
-
-- name: Install etcdctl from docker
-  import_tasks: install_etcdctl_docker.yml
-  when: etcd_cluster_setup
-
 - name: Get currently-deployed etcd version
   shell: "{{ docker_bin_dir }}/docker ps --filter='name={{ etcd_member_name }}' --format='{{ '{{ .Image }}' }}'"
   register: etcd_current_docker_image
diff --git a/roles/etcd/tasks/install_etcdctl_docker.yml b/roles/etcd/tasks/install_etcdctl_docker.yml
deleted file mode 100644
index 74ae07f18114766513760501cd75c3a5f8ee5bee..0000000000000000000000000000000000000000
--- a/roles/etcd/tasks/install_etcdctl_docker.yml
+++ /dev/null
@@ -1,11 +0,0 @@
----
-- name: Install | Copy etcdctl binary from docker container
-  command: sh -c "{{ docker_bin_dir }}/docker rm -f etcdctl-binarycopy;
-            {{ docker_bin_dir }}/docker create --name etcdctl-binarycopy {{ etcd_image_repo }}:{{ etcd_image_tag }} &&
-            {{ docker_bin_dir }}/docker cp etcdctl-binarycopy:/usr/local/bin/etcdctl {{ bin_dir }}/etcdctl &&
-            {{ docker_bin_dir }}/docker rm -f etcdctl-binarycopy"
-  register: etcdctl_install_result
-  until: etcdctl_install_result.rc == 0
-  retries: "{{ etcd_retries }}"
-  delay: "{{ retry_stagger | random + 3 }}"
-  changed_when: false
diff --git a/roles/etcd/tasks/install_host.yml b/roles/etcd/tasks/install_host.yml
index 4c2b3de8b32ef94806dfd0727235d237f081e7e9..d4baa2aac88eb19013d1e8bf6c88ac8bfdf95002 100644
--- a/roles/etcd/tasks/install_host.yml
+++ b/roles/etcd/tasks/install_host.yml
@@ -20,16 +20,7 @@
     - etcd_events_cluster_setup
     - etcd_version.lstrip('v') not in etcd_current_host_version.stdout | default('')
 
-- name: Install | Download etcd and etcdctl
-  include_tasks: "../../download/tasks/download_file.yml"
-  vars:
-    download: "{{ download_defaults | combine(downloads.etcd) }}"
-  when: etcd_cluster_setup
-  tags:
-    - never
-    - etcd
-
-- name: Install | Copy etcd and etcdctl binary from download dir
+- name: Install | Copy etcd binary from download dir
   copy:
     src: "{{ local_release_dir }}/etcd-{{ etcd_version }}-linux-{{ host_architecture }}/{{ item }}"
     dest: "{{ bin_dir }}/{{ item }}"
@@ -37,5 +28,4 @@
     remote_src: yes
   with_items:
     - etcd
-    - etcdctl
   when: etcd_cluster_setup
diff --git a/roles/etcd/tasks/main.yml b/roles/etcd/tasks/main.yml
index 53afecbb858d14e6eb1c8ecda840175af4db470f..40ca3de5f12eeb6fe05c6293f2916d6c85af491a 100644
--- a/roles/etcd/tasks/main.yml
+++ b/roles/etcd/tasks/main.yml
@@ -53,6 +53,17 @@
     - master
     - network
 
+- name: Install etcdctl and etcdutl binary
+  import_role:
+    name: etcdctl_etcdutl
+  tags:
+    - etcdctl
+    - etcdutl
+    - upgrade
+  when:
+    - inventory_hostname in groups['etcd']
+    - etcd_cluster_setup
+
 - name: Install etcd
   include_tasks: "install_{{ etcd_deployment_type }}.yml"
   when: is_etcd_master
diff --git a/roles/etcdctl/tasks/main.yml b/roles/etcdctl/tasks/main.yml
deleted file mode 100644
index e8f98f2324cf79b0a2979a1a5d484a66acfb3148..0000000000000000000000000000000000000000
--- a/roles/etcdctl/tasks/main.yml
+++ /dev/null
@@ -1,70 +0,0 @@
----
-# To get the binary from container to host, use the etcd data directory mounted
-# rw from host into the container.
-
-- name: Check unintentional include of this role
-  assert:
-    that: etcd_deployment_type == "kubeadm"
-
-- name: Check if etcdctl exist
-  stat:
-    path: "{{ bin_dir }}/etcdctl"
-    get_attributes: no
-    get_checksum: no
-    get_mime: no
-  register: stat_etcdctl
-
-- name: Remove old etcd binary
-  when: stat_etcdctl.stat.exists
-  block:
-  - name: Check version
-    command: "{{ bin_dir }}/etcdctl version"
-    register: etcdctl_version
-    check_mode: no
-    changed_when: false
-
-  - name: Remove old binary if version is not OK
-    file:
-      path: "{{ bin_dir }}/etcdctl"
-      state: absent
-    when: etcd_version.lstrip('v') not in etcdctl_version.stdout
-
-- name: Check if etcdctl still exist after version check
-  stat:
-    path: "{{ bin_dir }}/etcdctl"
-    get_attributes: no
-    get_checksum: no
-    get_mime: no
-  register: stat_etcdctl
-
-- name: Copy etcdctl script to host
-  when: not stat_etcdctl.stat.exists
-  block:
-  - name: Copy etcdctl script to host
-    shell: "{{ docker_bin_dir }}/docker cp \"$({{ docker_bin_dir }}/docker ps -qf ancestor={{ etcd_image_repo }}:{{ etcd_image_tag }})\":/usr/local/bin/etcdctl {{ etcd_data_dir }}/etcdctl"
-    when: container_manager ==  "docker"
-
-  - name: Copy etcdctl script to host
-    shell: >-
-      etcd_ctr_id="$({{ bin_dir }}/crictl ps -q --image {{ etcd_image_repo }}:{{ etcd_image_tag }})" &&
-      etcd_ctr_pid="$({{ bin_dir }}/crictl inspect --output go-template --template '{{ '{{' }} .info.pid {{ '}}' }}' ${etcd_ctr_id})" &&
-      cp /proc/${etcd_ctr_pid}/root/usr/local/bin/etcdctl {{ etcd_data_dir }}/etcdctl
-    when: container_manager in ['crio', 'containerd']
-
-  - name: Copy etcdctl to {{ bin_dir }}
-    copy:
-      src: "{{ etcd_data_dir }}/etcdctl"
-      dest: "{{ bin_dir }}"
-      remote_src: true
-      mode: 0755
-
-- name: Remove binary in etcd data dir
-  file:
-    path: "{{ etcd_data_dir }}/etcdctl"
-    state: absent
-
-- name: Create etcdctl wrapper script
-  template:
-    src: etcdctl.sh.j2
-    dest: "{{ bin_dir }}/etcdctl.sh"
-    mode: 0755
diff --git a/roles/etcdctl_etcdutl/tasks/main.yml b/roles/etcdctl_etcdutl/tasks/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..39e87dc31ce44fd86350ab2f9ef28e48b03e1b32
--- /dev/null
+++ b/roles/etcdctl_etcdutl/tasks/main.yml
@@ -0,0 +1,45 @@
+---
+- name: Copy etcdctl and etcdutl binary from docker container
+  command: sh -c "{{ docker_bin_dir }}/docker rm -f etcdxtl-binarycopy;
+            {{ docker_bin_dir }}/docker create --name etcdxtl-binarycopy {{ etcd_image_repo }}:{{ etcd_image_tag }} &&
+            {{ docker_bin_dir }}/docker cp etcdxtl-binarycopy:/usr/local/bin/{{ item }} {{ bin_dir }}/{{ item }} &&
+            {{ docker_bin_dir }}/docker rm -f etcdxtl-binarycopy"
+  with_items:
+    - etcdctl
+    - etcdutl
+  register: etcdxtl_install_result
+  until: etcdxtl_install_result.rc == 0
+  retries: "{{ etcd_retries }}"
+  delay: "{{ retry_stagger | random + 3 }}"
+  changed_when: false
+  when: container_manager ==  "docker"
+
+- name: Download etcd binary
+  include_tasks: "../../download/tasks/download_file.yml"
+  vars:
+    download: "{{ download_defaults | combine(downloads.etcd) }}"
+  when: container_manager in ['crio', 'containerd']
+
+- name: Copy etcd binary
+  unarchive:
+    src: "{{ downloads.etcd.dest }}"
+    dest: "{{ local_release_dir }}/"
+    remote_src: yes
+  when: container_manager in ['crio', 'containerd']
+
+- name: Copy etcdctl and etcdutl binary from download dir
+  copy:
+    src: "{{ local_release_dir }}/etcd-{{ etcd_version }}-linux-{{ host_architecture }}/{{ item }}"
+    dest: "{{ bin_dir }}/{{ item }}"
+    mode: 0755
+    remote_src: yes
+  with_items:
+    - etcdctl
+    - etcdutl
+  when: container_manager in ['crio', 'containerd']
+
+- name: Create etcdctl wrapper script
+  template:
+    src: etcdctl.sh.j2
+    dest: "{{ bin_dir }}/etcdctl.sh"
+    mode: 0755
diff --git a/roles/etcdctl/templates/etcdctl.sh.j2 b/roles/etcdctl_etcdutl/templates/etcdctl.sh.j2
similarity index 54%
rename from roles/etcdctl/templates/etcdctl.sh.j2
rename to roles/etcdctl_etcdutl/templates/etcdctl.sh.j2
index 266bcfdadd890c45acfc59a2a31439148a575c99..71578fa0bba3959286e3baa070c66780278fcbe4 100644
--- a/roles/etcdctl/templates/etcdctl.sh.j2
+++ b/roles/etcdctl_etcdutl/templates/etcdctl.sh.j2
@@ -3,6 +3,12 @@
 # example invocation: etcdctl.sh get --keys-only --from-key ""
 
 etcdctl \
+{% if etcd_deployment_type == "kubeadm" %}
   --cacert {{ kube_cert_dir }}/etcd/ca.crt \
   --cert {{ kube_cert_dir }}/etcd/server.crt \
   --key {{ kube_cert_dir }}/etcd/server.key "$@"
+{% else %}
+  --cacert {{ etcd_cert_dir }}/etcd/ca.crt \
+  --cert {{ etcd_cert_dir }}/etcd/server.crt \
+  --key {{ etcd_cert_dir }}/etcd/server.key "$@"
+  {% endif %}
\ No newline at end of file
diff --git a/roles/kubernetes/control-plane/tasks/kubeadm-etcd.yml b/roles/kubernetes/control-plane/tasks/kubeadm-etcd.yml
index ae47354527a505182186ba3a460eaeef27e92fbe..9de55c544593ad6fb8afaa02436e26bdc20f3c49 100644
--- a/roles/kubernetes/control-plane/tasks/kubeadm-etcd.yml
+++ b/roles/kubernetes/control-plane/tasks/kubeadm-etcd.yml
@@ -12,10 +12,13 @@
   tags:
     - network
 
-- name: Ensure etcdctl script is installed
+- name: Ensure etcdctl and etcdutl script is installed
   import_role:
-    name: etcdctl
+    name: etcdctl_etcdutl
   when: etcd_deployment_type == "kubeadm"
+  tags:
+    - etcdctl
+    - etcdutl
 
 - name: Set ownership for etcd data directory
   file: