Where to go next
Your path beyond the basics.
You made it to the end. You can now say what Kubernetes is and why anyone puts up with it. You can run an app as a Deployment (an object whose one job is keeping a set number of copies of your app running), keep it alive when a copy dies, ship a new version, roll that version back, feed it configuration and secrets, put a Service and an Ingress in front of it so real people can reach it, hand it storage that survives a restart, carve a cluster into namespaces, and set limits so one greedy app can't starve everything else. That is a real foundation. Plenty of people with Kubernetes on their resume can do about that much and no further. This last lesson is about what you do with it.
Nothing sticks until you build something
Reading about Kubernetes is a lot like reading recipes. You can get through a hundred of them and still freeze the first time someone hands you a knife and a pile of onions. The cure is cooking the same dish over and over until your hands know it without being told. So get yourself a cluster you can wreck with no consequences at all. Three free tools give you a complete Kubernetes on your own laptop: minikube, kind (short for Kubernetes IN Docker), and k3s (a lightweight single-file build). Pick one, start it, and check that its single machine, which Kubernetes calls a node, comes up ready. kubectl, the command-line tool you have been typing all course, is how you talk to it.
$ minikube start😄 minikube v1.34.0 on your laptop✨ Automatically selected the docker driver🐳 Preparing Kubernetes v1.31.0 on Docker 27.2.0 ...🌟 Enabled addons: default-storageclass, storage-provisioner🏄 Done! kubectl is now configured to use "minikube".$ kubectl get nodesNAME STATUS ROLES AGE VERSIONminikube Ready control-plane 40s v1.31.0
Then take one small app all the way around the loop, adding a piece at a time: deploy it, expose it, configure it, give it storage, scale it up, ship a new version, then roll that version back. Each piece is a manifest, a text file that spells out what you want, written in a plain format called YAML. Here is a complete one you can apply exactly as it stands. It comes in two parts joined by the '---' line: a Deployment that keeps two copies of a web server running, and a Service that puts one stable address in front of both.
apiVersion: apps/v1kind: Deploymentmetadata:name: hellospec:replicas: 2selector:matchLabels:app: hellotemplate:metadata:labels:app: hellospec:containers:- name: webimage: nginx:1.27ports:- containerPort: 80readinessProbe:httpGet:path: /port: 80initialDelaySeconds: 2periodSeconds: 5---apiVersion: v1kind: Servicemetadata:name: hellospec:selector:app: helloports:- port: 80targetPort: 80
$ kubectl apply -f deploy.yamldeployment.apps/hello createdservice/hello created$ kubectl get deploy,pods,svcNAME READY UP-TO-DATE AVAILABLE AGEdeployment.apps/hello 2/2 2 2 18sNAME READY STATUS RESTARTS AGEpod/hello-7c9f8b6d4b-4xk2p 1/1 Running 0 18spod/hello-7c9f8b6d4b-lm7qd 1/1 Running 0 18sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/hello ClusterIP 10.96.140.22 <none> 80/TCP 18s
Read that output slowly, because it is the whole point of the course. The Deployment line says READY 2/2, so both copies you asked for are up. Under it sit two lines starting with pod/, and each of those is one running copy of the web server. A copy only counts as READY once it passes the readinessProbe from the file, which is Kubernetes knocking on the app's front door with a small web request to the path / and waiting for an answer before it sends any real users that way. The Service shows a CLUSTER-IP of 10.96.140.22 and an EXTERNAL-IP of none, which is Kubernetes telling you that address works from inside the cluster and nowhere else. To open the app in your own browser, have your laptop hand traffic from a local port straight to the Service with kubectl port-forward service/hello 8080:80, then visit http://localhost:8080.
Put that file in Git on day one. Git is version control: it records every change to your files with a timestamp and a short note, so you can see exactly what changed and undo a mistake in seconds. There is a bigger idea hiding in that habit. Typing one-off commands is like shouting orders across a busy kitchen. It is quick, and an hour later nobody remembers a word of it. Keeping your setup in files and applying those files is closer to writing the order down, where anyone can read it, repeat it, or correct it later. That written-down way of working is how real teams run Kubernetes. Two commands get the habit going. Watch what the second apply says: 'unchanged', because the cluster already matches the file, so Kubernetes has nothing left to do.
$ git init && git add deploy.yaml && git commit -m "hello app: deployment + service"Initialized empty Git repository in /home/you/hello/.git/[main (root-commit) 9f1c2a0] hello app: deployment + service1 file changed, 36 insertions(+)$ kubectl apply -f .deployment.apps/hello unchangedservice/hello unchanged
Break it on purpose, before it breaks on you
The single most useful thing you can practise is recovering from a bad deploy, because a bad deploy is coming for you eventually. So cause one right now, while nothing is at stake. Open deploy.yaml, change the image line to nginx:1.72 (there is no such version), and apply it. Then watch what Kubernetes does with a version it cannot get hold of.
$ kubectl apply -f deploy.yamldeployment.apps/hello configuredservice/hello unchanged$ kubectl get podsNAME READY STATUS RESTARTS AGEhello-7c9f8b6d4b-4xk2p 1/1 Running 0 6mhello-7c9f8b6d4b-lm7qd 1/1 Running 0 6mhello-5d8c4f7b9c-p2wqr 0/1 ImagePullBackOff 0 25s
Read that carefully, because most of your real problems will arrive in this exact shape. Your two original copies are still Running and still serving users. The new copy is stuck at ImagePullBackOff: Kubernetes tried to download the image, failed, and is now waiting a little longer before each retry. It refuses to retire a working copy until a new one is healthy, so nobody outside notices a thing. To find out why the new copy is unhappy, ask the copy itself with kubectl describe pod.
$ kubectl describe pod hello-5d8c4f7b9c-p2wqr...Events:Type Reason Age From Message---- ------ ---- ---- -------Normal Scheduled 46s default-scheduler Successfully assigned default/hello-5d8c4f7b9c-p2wqr to minikubeNormal Pulling 45s kubelet Pulling image "nginx:1.72"Warning Failed 44s kubelet Failed to pull image "nginx:1.72": manifest for nginx:1.72 not found: manifest unknownWarning Failed 44s kubelet Error: ErrImagePullNormal BackOff 18s (x2 over 43s) kubelet Back-off pulling image "nginx:1.72"
That Events list is the control plane thinking out loud. default-scheduler picked a node for the new copy. Then kubelet, the agent running on that node, tried to pull the image and reported back 'manifest ... not found'. There is no nginx:1.72. So now you know what you are dealing with: a typo in the tag, not a broken app and not a broken cluster. The fix is one command. Undo the rollout and drop back to the version that worked.
$ kubectl rollout undo deployment/hellodeployment.apps/hello rolled back$ kubectl get podsNAME READY STATUS RESTARTS AGEhello-7c9f8b6d4b-4xk2p 1/1 Running 0 8mhello-7c9f8b6d4b-lm7qd 1/1 Running 0 8m
Nothing went down. The Deployment kept the two good copies serving and never sent a single user to the broken one. That same typo in a plain docker run on one box would have taken the site with it. rollout undo has you running again in seconds, and then you fix the tag back in deploy.yaml so the file and the running cluster tell the same story again.
Three roads out of here
Once that loop feels boring, you get to pick a direction. Three come up more often than the rest. If you want to run the cluster itself rather than the apps riding on it, the administration path goes inside the machine: the control plane (the brain that decides where things run and notices when they break), the networking and storage internals, cluster upgrades, and troubleshooting. If you want to defend clusters, the security path digs into RBAC (Role-Based Access Control, the rules for who is allowed to do what), network policy, and tougher attack-and-defence material, up to the CKS exam (Certified Kubernetes Security Specialist). And if what you want is smoother day-to-day work, the ecosystem is where to look: GitOps tools like Argo CD apply your Git changes to the cluster for you, Helm and Kustomize template your manifests so you stop copy-pasting them, and Prometheus with Grafana let you watch what the cluster is actually doing. Don't try to walk all three at once. Pick the one that matches what you actually want to be doing next, and ignore the rest until you need them. None of it is required to be useful today. It is the map, not the homework.
Here is what that foundation actually buys you on a team. Every course after this one, administration, packaging, security, opens by assuming you can already run the loop you ran a few minutes ago: apply a manifest, read the output, work out which piece is unhappy, and put it back. Nobody teaches that part twice. Build the reflex now, while a broken cluster costs you nothing.
If you want one concrete assignment, take this one. Build a small personal project on kind or minikube with five pieces in it: a Deployment, a ConfigMap, a Service, an Ingress, and a PVC (PersistentVolumeClaim, your app's written request for a slice of disk that outlives the Pod). Then break it deliberately and repair it using describe and logs. An hour of that beats rereading the analogies in this course, mine included.
The unglamorous stuff is what shows up during a real incident. A Service with no endpoints behind it. A readiness probe aimed at a path the app never serves. kubectl pointed at the wrong cluster. A PersistentVolumeClaim sitting in Pending because nothing in the cluster can satisfy it. Keep those kubectl reflexes sharp. They will carry you much further than sprinting ahead to exotic tooling.
Try this
Take stock of what you can already reach for without thinking. List every Deployment, Service and Ingress across the whole cluster, check which kinds of object live inside a namespace, and confirm which cluster kubectl is currently pointed at. Then keep a scratch file of the commands you typed most in this course. That file becomes your own cheat sheet, and it will serve you better than anyone else's.
$ kubectl get deploy,svc,ingress -A | Select-Object -First 30NAMESPACE NAME READYdefault deployment.apps/… 1/1...NAMESPACE NAME TYPE CLUSTER-IPdefault service/kubernetes ClusterIP 10.96.0.1...$ kubectl api-resources --namespaced=true | Select-String 'deployments|services|configmaps|secrets|ingresses|persistentvolumeclaims'configmaps cm true ConfigMapdeployments deploy true Deploymentingresses ing true Ingresspersistentvolumeclaims pvc true PersistentVolumeClaimsecrets true Secretservices svc true Service$ kubectl config current-contextkind-kind
Takeaway
The fundamentals are Deployments, Services, configuration, storage and namespaces, practised with kubectl until they bore you. Your next move is the hello app from this lesson, taken from first apply to broken tag to rollout undo without looking anything up. Specialise into administration, packaging or security after that.