At the end of the previous lesson you had a cluster started and verified with a couple of borrowed commands. Now let us go for the tool itself. kubectl is the official client for the Kubernetes API and it is going to be, by a wide margin, the program you use most in this course and in your professional life with Kubernetes: creating, querying, inspecting, debugging and deleting all go through it. The good news is that kubectl is extraordinarily regular: once you understand its grammar, you know how to use it with any type of object, including the ones that do not exist yet. This lesson teaches you that grammar, the configuration file that decides which cluster you talk to, the verbs you will use daily on the Rutas Norte components, the output formats that turn kubectl into a serious query tool, and the productivity settings that separate whoever fights the cluster from whoever works with ease.

Contents

  1. What kubectl is, and installing it
  2. The kubeconfig file: clusters, users and contexts
  3. Anatomy of a kubectl command
  4. The day-to-day verbs
  5. Output formats and queries
  6. Label selection and live watching
  7. Imperative versus declarative
  8. Productivity: aliases, completion, explain and plugins

  1. What kubectl is, and installing it

kubectl has no intelligence of its own: it translates your commands into HTTPS requests against the kube-apiserver. Everything kubectl can do could be done by curl with the right certificate; what it adds is convenience, formatting and local validation.

Practical consequence: anything kubectl will not let you do is being forbidden by the server (RBAC), not by the tool.

Installation

Linux:

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl
rm kubectl

macOS:

brew install kubectl

Windows (PowerShell):

winget install -e --id Kubernetes.kubectl

Verification:

kubectl version
Client Version: v1.30.2
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.30.0

Compatibility rule (version skew): kubectl supports a difference of one minor version above or below the server. A 1.30 client works with 1.29, 1.30 and 1.31 servers. With bigger gaps, specific commands can fail in ways that are not obvious, so keep the client close to the server.

  1. The kubeconfig file: clusters, users and contexts

When minikube said "kubectl is now configured to use rutas-norte" in the previous lesson, what it did was write to ~/.kube/config. That file answers three questions: which server do I talk to?, with which credentials? and in which default namespace?

# ~/.kube/config (simplified)
apiVersion: v1
kind: Config
clusters:                       # WHICH server
  - name: rutas-norte
    cluster:
      server: https://192.168.49.2:8443
      certificate-authority: /home/user/.minikube/ca.crt
users:                          # WITH WHICH credentials
  - name: rutas-norte
    user:
      client-certificate: /home/user/.minikube/profiles/rutas-norte/client.crt
      client-key: /home/user/.minikube/profiles/rutas-norte/client.key
contexts:                       # THE COMBINATION of both + namespace
  - name: rutas-norte
    context:
      cluster: rutas-norte
      user: rutas-norte
      namespace: default
current-context: rutas-norte    # which one is active right now

The key idea: a context is a triple (cluster, user, namespace). Switching context means switching cluster or identity with a single command. In a professional environment you will have contexts such as rutas-norte-dev, rutas-norte-pre and rutas-norte-pro, and getting the context wrong is the quickest way to cause an incident.

Context management commands

# See every available context; the active one carries an asterisk
kubectl config get-contexts

# Switch context
kubectl config use-context rutas-norte

# See only the name of the active context
kubectl config current-context

# Set the default namespace of the active context
kubectl config set-context --current --namespace=rutas-norte-dev
CURRENT   NAME          CLUSTER       AUTHINFO      NAMESPACE
*         rutas-norte   rutas-norte   rutas-norte   rutas-norte-dev
          docker-desktop docker-desktop docker-desktop

That last command —setting the default namespace— will save you typing -n rutas-norte-dev hundreds of times during the course. Do it as soon as you create the namespace in lesson 01-07.

The KUBECONFIG variable: you can have several files and combine them. That is the usual arrangement when you work with several clients or environments:

export KUBECONFIG=~/.kube/config:~/.kube/rutas-norte-pro.yaml
kubectl config get-contexts

Security tip: the kubeconfig contains credentials. Treat it like a private key: 600 permissions, never in Git, never over chat.

  1. Anatomy of a kubectl command

The whole CLI follows this grammar:

kubectl [verb] [type] [name] [flags]
         │      │      │      │
         │      │      │      └── -n, -o, -l, --dry-run, --watch...
         │      │      └───────── name of the object (optional: if absent, all of them)
         │      └──────────────── pod, deployment, service, pvc, node...
         └─────────────────────── get, describe, apply, delete, logs, exec...

Examples read aloud:

kubectl get pods                                  # give me every pod in the current namespace
kubectl get pod bookings-api -n rutas-norte-dev   # give me that specific pod in that namespace
kubectl describe deployment web-store             # explain that Deployment to me in detail
kubectl delete pod bookings-api                   # delete that pod

Names, plurals and short names

Kubernetes accepts singular, plural and short name interchangeably. kubectl get po, kubectl get pod and kubectl get pods are identical. The short names that save the most typing:

Type Short name Type Short name
pods po services svc
deployments deploy namespaces ns
replicasets rs configmaps cm
statefulsets sts persistentvolumeclaims pvc
daemonsets ds persistentvolumes pv
ingresses ing serviceaccounts sa

The complete, authoritative list for your cluster, with short names, API group and whether the resource lives in a namespace:

kubectl api-resources
NAME          SHORTNAMES   APIVERSION   NAMESPACED   KIND
configmaps    cm           v1           true         ConfigMap
pods          po           v1           true         Pod
services      svc          v1           true         Service
nodes         no           v1           false        Node
deployments   deploy       apps/v1      true         Deployment
ingresses     ing          networking.k8s.io/v1  true  Ingress

The NAMESPACED column matters: it explains why kubectl get nodes -n rutas-norte-dev ignores the namespace (nodes are cluster-scoped).

The namespace, always present

kubectl get pods                       # the current context's namespace
kubectl get pods -n rutas-norte-pre    # one specific namespace
kubectl get pods -A                    # every namespace (--all-namespaces)

Forgetting the namespace is the number one cause of "my pod has vanished".

  1. The day-to-day verbs

We will work on a Rutas Norte scenario with the web store and the API already deployed in rutas-norte-dev.

4.1. get — a quick inventory

kubectl get pods -n rutas-norte-dev
NAME                              READY   STATUS             RESTARTS      AGE
bookings-api-6c8d7f9b45-2xk9p     1/1     Running            0             12m
bookings-api-6c8d7f9b45-hn4vq     1/1     Running            0             12m
web-store-7d4f8c6b9-lq2mn         1/1     Running            0             18m
notifications-worker-59c7d8f      0/1     CrashLoopBackOff   5 (48s ago)   9m

How to read each column:

  • READY 1/1: containers ready / total containers in the pod. 0/1 means the container exists but has not passed its readiness probe.
  • STATUS: Running is normal; Pending (the scheduler finds no room), ContainerCreating, ImagePullBackOff (it cannot pull the image), CrashLoopBackOff (it starts and dies in a loop), Completed (finished tasks).
  • RESTARTS: restarts. A growing number is the clearest sign of a problem.
  • AGE: since the object was created.

In that output, notifications-worker is clearly broken. The next three verbs are how you investigate it.

4.2. describe — the diagnosis

kubectl describe pod notifications-worker-59c7d8f -n rutas-norte-dev
Name:         notifications-worker-59c7d8f
Namespace:    rutas-norte-dev
Node:         rutas-norte/192.168.49.2
Labels:       app=notifications-worker
              app.kubernetes.io/part-of=rutas-norte
              environment=dev
Status:       Running
Containers:
  worker:
    Image:          registry.rutasnorte.example/notifications-worker:1.2.0
    State:          Waiting
      Reason:       CrashLoopBackOff
    Last State:     Terminated
      Reason:       Error
      Exit Code:    1
    Restart Count:  5
Events:
  Type     Reason     Age                   From     Message
  ----     ------     ----                  ----     -------
  Normal   Scheduled  9m                    default-scheduler  Successfully assigned ...
  Normal   Pulled     8m (x4 over 9m)       kubelet  Container image already present
  Warning  BackOff    45s (x22 over 8m)     kubelet  Back-off restarting failed container

The Events section is always the first thing to read. It is the diary of what the cluster has tried to do with that object and why it failed. Here it tells us that the container exits with code 1 and the kubelet retries with a growing back-off. We also see that the scheduler did assign it (so it is not a resource problem) and that the image pulled fine (so it is not a registry problem): the failure is inside the application.

describe works with any type: kubectl describe node rutas-norte, kubectl describe svc bookings-api, kubectl describe pvc postgres-data.

4.3. logs — the application's voice

# The pod's last lines
kubectl logs notifications-worker-59c7d8f -n rutas-norte-dev

# The logs of the PREVIOUS run: essential in a CrashLoopBackOff
kubectl logs notifications-worker-59c7d8f -n rutas-norte-dev --previous

# Follow live, last 50 lines, with timestamps
kubectl logs -f --tail=50 --timestamps bookings-api-6c8d7f9b45-2xk9p -n rutas-norte-dev

# Aggregated logs from ALL the replicas selected by label
kubectl logs -l app=bookings-api -n rutas-norte-dev --tail=20

# One specific container of a multi-container pod
kubectl logs my-pod -c metrics-sidecar
[2026-08-05T02:14:07Z] notifications-worker v1.2.0 starting up
[2026-08-05T02:14:07Z] connecting to redis-cache:6379
[2026-08-05T02:14:12Z] ERROR: dial tcp: lookup redis-cache: no such host
[2026-08-05T02:14:12Z] process exited with code 1

There is the cause: the worker cannot find redis-cache through DNS, probably because the Service does not exist yet. --previous is the most forgotten flag and the most useful: without it, in a CrashLoopBackOff you will see the logs of a container that has just started and has not failed yet.

4.4. exec — getting into the container

# A one-off command
kubectl exec bookings-api-6c8d7f9b45-2xk9p -n rutas-norte-dev -- env | grep DB_

# An interactive session
kubectl exec -it bookings-api-6c8d7f9b45-2xk9p -n rutas-norte-dev -- sh
DB_HOST=bookings-postgres
DB_PORT=5432
DB_NAME=bookings

The -- separates kubectl's flags from the command to run inside; forgetting it is a classic mistake. -it means interactive with a terminal, just as in Docker.

A professional note: many well-built images are distroless and have neither sh nor networking tools. For those cases there is kubectl debug, which injects an ephemeral container with utilities; we will see it in module 7.

4.5. apply and delete — changing the desired state

kubectl apply -f k8s/base/bookings-api.yaml         # one file
kubectl apply -f k8s/base/                          # every file in the directory
kubectl apply -f k8s/base/ --recursive              # including subdirectories

kubectl delete -f k8s/base/bookings-api.yaml        # delete whatever the file declares
kubectl delete pod bookings-api-6c8d7f9b45-2xk9p    # delete a specific object
kubectl delete pods -l app=bookings-api             # delete by label

apply is the heart of the declarative model and we will study it thoroughly in the next lesson, Objects, YAML Manifests and the Declarative Model.

About delete: remember that deletion is asynchronous and that deleting a pod managed by a controller does not remove it, it only causes a new one to be created. To remove it for real you have to delete the controller.

4.6. edit — a one-off change in place

kubectl edit deployment bookings-api -n rutas-norte-dev

It opens the object in your editor ($EDITOR) and applies the changes when you save. It is convenient for experimenting and dangerous in production: the change does not end up in Git, so the next apply from the repository will silently revert it. Use it to explore; never as a way to deploy.

4.7. port-forward — local access without exposing anything

kubectl port-forward pod/web-store-7d4f8c6b9-lq2mn 8080:80 -n rutas-norte-dev
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80

Now http://localhost:8080 reaches that pod. The format is local-port:container-port. It also works against a Service (svc/bookings-api 8081:80). It is the fastest way to check that something works before you have Ingress, and it is exactly what you will use in lesson 01-07 to see the Rutas Norte store in your browser. The process stays in the foreground: stop it with Ctrl+C.

4.8. explain — the offline documentation

kubectl explain pod.spec.containers.resources
KIND:       Pod
VERSION:    v1
FIELD: resources <ResourceRequirements>
DESCRIPTION:
    Compute Resources required by this container. Cannot be updated.
FIELDS:
  limits <map[string]Quantity>
    Limits describes the maximum amount of compute resources allowed.
  requests <map[string]Quantity>
    Requests describes the minimum amount of compute resources required.

This command deserves a paragraph of its own because it is the one that pays off most in the long run. It queries the schema of your own cluster, including the CRDs installed, so it never gives you stale information or information from another version. It works at any depth and, with --recursive, shows the complete tree of fields:

kubectl explain deployment.spec.strategy
kubectl explain ingress.spec --recursive | head -30

In the CKA/CKAD certification, where only the official documentation may be consulted, kubectl explain is the shortcut that saves minutes.

  1. Output formats and queries

The -o flag turns kubectl from a viewer into a query tool.

Format What it is for
(default) A readable summary table
-o wide Adds columns: pod IP, node, image
-o yaml The complete object exactly as it is in the API, with its status
-o json The same, in JSON, for processing with jq
-o name Only type/name, ideal for chaining commands
-o jsonpath='...' Extracts specific fields
-o custom-columns=... A bespoke table
kubectl get pods -o wide -n rutas-norte-dev
NAME                            READY   STATUS    IP           NODE          NOMINATED NODE
bookings-api-6c8d7f9b45-2xk9p   1/1     Running   10.244.0.17  rutas-norte   <none>
web-store-7d4f8c6b9-lq2mn       1/1     Running   10.244.0.14  rutas-norte   <none>
# The complete object, including what the system has written
kubectl get pod bookings-api-6c8d7f9b45-2xk9p -n rutas-norte-dev -o yaml | head -25

# Extract specific fields with jsonpath
kubectl get pods -n rutas-norte-dev \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.podIP}{"\n"}{end}'

# A bespoke table: name, image and node
kubectl get pods -n rutas-norte-dev \
  -o custom-columns='POD:.metadata.name,IMAGE:.spec.containers[0].image,NODE:.spec.nodeName'

# Sorting: the pods that have restarted most, first
kubectl get pods -A --sort-by=.status.containerStatuses[0].restartCount

# Sorting by age
kubectl get pods -n rutas-norte-dev --sort-by=.metadata.creationTimestamp
POD                             IMAGE                                                     NODE
bookings-api-6c8d7f9b45-2xk9p   registry.rutasnorte.example/bookings-api:2.4.0            rutas-norte
web-store-7d4f8c6b9-lq2mn       registry.rutasnorte.example/web-store:1.8.0               rutas-norte

custom-columns and jsonpath are the two tools that make kubectl fit for quick audits: "give me every image in use in production", "tell me which pods have no memory limits".

  1. Label selection and live watching

Remember from the lesson Key Concepts and Terminology that labels are the system's coupling mechanism. The -l flag gives you access to that same mechanism from the CLI:

# Equality
kubectl get pods -l app=bookings-api -n rutas-norte-dev

# Several conditions (AND)
kubectl get pods -l app=bookings-api,environment=dev -n rutas-norte-dev

# Inequality
kubectl get pods -l 'environment!=pro' -A

# Sets
kubectl get pods -l 'app in (bookings-api,web-store)' -n rutas-norte-dev
kubectl get pods -l 'app notin (occupancy-reports)' -n rutas-norte-dev

# Existence of the key, with any value
kubectl get pods -l app.kubernetes.io/part-of -A

# The whole Rutas Norte platform, of any type, in one go
kubectl get all -l app.kubernetes.io/part-of=rutas-norte -A

That last query is the reason it is worth being disciplined about the project's labelling scheme: it lets you see, delete or inspect the entire platform with a single command.

Live watching with --watch (or -w): the command does not finish and prints changes as they happen. It is the best way to see the reconciliation loop in action:

kubectl get pods -l app=bookings-api -n rutas-norte-dev --watch
NAME                            READY   STATUS              RESTARTS   AGE
bookings-api-6c8d7f9b45-2xk9p   1/1     Running             0          12m
bookings-api-6c8d7f9b45-2xk9p   1/1     Terminating         0          14m
bookings-api-6c8d7f9b45-wq7rt   0/1     Pending             0          0s
bookings-api-6c8d7f9b45-wq7rt   0/1     ContainerCreating   0          1s
bookings-api-6c8d7f9b45-wq7rt   1/1     Running             0          4s

You recognise that sequence: it is exactly the walkthrough from the lesson Kubernetes ArchitecturePending while the scheduler decides, ContainerCreating while the kubelet works, Running when the container is alive— happening in front of your eyes because the ReplicaSet has replaced a deleted pod.

Sibling commands that also wait:

kubectl wait --for=condition=Ready pod -l app=bookings-api -n rutas-norte-dev --timeout=60s
kubectl get events -n rutas-norte-dev --watch

  1. Imperative versus declarative

kubectl supports both styles and it is worth having a view on when to use each.

Aspect Imperative Declarative
Typical command kubectl create deployment ..., kubectl scale, kubectl expose kubectl apply -f k8s/
Source of truth Your terminal history The repository's files
Reproducible No Yes
Reviewable in a pull request No Yes
Speed for something one-off Very high Medium
Recommended use Learning, quick tests, timed exams Anything that reaches a real environment
# Imperative: fast, but it leaves no trace in any file
kubectl create deployment bookings-api --image=registry.rutasnorte.example/bookings-api:2.4.0
kubectl scale deployment bookings-api --replicas=4
kubectl expose deployment bookings-api --port=80 --target-port=3000

# Declarative: the desired state lives in Git
kubectl apply -f k8s/base/bookings-api.yaml

The middle ground, and probably the most useful trick in the whole lesson: generate the manifest with an imperative command and save it, using --dry-run=client -o yaml.

kubectl create deployment bookings-api \
  --image=registry.rutasnorte.example/bookings-api:2.4.0 \
  --dry-run=client -o yaml > k8s/base/bookings-api.yaml

This creates nothing in the cluster: it only prints the YAML kubectl would have sent. You edit it, version it and apply it. It is the usual way to avoid writing manifests from scratch, and in the CKAD it is practically mandatory if you want to finish in time. The next lesson goes deeper into --dry-run, kubectl diff and the internal mechanics of apply.

  1. Productivity: aliases, completion, explain and plugins

Add this to your ~/.bashrc or ~/.zshrc. The difference in comfort is enormous:

# Completion (bash)
source <(kubectl completion bash)

# A short alias, with completion for the alias too
alias k=kubectl
complete -o default -F __start_kubectl k

# Common shortcuts
alias kgp='kubectl get pods'
alias kgpw='kubectl get pods -o wide'
alias kd='kubectl describe'
alias kl='kubectl logs'
alias kaf='kubectl apply -f'
alias kns='kubectl config set-context --current --namespace'

For zsh, swap completion bash for completion zsh. With this, k get po -n <TAB> even completes the names of the existing namespaces for you, because completion queries the API.

Other tools worth knowing about, even though we are not using them yet:

Tool What it adds
krew A plugin manager for kubectl (kubectl krew install ...)
kubectl ctx / kubectl ns (the ctx and ns plugins) Switching context and namespace interactively
kubectl tree Seeing the Deployment → ReplicaSet → Pod hierarchy
kubectl neat Strips a -o yaml of system-generated fields
stern Aggregated, colour-coded logs from many pods at once
k9s A terminal interface for navigating the cluster

Installing krew and a plugin, by way of example:

kubectl krew install ctx ns
kubectl ns rutas-norte-dev

None of this is essential, but on a real working day it saves hours. What matters is the previous part: aliases, completion and kubectl explain.

Common Mistakes and Tips

  • Working in the wrong context. The most expensive mistake of all. Before any destructive command, kubectl config current-context. Configure your prompt to show context and namespace (kube-ps1, starship, oh-my-zsh) and treat it as non-negotiable as soon as you have access to a production environment.
  • Forgetting the namespace. If an object "does not exist", try it with -A before concluding anything. Set the default namespace with kubectl config set-context --current --namespace=....
  • Reading logs without --previous in a CrashLoopBackOff. You will see the current container starting up, not the error that killed the previous one.
  • Omitting the -- in kubectl exec. Without it, kubectl reads your command's flags as its own and fails confusingly.
  • Using kubectl edit as a deployment method. The change is not recorded anywhere and is lost on the next apply. It is for investigating, not for operating.
  • Believing kubectl delete pod removes the application. If there is a controller behind it, the pod comes back. You have to delete the Deployment, or better, the manifest with kubectl delete -f.
  • Ignoring kubectl explain. It is exact documentation for your version, with no internet connection and without switching windows. Get into the habit of using it before searching the web.
  • Tip: memorise this diagnostic trio and always apply it in this order — kubectl get (does it exist and in what state?), kubectl describe (what has the cluster tried and what do the events say?), kubectl logs (what does the application say?). It resolves the vast majority of incidents.

Exercises

Exercise 1: Context, namespace and exploration

On your rutas-norte cluster:

  1. Show the active context and check that it is the right one.
  2. Create the rutas-norte-dev namespace and set it as the context's default namespace.
  3. List every pod in the cluster, from every namespace, showing the node and the IP.
  4. Find out, without leaving the terminal and without searching the internet, what a pod's spec.restartPolicy field means and which values it accepts.
  5. Show the kube-system pods sorted by age, from oldest to most recent.

Exercise 2: Advanced audit queries

Rutas Norte's security team asks for an inventory. Write the commands that answer:

  1. Every image in use in the cluster, with the pod and the namespace it belongs to, in table format.
  2. The pods of the whole cluster sorted by restart count, to spot the unstable ones.
  3. Only the names of the pods carrying the label app.kubernetes.io/part-of=rutas-norte, in a format suitable for chaining with another command.
  4. Every resource in the cluster that does not live in a namespace.

Exercise 3: From imperative to declarative

Without creating anything in the cluster, generate the YAML manifest of a pod called web-store with the image registry.rutasnorte.example/web-store:1.8.0, save it to k8s/base/web-store-pod.yaml, and then explain what the difference is between running that command with --dry-run=client and without it. As an extra check, use kubectl to find out whether the Pod resource belongs to the core API group or to apps.

Solutions

Solution 1

# 1
kubectl config current-context                       # -> rutas-norte

# 2
kubectl create namespace rutas-norte-dev
kubectl config set-context --current --namespace=rutas-norte-dev

# 3
kubectl get pods -A -o wide

# 4
kubectl explain pod.spec.restartPolicy
FIELD: restartPolicy <string>
DESCRIPTION:
    Restart policy for all containers within the pod. One of Always, OnFailure,
    Never. Default to Always.
# 5
kubectl get pods -n kube-system --sort-by=.metadata.creationTimestamp

Solution 2

# 1
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,POD:.metadata.name,IMAGE:.spec.containers[*].image'

# 2
kubectl get pods -A --sort-by=.status.containerStatuses[0].restartCount

# 3
kubectl get pods -A -l app.kubernetes.io/part-of=rutas-norte -o name

# 4
kubectl api-resources --namespaced=false

Point 3 returns lines such as pod/bookings-api-6c8d7f9b45-2xk9p, which can be piped straight into kubectl delete or kubectl describe. Point 4 lists nodes, namespaces, persistentvolumes, storageclasses, clusterroles, among others: the cluster-scoped resources we saw in the concepts lesson.

Solution 3

kubectl run web-store \
  --image=registry.rutasnorte.example/web-store:1.8.0 \
  --dry-run=client -o yaml > k8s/base/web-store-pod.yaml

The difference: with --dry-run=client, kubectl builds the object locally and prints it, sending nothing to the server; no pod is created and the cluster does not even need to be running. Without that flag, the request is sent to the apiserver, goes through authentication, authorization and admission, is written to etcd and the pod is really created. There is also --dry-run=server, which does send the request but tells the server not to persist it: it validates against the real admission webhooks without creating anything. We will see it in the next lesson.

For the API group:

kubectl api-resources | grep -w pods
pods    po    v1    true    Pod

The APIVERSION column shows a plain v1, with no slash and no group prefix: that means Pod belongs to the core group (or "legacy"), unlike Deployment, which appears as apps/v1.

Conclusion

kubectl is an HTTP client with a very regular grammar: verb, type, name and flags. Mastering it comes down to internalising that grammar, always knowing which context and namespace you are working in, handling the getdescribelogs diagnostic trio fluently, and taking advantage of the output formats and label selection to turn the CLI into a genuine query tool. With kubectl explain you also have exact documentation for your own cluster without leaving the terminal, and with aliases and completion the daily work stops being typing.

In this lesson we have used apply without fully explaining what it does inside, and we have seen YAML manifests without breaking down their structure. The next lesson settles that debt, Objects, YAML Manifests and the Declarative Model: the field-by-field anatomy of an object, API groups and versions, the YAML you need to know, the real difference between apply, create and replace, the tools for validating beforehand and how to organise the Rutas Norte manifests in the k8s/ directory.

Kubernetes Course

Module 1: Introduction to Kubernetes

Module 2: Core Kubernetes Components

Module 3: Configuration and Secret Management

Module 4: Networking in Kubernetes

Module 5: Storage in Kubernetes

Module 6: Advanced Kubernetes Concepts

Module 7: Monitoring and Logging

Module 8: Kubernetes Security

Module 9: Scaling and Performance

Module 10: Kubernetes Ecosystem and Tooling

Module 11: Case Studies and Real-World Applications

Module 12: Preparing for Kubernetes Certification

© Copyright 2026. All rights reserved