Skip to Content

Kubernetes Tutorial: Deploy Your First App on Kubernetes Today

Kubernetes Tutorial: Deploy Your First App on Kubernetes Today

Summary

  • What you'll build: a deployed, exposed, scaled app running on a real Kubernetes cluster.
  • Prerequisites: a local cluster (kind, minikube, or Docker Desktop's built-in Kubernetes) and kubectl.
  • Time: about 20 minutes.
  • Level: beginner. Basic Docker or container familiarity helps but is not required.
  • What you'll learn: Pods, Deployments, Services, scaling, self-healing, and the declarative YAML workflow.
  • Real commands: every step is real kubectl run against a real cluster, not pseudo-code.

You have heard that Kubernetes runs the world's containers, and also that it is famously hard. Both are true, but here is the part nobody tells beginners: deploying your first app to Kubernetes is only a handful of commands, and doing it once makes the whole thing far less intimidating.

By the end of this tutorial you will have deployed an app to a Kubernetes cluster, exposed it, reached it in your browser, scaled it to multiple copies, watched Kubernetes heal it automatically, and done the whole thing again the proper declarative way with a YAML file. Every command below is real kubectl, run against a real cluster. You need two things first: a running Kubernetes cluster (a local one is perfect, see the next section) and the kubectl command-line tool. The whole walkthrough takes about twenty minutes.

What Is Kubernetes, in One Minute

Kubernetes is a system for running containers across a group of machines for you. You tell it what you want ("run three copies of this app, keep them healthy, and make them reachable"), and Kubernetes makes reality match that, restarting crashed containers, rescheduling them when a machine dies, and load-balancing across the copies. You describe the desired state; Kubernetes does the work of maintaining it.

The vocabulary that trips up beginners is small once you see it in action. A Pod is the smallest unit, one or more containers running together. A Deployment manages a set of identical Pods and keeps the right number running. A Service gives those Pods one stable address so other things can reach them even as individual Pods come and go. You will create all three in the next few minutes. If you want more background first, our What Is Kubernetes? guide covers the concepts, but you do not need it to start.

Before You Start

You need a cluster and kubectl. For learning, a local single-node cluster is ideal, and you have a few options: enable Kubernetes in Docker Desktop (Settings, then Kubernetes, then Enable), or use kind (kind create cluster), or minikube (minikube start). Any of them gives you a real cluster on your own machine. No local setup at all? You can run everything in a browser with KodeKloud's free Kubernetes labs or the multi-node Kubernetes playground.

Once your cluster is up, confirm kubectl can reach it:

kubectl get nodes
NAME                   STATUS   ROLES           AGE   VERSION
k8stut-control-plane   Ready    control-plane   24s   v1.36.1

A node listed as Ready means your cluster is working and you are good to go. If you instead see a connection error, your cluster is not running yet (we cover that in Common Errors).

Step 1: Run Your First App with a Deployment

The simplest way to run an app is to create a Deployment. This one runs the nginx web server:

kubectl create deployment myapp --image=nginx
deployment.apps/myapp created

That single command told Kubernetes your desired state: "I want an app called myapp running the nginx image." Kubernetes created a Deployment, which created a ReplicaSet, which created a Pod to actually run the container. You did not have to manage any of those pieces by hand.

Step 2: See What Kubernetes Created

Look at what is now running. First the Pods:

kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
myapp-68db9dd855-v7tqj   1/1     Running   0          2m37s

Then the Deployment that manages them:

kubectl get deployments
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
myapp   1/1     1            1           2m37s

The Deployment reports how many Pods it wants and how many are ready. Right now that is one of one. If a Pod is still starting, give it a few seconds and run the command again.

Step 3: Expose Your App with a Service

Your Pod is running, but nothing outside the cluster can reach it yet, and its IP changes every time it is recreated. A Service fixes that by giving the Deployment one stable address. Create one:

kubectl expose deployment myapp --port=80 --type=NodePort
service/myapp exposed
kubectl get service myapp
NAME    TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
myapp   NodePort   10.96.117.248   <none>        80:32088/TCP   0s

The simplest way to reach your app from your laptop on a local cluster is kubectl port-forward, which tunnels a local port to the app:

kubectl port-forward service/myapp 8080:80

Leave that running, open another terminal, and curl it:

curl http://localhost:8080
<title>Welcome to nginx!</title>

That is your app, running in Kubernetes, reachable from your machine. (On a real cloud cluster you would more often use a LoadBalancer Service; on a local cluster, port-forward is the quickest way to get traffic in.)

Step 4: Scale Your App

Here is where Kubernetes earns its reputation. Want three copies of your app instead of one? Change the desired number:

kubectl scale deployment myapp --replicas=3
deployment.apps/myapp scaled
kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
myapp-68db9dd855-qvh89   1/1     Running   0          25s
myapp-68db9dd855-skhtb   1/1     Running   0          25s
myapp-68db9dd855-v7tqj   1/1     Running   0          3m26s

You did not start three servers or configure a load balancer by hand. You stated "I want three," and Kubernetes made it so. The Service from Step 3 automatically spreads traffic across all three Pods.

Step 5: Watch Kubernetes Heal Itself

Kubernetes constantly works to keep reality matching your desired state, which means it repairs damage on its own. Delete one of the Pods and watch what happens:

kubectl delete pod myapp-68db9dd855-qvh89
kubectl get pods
pod "myapp-68db9dd855-qvh89" deleted from default namespace
NAME                     READY   STATUS              RESTARTS   AGE
myapp-68db9dd855-qv245   0/1     ContainerCreating   0          0s
myapp-68db9dd855-skhtb   1/1     Running             0          26s
myapp-68db9dd855-v7tqj   1/1     Running             0          3m27s

You asked for three replicas, so when one disappeared, the Deployment immediately created a replacement to get back to three. This self-healing is a core reason teams trust Kubernetes to run production workloads: it recovers from failures without anyone being paged.

Step 6: Do It the Declarative Way with YAML

Typing commands is fine for learning, but real Kubernetes work is declarative: you write the desired state in a YAML file and apply it, so it is repeatable and can live in version control. Here is the same app as a file, deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: nginx
          image: nginx
          ports:
            - containerPort: 80

Our app is currently running from the command we typed earlier. Let us remove that one and recreate it from the file, so the file becomes the single source of truth:

kubectl delete deployment myapp
kubectl apply -f deployment.yaml
deployment.apps "myapp" deleted from default namespace
deployment.apps/myapp created

kubectl apply is idempotent: run it once and it creates the Deployment, run it again after editing the file and it updates only what changed. This file-based, declarative approach is how Kubernetes is actually run in practice, and it is the foundation for GitOps workflows where Git is the source of truth.

What Just Happened

Step back and the model is consistent: you declare a desired state and Kubernetes maintains it. A Deployment says "keep N identical Pods running this image." A Pod is where your container actually runs. A Service gives those Pods one stable address. Scaling is just changing N, self-healing is Kubernetes restoring N after a failure, and YAML is the durable, repeatable way to declare all of it. Almost everything else in Kubernetes builds on these ideas. This KodeKloud crash course walks through them end to end:

Common Errors and Fixes

Beginners hit a predictable set of errors. Here are the common ones and what they mean.

A connection error from kubectl. If kubectl cannot reach a cluster, you will see something like this, which means your cluster is not running or kubectl is pointed at the wrong place:

The connection to the server localhost:8080 was refused - did you specify the right host or port?

Fix: start your cluster (enable Kubernetes in Docker Desktop, or kind create cluster, or minikube start) and check kubectl config current-context.

ImagePullBackOff or ErrImagePull. The Pod cannot download its image, usually a typo in the image name or a private image with no credentials:

NAME                      READY   STATUS             RESTARTS   AGE
broken-7968bb7bf8-hpdml   0/1     ImagePullBackOff   0          102s

Fix: check the image name and tag, and use kubectl describe pod <name> to read the exact reason under Events.

CrashLoopBackOff. The container starts and then exits or crashes repeatedly, so Kubernetes keeps restarting it with a growing delay:

NAME      READY   STATUS             RESTARTS      AGE
crasher   0/1     CrashLoopBackOff   3 (52s ago)   113s

Fix: look at the container's logs with kubectl logs <pod> to see why it is exiting, then fix the app or its configuration.

Clean Up

Remove what you created so your cluster is tidy. Delete the Service and the Deployment (deleting the Deployment removes its Pods too):

kubectl delete service myapp
kubectl delete deployment myapp
service "myapp" deleted from default namespace
deployment.apps "myapp" deleted from default namespace

If you spun up a throwaway local cluster just for this, you can remove the whole thing (for example, kind delete cluster or minikube delete).

Next Steps

You can now deploy, expose, scale, and heal an app on Kubernetes, and declare it in YAML, which is the core of day-to-day Kubernetes. From here, the natural next topics are ConfigMaps and Secrets for configuration, persistent volumes for stateful apps, Ingress for routing real traffic, and rolling updates. KodeKloud's Kubernetes for the Absolute Beginners course takes you through these step by step with hands-on labs, and the full Kubernetes learning path goes from here toward production and certification. If a credential is your goal, our Kubernetes certifications guide maps the options.

Conclusion

Kubernetes feels less like magic and more like a tool the moment you deploy something to it, and you just deployed, exposed, scaled, and healed a real app, then declared it in YAML. The mental model is small (declare a desired state, let Kubernetes maintain it), and everything else is a layer on top. The fastest way to get comfortable is to keep a throwaway cluster open and keep experimenting, because nothing here breaks anything you cannot recreate in seconds.

Ready to Go from First App to Production?

You have deployed your first app; the next step is making those skills real with practice. KodeKloud's Kubernetes for the Absolute Beginners course builds on exactly what you did here with browser-based labs, and the full Kubernetes learning path takes you toward real-world, production-grade Kubernetes. The fastest way to learn is to keep deploying things, so keep a cluster open and keep building.

Start the Kubernetes learning path ->


FAQs

Q1: Do I need a cloud account to learn Kubernetes?

No. A local single-node cluster (Docker Desktop's built-in Kubernetes, kind, or minikube) is a real Kubernetes cluster and is perfect for learning. A browser-based playground works too, with no install at all. You only need a cloud provider when you want a production-grade, internet-facing cluster.

Q2: What is the difference between a Pod, a Deployment, and a Service?

A Pod runs your container (the smallest unit). A Deployment manages a set of identical Pods and keeps the desired number running and healthy. A Service gives those Pods one stable network address so other things can reach them even as individual Pods are replaced. You almost always create a Deployment and a Service together.

Q3: Do I need to learn Docker before Kubernetes?

It helps a lot, because Kubernetes runs containers and you will be working with container images. You do not need to be an expert, but understanding what an image and a container are makes Kubernetes much easier. If you are new to containers, start with our Docker tutorial for beginners first.

Q4: Why can't I reach my app even though the Pod is running?

Usually because there is no Service exposing it, or you have not forwarded a port. On a local cluster, create a Service and use kubectl port-forward to reach it. A LoadBalancer Service stays in a Pending state on a local cluster because there is no cloud load balancer to fulfill it.

Q5: Should I use commands or YAML files?

Commands like kubectl create are great for learning and quick experiments. Real work is done with YAML files and kubectl apply, because they are repeatable, reviewable, and can live in version control. Learn the commands first, then move to YAML, which is exactly the order this tutorial follows.


Sources: commands were run with kubectl against a real single-node cluster. Concepts and resource behavior follow the official Kubernetes documentation (kubernetes.io/docs). KodeKloud learning: Kubernetes for the Absolute Beginners and the Kubernetes learning path.

Nimesha Jinarajadasa Nimesha Jinarajadasa
Nimesha Jianrajadasa is a DevOps & Cloud Consultant, K8s expert, and instructional content strategist-crafting hands-on learning experiences in DevOps, Kubernetes, and platform engineering.

Subscribe to Newsletter

Join me on this exciting journey as we explore the boundless world of web design together.