DevOps · Kubernetes · CLI

☸ kubectl Zero-To-Hero

Learn kubectl from absolute zero to production-ready confidence in 10 focused days. Each day has a clear goal, key concepts, commands to practice, and a checkpoint task to commit to your repo.

10 Days Beginner → Production Hands-On No Prior K8s Experience Needed

Contents

// Before You Begin

Prerequisites

You don't need any Kubernetes experience. You just need:

OptionDescriptionLink
MinikubeRuns locally on your machineminikube.sigs.k8s.io
KindKubernetes in Dockerkind.sigs.k8s.io
k3sLightweight, great for VMsk3s.io
Play with K8sFree browser-based clusterplay-with-k8s.com
// Getting Started

How to Use This Guide

  1. Fork or clone this repo to track your progress
  2. Work through one day at a time — don't skip ahead
  3. Run every command yourself — reading is not enough
  4. Complete the checkpoint at the end of each day
  5. Commit your work — push your YAML files and notes as you go

Suggested Repo Structure

kubectl-learning/
├── README.md
├── day-01/
│   └── notes.md
├── day-02/
│   └── notes.md
├── day-03/
│   ├── my-first-pod.yaml
│   └── notes.md
├── day-04/
│   ├── deployment.yaml
│   └── notes.md
...
└── day-10/
    ├── deployment.yaml
    ├── service.yaml
    ├── configmap.yaml
    ├── secret.yaml
    └── notes.md
// Big Picture

Roadmap Overview

Day 1
Why kubectl Exists
Understand the problem it solves
Day 2
First Commands
Get comfortable with the CLI
Day 3
Pods
Understand what actually runs
Day 4
Deployments & ReplicaSets
Run apps the Kubernetes way
Day 5
Services & Networking
Make apps reachable
Day 6
Config & Secrets
Stop hardcoding configs
Day 7
Debugging
Be useful during outages
Day 8
YAML Mastery
Read & write manifests confidently
Day 9
Multiple Environments
Real-world kubectl usage
Day 10
Mini Project
Cement everything
DAY 01
// Goal: Understand the problem kubectl solves before touching any commands

Why kubectl Exists

Concepts

Setup

# macOS
brew install kubectl

# Linux
curl -LO "https://dl.k8s.io/release/$(curl -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl && sudo mv kubectl /usr/local/bin/

# Verify
kubectl version --client
# Start a local cluster (choose one)
minikube start       # Minikube
kind create cluster  # Kind

# Verify cluster access
kubectl cluster-info
kubectl get nodes
Day 1 Checkpoint
  • kubectl installed and kubectl version --client shows output
  • Cluster running and kubectl get nodes shows a Ready node
  • Create day-01/notes.md: explain what is the API server and why kubectl talks to it
DAY 02
// Goal: Get comfortable navigating a cluster from the CLI

First kubectl Commands

Core Commands

# List resources
kubectl get pods
kubectl get nodes
kubectl get all

# Get more details
kubectl get pods -o wide          # shows IP, node
kubectl get pods -o yaml          # full YAML output

# Inspect a specific resource
kubectl describe pod <pod-name>   # human-readable details + events

# Understand any resource
kubectl explain pod
kubectl explain pod.spec
kubectl explain deployment.spec.template

Namespaces

Namespaces are like folders — they group and isolate resources.

kubectl get namespaces
kubectl get pods --namespace kube-system   # system pods
kubectl get pods -n kube-system            # shorthand
kubectl get pods --all-namespaces          # everything

Contexts & Clusters

A context = cluster + user + namespace. Useful when you have multiple clusters.

kubectl config view                        # see all contexts
kubectl config current-context            # which context is active
kubectl config use-context <name>         # switch context
kubectl config get-contexts               # list all contexts
Day 2 Checkpoint
  • Run kubectl get all -n kube-system and understand what you see
  • Use kubectl explain on at least 3 different resources
  • Create day-02/notes.md: What's the difference between get and describe?
DAY 03
// Goal: Understand what actually runs in Kubernetes

Pods — The Core Unit

What is a Pod?

Pod Lifecycle

PhaseMeaning
PendingScheduled but container not started yet
RunningAt least one container is running
SucceededAll containers exited successfully
FailedAt least one container exited with error
UnknownNode communication lost

Create a Pod (Declarative YAML)

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  labels:
    app: demo
spec:
  containers:
  - name: nginx
    image: nginx:latest
    ports:
    - containerPort: 80
# Apply the manifest
kubectl apply -f day-03/my-first-pod.yaml

# Logs & exec
kubectl logs my-pod
kubectl logs my-pod -f                    # follow live
kubectl logs my-pod --previous            # last crashed container
kubectl exec -it my-pod -- /bin/bash

# Clean up
kubectl delete pod my-pod
Day 3 Checkpoint
  • Create a pod using YAML and commit to day-03/
  • Shell into the pod and run a command
  • Create day-03/notes.md: Why are pods ephemeral, and why does that matter?
DAY 04
// Goal: Run apps the right way in Kubernetes

Deployments & ReplicaSets

Why Not Just Use Pods?

ProblemWhat Happens
Pod crashesIt's gone — nothing restarts it
Need 3 copiesYou have to create 3 pods manually
Want to update your appYou have to delete and recreate pods

Deployments solve all of this. A ReplicaSet ensures N copies of a pod always run; a Deployment manages ReplicaSets and handles updates.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: nginx
        image: nginx:1.25
        ports:
        - containerPort: 80
# Scale
kubectl scale deployment my-app --replicas=5

# Rolling update
kubectl set image deployment/my-app nginx=nginx:1.26
kubectl rollout status deployment/my-app
kubectl rollout history deployment/my-app

# Rollback
kubectl rollout undo deployment/my-app
kubectl rollout undo deployment/my-app --to-revision=1
Day 4 Checkpoint
  • Deploy 3 replicas, then scale to 5
  • Do a rolling update and watch kubectl rollout status
  • Roll back and confirm the old image is running
  • Commit day-04/deployment.yaml
DAY 05
// Goal: Make your application reachable

Services & Networking

Pods have IP addresses, but they change when pods restart. You need a stable endpoint to reach your app — that's what a Service is.

Service Types

TypeAccessWhen to Use
ClusterIPInside cluster only (default)Internal microservices
NodePortVia node IP + port (30000-32767)Local dev / testing
LoadBalancerExternal IP via cloud load balancerProduction on cloud
apiVersion: v1
kind: Service
metadata:
  name: my-app-svc
spec:
  selector:
    app: my-app        # matches pods with this label
  ports:
  - port: 80           # service port
    targetPort: 80     # container port
  type: NodePort
kubectl apply -f day-05/service.yaml

# On Minikube
minikube service my-app-svc --url

# Port-forward (works on any cluster)
kubectl port-forward svc/my-app-svc 8080:80
# Open http://localhost:8080
Day 5 Checkpoint
  • Expose your deployment and reach it from browser or curl
  • Commit day-05/service.yaml
  • Create day-05/notes.md: What would happen if a pod's label didn't match the service selector?
DAY 06
// Goal: Never hardcode configuration again

Config & Secrets

ConfigMaps

Store non-sensitive config (URLs, feature flags, settings).

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: "production"
  LOG_LEVEL: "info"
  DATABASE_URL: "postgres://db-service:5432/mydb"

Secrets

Store sensitive data (passwords, tokens, API keys). Values are base64-encoded — but can be encrypted at rest.

kubectl create secret generic db-secret \
  --from-literal=DB_PASSWORD=supersecret \
  --from-literal=API_KEY=myapikey123

# Decode a value
kubectl get secret db-secret -o jsonpath='{.data.DB_PASSWORD}' | base64 -d

Use Config in a Deployment

env:
- name: APP_ENV
  valueFrom:
    configMapKeyRef:
      name: app-config
      key: APP_ENV
- name: DB_PASSWORD
  valueFrom:
    secretKeyRef:
      name: db-secret
      key: DB_PASSWORD
⚠ Security Note
Never commit real secrets to Git. Use tools like Sealed Secrets, External Secrets, or Vault for production.
Day 6 Checkpoint
  • Create a ConfigMap and a Secret
  • Deploy an app that reads from both
  • Verify: kubectl exec -it <pod> -- env | grep APP_ENV
  • Commit day-06/configmap.yaml (never commit real secrets!)
DAY 07
// Goal: Be useful when things break

Debugging Like a Pro

The Debug Workflow

1. kubectl get pods           → Is the pod running?
2. kubectl describe pod       → What events happened?
3. kubectl logs               → What did the app say?
4. kubectl exec               → Can I get inside?

Common Problems

ErrorCommon CausesDebug Command
CrashLoopBackOffApp crash, bad config, missing env varskubectl logs <pod> --previous
ImagePullBackOffWrong image name/tag, private registrykubectl describe pod <pod>
PendingInsufficient resources, node selector mismatchkubectl describe pod <pod>
OOMKilledMemory limit too low, memory leakkubectl describe pod <pod> → Last State
# Watch pods in real time
kubectl get pods -w

# Get events for the whole namespace
kubectl get events --sort-by=.lastTimestamp

# Run a temporary debug pod
kubectl run debug --image=busybox --rm -it --restart=Never -- sh

# Check resource usage (requires metrics-server)
kubectl top pods
kubectl top nodes
Day 7 Checkpoint
  • Deliberately break a deployment (wrong image tag, e.g. nginx:doesnotexist)
  • Debug it using only describe, logs, and events
  • Fix it with a correct image and do a rollout
  • Create day-07/notes.md: Walk through your debug session step by step
DAY 08
// Goal: Read and write Kubernetes manifests confidently

YAML Mastery

The 4 Top-Level Fields

apiVersion: apps/v1   # API group + version
kind: Deployment      # resource type
metadata:             # name, namespace, labels, annotations
  name: my-app
  namespace: default
  labels:
    app: my-app
    env: production
  annotations:
    description: "My production app"
spec:                 # desired state — differs per resource type
  ...

Labels vs Annotations

LabelsAnnotations
PurposeSelection & groupingMetadata & documentation
Used bySelectors, servicesTools, humans
QueryableYes (-l app=my-app)No

Resource Requests & Limits

resources:
  requests:          # minimum guaranteed
    cpu: "100m"      # 100 millicores = 0.1 CPU
    memory: "128Mi"
  limits:            # maximum allowed
    cpu: "500m"
    memory: "256Mi"

Liveness & Readiness Probes

livenessProbe:       # if this fails → restart container
  httpGet:
    path: /healthz
    port: 80
  initialDelaySeconds: 10
  periodSeconds: 5
readinessProbe:      # if this fails → remove from Service endpoints
  httpGet:
    path: /ready
    port: 80
  initialDelaySeconds: 5
  periodSeconds: 3
# Dry run — validate without applying
kubectl apply -f my-file.yaml --dry-run=client
kubectl apply -f my-file.yaml --dry-run=server

# See what would change
kubectl diff -f my-file.yaml
Day 8 Checkpoint
  • Write a complete Deployment YAML from scratch (no copy-paste)
  • Include: resource limits, a liveness probe, labels, annotations
  • Validate with --dry-run=client before applying
  • Commit as day-08/deployment-with-probes.yaml
DAY 09
// Goal: Manage apps across dev, staging, and production

Working with Multiple Environments

# Create namespaces
kubectl create namespace dev
kubectl create namespace staging
kubectl create namespace production

# Deploy to a specific namespace
kubectl apply -f deployment.yaml -n dev
kubectl apply -f deployment.yaml -n staging

# View resources per namespace
kubectl get all -n dev

Context Switching

kubectl config get-contexts
kubectl config use-context my-prod-cluster
kubectl config set-context --current --namespace=dev
kubectl config rename-context old-name new-name
💡 Tip: kubectx + kubens
Install kubectx and kubens for much faster context and namespace switching:

brew install kubectx
kubectx my-prod — switch cluster
kubens dev — switch namespace
Day 9 Checkpoint
  • Create dev and staging namespaces
  • Deploy your app to both with different ConfigMap values
  • Practice switching between them
  • Create day-09/notes.md: How would you prevent accidental kubectl delete in production?
DAY 10
// Goal: Tie everything together into one real-world workflow

Mini Project

What You'll Build

nginx (web server)
  ├── Deployment (3 replicas)
  ├── Service (NodePort)
  ├── ConfigMap (app settings)
  └── Secret (fake API key)

Step 1 — Create the Namespace

kubectl create namespace mini-project

Step 2 — Apply All Manifests

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_NAME: "My kubectl App"
  LOG_LEVEL: "info"
apiVersion: v1
kind: Secret
metadata:
  name: app-secret
type: Opaque
data:
  API_KEY: bXlzZWNyZXRrZXkxMjM=   # base64 of "mysecretkey123"
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: nginx
        image: nginx:1.25
        ports:
        - containerPort: 80
        env:
        - name: APP_NAME
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: APP_NAME
        - name: API_KEY
          valueFrom:
            secretKeyRef:
              name: app-secret
              key: API_KEY
        resources:
          requests:
            cpu: "100m"
            memory: "64Mi"
          limits:
            cpu: "200m"
            memory: "128Mi"
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5
apiVersion: v1
kind: Service
metadata:
  name: my-app-svc
spec:
  selector:
    app: my-app
  ports:
  - port: 80
    targetPort: 80
  type: NodePort

Step 3 — Verify & Access

kubectl apply -f day-10/ -n mini-project
kubectl get all -n mini-project
kubectl port-forward svc/my-app-svc 8080:80 -n mini-project
curl http://localhost:8080

Step 4 — Rolling Update

kubectl set image deployment/my-app nginx=nginx:1.26 -n mini-project
kubectl rollout status deployment/my-app -n mini-project

Step 5 — Break It & Debug It

# Set a bad image
kubectl set image deployment/my-app nginx=nginx:thisdoesnotexist -n mini-project

# Debug
kubectl get pods -n mini-project
kubectl describe pod <pod-name> -n mini-project

# Roll back
kubectl rollout undo deployment/my-app -n mini-project
kubectl rollout status deployment/my-app -n mini-project
Day 10 Checkpoint
  • All 4 manifests applied and app is reachable
  • Rolling update completed successfully
  • Deliberately broken, debugged, and rolled back
  • Everything committed to day-10/
  • Write day-10/notes.md: What was the hardest part? What clicked?
// Bookmark This

Quick Reference

Most Used Commands
# Get resources
kubectl get pods / nodes / deployments / svc / configmaps / secrets / all

# Inspect
kubectl describe <resource> <name>
kubectl logs <pod-name> [-f] [--previous]
kubectl exec -it <pod-name> -- /bin/bash

# Apply & manage
kubectl apply -f <file.yaml>
kubectl delete -f <file.yaml>
kubectl delete pod/svc/deployment <name>

# Deployments
kubectl scale deployment <name> --replicas=<n>
kubectl set image deployment/<name> <container>=<image>:<tag>
kubectl rollout status / history / undo deployment/<name>

# Namespaces & contexts
kubectl get all -n <namespace>
kubectl config use-context <name>
kubectl config set-context --current --namespace=<ns>

# Debug
kubectl get events --sort-by=.lastTimestamp
kubectl run debug --image=busybox --rm -it --restart=Never -- sh
kubectl apply -f file.yaml --dry-run=client
// After Completion

What You'll Know

// Keep Learning

Useful Resources

kubectl Cheat Sheetkubernetes.io
Kubernetes Conceptskubernetes.io
Minikube Getting Startedminikube.sigs.k8s.io
Kind Quickstartkind.sigs.k8s.io
kubectx / kubensgithub.com/ahmetb