Online resources that will help you prepare for taking the CNCF CKA 2020 "Kubernetes Certified Administrator" Certification exam. with time, This is not likely the comprehensive up to date list - please make a pull request if there something that should be added here.
Shell
4,456
153 commits
updated Sep 18, 2026
Online resources that will help you prepare for taking the Kubernetes Certified Administrator Certification exam.
Beyond the curriculum links, these cover method: how to diagnose problems and how to study.
Disclaimer: This is not likely a comprehensive list as the exam will be a moving target with the fast pace of k8s development - please make a pull request if there something wrong, should be added, or updated.
I tried to restrict the cross references of resources to kubernetes.io. Youtube videos and other blog resources are optional; however, I still found them useful in my k8s learning journey.
Ensure you have the right version of Kubernetes documentation selected (v1.35 as of the September 2026 exam environment) especially for API objects and annotations.
TL;DR: practice practice practice
These are the exam objectives you review and understand in order to pass the test.
| Exam environment | Kubernetes v1.35 |
| Duration | 2 hours |
| Passing score | 66% |
| Included | 2 exam attempts, 2 Killer.sh simulator sessions |
| Validity | 2 years |
The exam environment is aligned with the most recent Kubernetes minor release within roughly 4 to 8 weeks of that release, so confirm the version in the CKA/CKAD/CKS FAQ and on the CKA certification page before you sit it.
If you studied from an older version of this README or an older course, these are the differences that matter. Items marked new were not in the previous curriculum at all.
Domain weights are unchanged: Troubleshooting 30%, Cluster Architecture 25%, Services and Networking 20%, Workloads and Scheduling 15%, Storage 10%.
Prepare underlying infrastructure for installing a Kubernetes cluster
Implement and configure a highly-available control plane
Use Helm and Kustomize to install cluster components
Understand extension interfaces (CNI, CSI, CRI, etc.)
Understand CRDs, install and configure operators
The etcd and kubeadm upgrade commands below were run against a real two-node kubeadm cluster built from
labs/kubeadm-cluster, upgrading it from v1.34.11 to v1.35.8 (September 2026). Step 0, the package repository change, is not optional: without itapt-get install kubeadm='1.35.8-1.1'fails withE: Version '1.35.8-1.1' for 'kubeadm' was not found.
# etcd backup and restore
# ETCDCTL_API=3 is the default from etcd 3.4 onwards, but setting it explicitly is harmless
etcdctl snapshot save -h # find save options
etcdutl snapshot restore -h # find restore options
# save a snapshot - against a TLS-enabled etcd you must pass the endpoint AND the ca/cert/key.
# Omitting --endpoints silently falls back to 127.0.0.1:2379, which is not always where etcd listens.
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /backup/snapshot.db
# verify the snapshot
etcdutl --write-out=table snapshot status /backup/snapshot.db
# restore into a NEW data directory.
# NOTE: `etcdctl snapshot restore` is deprecated since etcd 3.5 and removed in etcd 3.6 - use etcdutl.
etcdutl --data-dir /var/lib/etcd-restore snapshot restore /backup/snapshot.db
# then point the static pod at the restored data directory and let the kubelet restart it:
# edit /etc/kubernetes/manifests/etcd.yaml and set volumes.hostPath.path
# for the volume named `etcd-data` to /var/lib/etcd-restore
# evicting pods/nodes and bringing a node back into the cluster
kubectl drain <node> --ignore-daemonsets # drain a node (DaemonSet pods cannot be evicted)
kubectl uncordon <node> # return a node to the cluster as schedulable
kubectl cordon <node> # stop scheduling new pods on a node
# upgrade a kubernetes WORKER node (Debian/Ubuntu, run on the node itself unless noted)
# 0. point the package repository at the target minor version.
# pkgs.k8s.io has one repository per MINOR version, so without this
# `apt-get install kubeadm='1.35.x-*'` cannot find the package at all.
# Only needed when crossing a minor version (1.34 -> 1.35), not for
# a patch bump within the same minor (1.35.5 -> 1.35.7).
# On RHEL/Fedora the equivalent file is /etc/yum.repos.d/kubernetes.repo.
# Debian/Ubuntu:
sudo sed -i 's#/v1.34/#/v1.35/#' /etc/apt/sources.list.d/kubernetes.list
# find the exact patch version available in that repository
sudo apt update && sudo apt-cache madison kubeadm
# 1. upgrade kubeadm and kubectl
sudo apt-mark unhold kubeadm kubectl && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.35.x-*' kubectl='1.35.x-*' && \
sudo apt-mark hold kubeadm kubectl
# 2. upgrade the local kubelet config
sudo kubeadm upgrade node
# 3. drain the node (run from a control plane node)
kubectl drain <node-to-drain> --ignore-daemonsets
# 4. upgrade the kubelet
sudo apt-mark unhold kubelet && \
sudo apt-get update && sudo apt-get install -y kubelet='1.35.x-*' && \
sudo apt-mark hold kubelet
# 5. restart the kubelet
sudo systemctl daemon-reload
sudo systemctl restart kubelet
# 6. uncordon the node (run from a control plane node)
kubectl uncordon <node-to-uncordon>
# kubeadm CONTROL PLANE upgrade steps (one node at a time, first node shown)
# 0. change the package repository to the target minor version (see worker step 0)
# 1. upgrade the kubeadm package FIRST - `kubeadm upgrade plan` is run by the
# new kubeadm binary, so upgrading it is a prerequisite, not an afterthought
sudo apt-mark unhold kubeadm && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.35.x-*' && \
sudo apt-mark hold kubeadm
kubeadm version # confirm the new binary is in place
# 2. plan and apply
sudo kubeadm upgrade plan # shows which versions you can upgrade to
sudo kubeadm upgrade apply v1.35.x # FIRST control plane node only
# every OTHER control plane node: upgrade the kubeadm package, then run
# sudo kubeadm upgrade node
# instead of `kubeadm upgrade apply`
# 3. the control plane node still needs its kubelet and kubectl upgraded,
# exactly like a worker (drain, upgrade, restart, uncordon)
kubectl drain <cp-node> --ignore-daemonsets
sudo apt-mark unhold kubelet kubectl && \
sudo apt-get update && sudo apt-get install -y kubelet='1.35.x-*' kubectl='1.35.x-*' && \
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload
sudo systemctl restart kubelet
kubectl uncordon <cp-node>
#### Storage Class example
#
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage-sc
provisioner: kubernetes.io/no-provisioner # local volumes have no dynamic provisioner
volumeBindingMode: WaitForFirstConsumer # bind only when a Pod using the PVC is scheduled
reclaimPolicy: Retain
---
#### Persistent Volume Claim example
#
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: local-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-storage-sc
resources:
requests:
storage: 100Mi
---
## Persistent Volume example
#
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 200Mi
local:
path: /data/pv/disk021
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage-sc
volumeMode: Filesystem
# A local PersistentVolume is rejected without nodeAffinity:
# "You must set a PersistentVolume nodeAffinity when using local volumes."
# Replace <node-name> with the node holding /data/pv/disk021.
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- <node-name>
---
### Pod using the pvc
#
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
name: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: local-persistent-storage
mountPath: /var/www/html
volumes:
- name: local-persistent-storage
persistentVolumeClaim:
claimName: local-pvc
For a full diagnostic method and practice scenarios, see TROUBLESHOOTING.md.
practice practice practice
Get familiar with:
Familiarize yourself with the documentation, initially concepts and mostly tasks, kubectl explain command, kubectl cheatsheet, and kubectl commands reference
kubectl api-versions and kubectl api-resources with grep for a specific resource e.g. pv, pvc, deployment, storageclass, ..etc can help figure out the apiVersion, and kind combined with explain below will help in constructing the yaml manifest
kubectl explain --recursive to construct out any yaml manifest you need and find its specd and details
When using kubectl for investigations and troubleshooting utilize the wide output it gives your more details
$kubectl get pods -o wide --show-labels --all-namespaces
or
$kubectl get pods -o wide --show-labels -A # -A is quicker than --all-namespaces
In kubectl utilize --all-namespaces or better -A to ensure deployments, pods, objects are on the right name space, and right desired state
for events and troubleshooting utilize kubectl describe if its pod/resource related and logs if it is application issue related
$kubectl describe pods <PODID> # for pod, deployment, other k8s resource issues/events
$kubectl logs <PODID> # for container/application issues like crash loops
--dry-run=client allows you to create a manifest template from an imperative spec, combined with --edit it allows you to modify the object before creationkubectl create service clusterip my-svc -o yaml --dry-run=client > /tmp/srv.yaml
kubectl create --edit -f /tmp/srv.yaml
alias k='kubectl'
alias kg='kubectl get'
alias kgpo='kubectl get pod'
alias kcpyd='kubectl run -o yaml --dry-run=client' # `kubectl create pod` is not a valid subcommand - pods are created with `kubectl run`
alias ksysgpo='kubectl --namespace=kube-system get pod'
alias kd='kubectl delete'
alias kdf='kubectl delete -f'
## for quick deletes you can add --force --grace-period=0 **Not sure if it is a good idea if you are in a production cluster**
alias krmgf='kubectl delete --grace-period 0 --force'
alias kgsvcoyaml='kubectl get service -o=yaml'
alias kgsvcwn='watch kubectl get service --namespace'
alias kgsvcslwn='watch kubectl get service --show-labels --namespace'
#example usage of aliases
krmgf nginx-8jk71 # kill pod nginx-8jk71 using grace period 0 and force
k -n [Press Tab] will suggest available namespaces). Example command to enable autocomplete is available at official kubectl Cheat Sheet page, you don't have to remember anything.source <(kubectl completion bash) # setup autocomplete in bash into the current shell, bash-completion package should be installed first.
echo "source <(kubectl completion bash)" >> ~/.bashrc # add autocomplete permanently to your bash shell.
alias k=kubectl
complete -F __start_kubectl k
Double check if the course is uptodate with the latest exam information (e.g. api, or curriculum)
VMware's KubeAcademy has been retired (notice) and its free "How to prepare for the CKA exam" course was removed from this list. The remaining links were last checked in September 2026.
(top 24 of 42)
336 followers · starred Jun 2018
90 followers · starred Dec 2020
1,116 followers · starred Dec 2017
216 followers · starred Jan 2019
Online resources that will help you prepare for taking the CNCF CKA 2020 "Kubernetes Certified Administrator" Certification exam. with time, This is not likely the comprehensive up to date list - please make a pull request if there something that should be added here.
Shell
4,456
153 commits
updated Sep 18, 2026
Online resources that will help you prepare for taking the Kubernetes Certified Administrator Certification exam.
Beyond the curriculum links, these cover method: how to diagnose problems and how to study.
Disclaimer: This is not likely a comprehensive list as the exam will be a moving target with the fast pace of k8s development - please make a pull request if there something wrong, should be added, or updated.
I tried to restrict the cross references of resources to kubernetes.io. Youtube videos and other blog resources are optional; however, I still found them useful in my k8s learning journey.
Ensure you have the right version of Kubernetes documentation selected (v1.35 as of the September 2026 exam environment) especially for API objects and annotations.
TL;DR: practice practice practice
These are the exam objectives you review and understand in order to pass the test.
| Exam environment | Kubernetes v1.35 |
| Duration | 2 hours |
| Passing score | 66% |
| Included | 2 exam attempts, 2 Killer.sh simulator sessions |
| Validity | 2 years |
The exam environment is aligned with the most recent Kubernetes minor release within roughly 4 to 8 weeks of that release, so confirm the version in the CKA/CKAD/CKS FAQ and on the CKA certification page before you sit it.
If you studied from an older version of this README or an older course, these are the differences that matter. Items marked new were not in the previous curriculum at all.
Domain weights are unchanged: Troubleshooting 30%, Cluster Architecture 25%, Services and Networking 20%, Workloads and Scheduling 15%, Storage 10%.
Prepare underlying infrastructure for installing a Kubernetes cluster
Implement and configure a highly-available control plane
Use Helm and Kustomize to install cluster components
Understand extension interfaces (CNI, CSI, CRI, etc.)
Understand CRDs, install and configure operators
The etcd and kubeadm upgrade commands below were run against a real two-node kubeadm cluster built from
labs/kubeadm-cluster, upgrading it from v1.34.11 to v1.35.8 (September 2026). Step 0, the package repository change, is not optional: without itapt-get install kubeadm='1.35.8-1.1'fails withE: Version '1.35.8-1.1' for 'kubeadm' was not found.
# etcd backup and restore
# ETCDCTL_API=3 is the default from etcd 3.4 onwards, but setting it explicitly is harmless
etcdctl snapshot save -h # find save options
etcdutl snapshot restore -h # find restore options
# save a snapshot - against a TLS-enabled etcd you must pass the endpoint AND the ca/cert/key.
# Omitting --endpoints silently falls back to 127.0.0.1:2379, which is not always where etcd listens.
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
snapshot save /backup/snapshot.db
# verify the snapshot
etcdutl --write-out=table snapshot status /backup/snapshot.db
# restore into a NEW data directory.
# NOTE: `etcdctl snapshot restore` is deprecated since etcd 3.5 and removed in etcd 3.6 - use etcdutl.
etcdutl --data-dir /var/lib/etcd-restore snapshot restore /backup/snapshot.db
# then point the static pod at the restored data directory and let the kubelet restart it:
# edit /etc/kubernetes/manifests/etcd.yaml and set volumes.hostPath.path
# for the volume named `etcd-data` to /var/lib/etcd-restore
# evicting pods/nodes and bringing a node back into the cluster
kubectl drain <node> --ignore-daemonsets # drain a node (DaemonSet pods cannot be evicted)
kubectl uncordon <node> # return a node to the cluster as schedulable
kubectl cordon <node> # stop scheduling new pods on a node
# upgrade a kubernetes WORKER node (Debian/Ubuntu, run on the node itself unless noted)
# 0. point the package repository at the target minor version.
# pkgs.k8s.io has one repository per MINOR version, so without this
# `apt-get install kubeadm='1.35.x-*'` cannot find the package at all.
# Only needed when crossing a minor version (1.34 -> 1.35), not for
# a patch bump within the same minor (1.35.5 -> 1.35.7).
# On RHEL/Fedora the equivalent file is /etc/yum.repos.d/kubernetes.repo.
# Debian/Ubuntu:
sudo sed -i 's#/v1.34/#/v1.35/#' /etc/apt/sources.list.d/kubernetes.list
# find the exact patch version available in that repository
sudo apt update && sudo apt-cache madison kubeadm
# 1. upgrade kubeadm and kubectl
sudo apt-mark unhold kubeadm kubectl && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.35.x-*' kubectl='1.35.x-*' && \
sudo apt-mark hold kubeadm kubectl
# 2. upgrade the local kubelet config
sudo kubeadm upgrade node
# 3. drain the node (run from a control plane node)
kubectl drain <node-to-drain> --ignore-daemonsets
# 4. upgrade the kubelet
sudo apt-mark unhold kubelet && \
sudo apt-get update && sudo apt-get install -y kubelet='1.35.x-*' && \
sudo apt-mark hold kubelet
# 5. restart the kubelet
sudo systemctl daemon-reload
sudo systemctl restart kubelet
# 6. uncordon the node (run from a control plane node)
kubectl uncordon <node-to-uncordon>
# kubeadm CONTROL PLANE upgrade steps (one node at a time, first node shown)
# 0. change the package repository to the target minor version (see worker step 0)
# 1. upgrade the kubeadm package FIRST - `kubeadm upgrade plan` is run by the
# new kubeadm binary, so upgrading it is a prerequisite, not an afterthought
sudo apt-mark unhold kubeadm && \
sudo apt-get update && sudo apt-get install -y kubeadm='1.35.x-*' && \
sudo apt-mark hold kubeadm
kubeadm version # confirm the new binary is in place
# 2. plan and apply
sudo kubeadm upgrade plan # shows which versions you can upgrade to
sudo kubeadm upgrade apply v1.35.x # FIRST control plane node only
# every OTHER control plane node: upgrade the kubeadm package, then run
# sudo kubeadm upgrade node
# instead of `kubeadm upgrade apply`
# 3. the control plane node still needs its kubelet and kubectl upgraded,
# exactly like a worker (drain, upgrade, restart, uncordon)
kubectl drain <cp-node> --ignore-daemonsets
sudo apt-mark unhold kubelet kubectl && \
sudo apt-get update && sudo apt-get install -y kubelet='1.35.x-*' kubectl='1.35.x-*' && \
sudo apt-mark hold kubelet kubectl
sudo systemctl daemon-reload
sudo systemctl restart kubelet
kubectl uncordon <cp-node>
#### Storage Class example
#
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: local-storage-sc
provisioner: kubernetes.io/no-provisioner # local volumes have no dynamic provisioner
volumeBindingMode: WaitForFirstConsumer # bind only when a Pod using the PVC is scheduled
reclaimPolicy: Retain
---
#### Persistent Volume Claim example
#
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: local-pvc
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-storage-sc
resources:
requests:
storage: 100Mi
---
## Persistent Volume example
#
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 200Mi
local:
path: /data/pv/disk021
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage-sc
volumeMode: Filesystem
# A local PersistentVolume is rejected without nodeAffinity:
# "You must set a PersistentVolume nodeAffinity when using local volumes."
# Replace <node-name> with the node holding /data/pv/disk021.
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- <node-name>
---
### Pod using the pvc
#
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
name: nginx
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- name: local-persistent-storage
mountPath: /var/www/html
volumes:
- name: local-persistent-storage
persistentVolumeClaim:
claimName: local-pvc
For a full diagnostic method and practice scenarios, see TROUBLESHOOTING.md.
practice practice practice
Get familiar with:
Familiarize yourself with the documentation, initially concepts and mostly tasks, kubectl explain command, kubectl cheatsheet, and kubectl commands reference
kubectl api-versions and kubectl api-resources with grep for a specific resource e.g. pv, pvc, deployment, storageclass, ..etc can help figure out the apiVersion, and kind combined with explain below will help in constructing the yaml manifest
kubectl explain --recursive to construct out any yaml manifest you need and find its specd and details
When using kubectl for investigations and troubleshooting utilize the wide output it gives your more details
$kubectl get pods -o wide --show-labels --all-namespaces
or
$kubectl get pods -o wide --show-labels -A # -A is quicker than --all-namespaces
In kubectl utilize --all-namespaces or better -A to ensure deployments, pods, objects are on the right name space, and right desired state
for events and troubleshooting utilize kubectl describe if its pod/resource related and logs if it is application issue related
$kubectl describe pods <PODID> # for pod, deployment, other k8s resource issues/events
$kubectl logs <PODID> # for container/application issues like crash loops
--dry-run=client allows you to create a manifest template from an imperative spec, combined with --edit it allows you to modify the object before creationkubectl create service clusterip my-svc -o yaml --dry-run=client > /tmp/srv.yaml
kubectl create --edit -f /tmp/srv.yaml
alias k='kubectl'
alias kg='kubectl get'
alias kgpo='kubectl get pod'
alias kcpyd='kubectl run -o yaml --dry-run=client' # `kubectl create pod` is not a valid subcommand - pods are created with `kubectl run`
alias ksysgpo='kubectl --namespace=kube-system get pod'
alias kd='kubectl delete'
alias kdf='kubectl delete -f'
## for quick deletes you can add --force --grace-period=0 **Not sure if it is a good idea if you are in a production cluster**
alias krmgf='kubectl delete --grace-period 0 --force'
alias kgsvcoyaml='kubectl get service -o=yaml'
alias kgsvcwn='watch kubectl get service --namespace'
alias kgsvcslwn='watch kubectl get service --show-labels --namespace'
#example usage of aliases
krmgf nginx-8jk71 # kill pod nginx-8jk71 using grace period 0 and force
k -n [Press Tab] will suggest available namespaces). Example command to enable autocomplete is available at official kubectl Cheat Sheet page, you don't have to remember anything.source <(kubectl completion bash) # setup autocomplete in bash into the current shell, bash-completion package should be installed first.
echo "source <(kubectl completion bash)" >> ~/.bashrc # add autocomplete permanently to your bash shell.
alias k=kubectl
complete -F __start_kubectl k
Double check if the course is uptodate with the latest exam information (e.g. api, or curriculum)
VMware's KubeAcademy has been retired (notice) and its free "How to prepare for the CKA exam" course was removed from this list. The remaining links were last checked in September 2026.
(top 24 of 42)
336 followers · starred Jun 2018
90 followers · starred Dec 2020
1,116 followers · starred Dec 2017
216 followers · starred Jan 2019