Kubernetes Basics: Deploy Your First App to a K8s Cluster (2026)

Khimananda Oli 9 min read Database
Kubernetes Basics: Deploy Your First App to a K8s Cluster (2026)

By Khimananda Oli | Last reviewed: August 2026

You containerized your app, it runs perfectly on your laptop, and then a single server falls over at 2 a.m. and takes the whole site with it. That gap — between "runs in one container" and "stays up, scales, and self-heals in production" — is exactly what Kubernetes basics close. This guide assumes you already have a Docker image (if not, start with my Docker for beginners guide, which is the prerequisite) and walks you from an empty managed cluster to a live, scaled, self-updating deployment. The same container-orchestration workflow underpins the production DevOps and cloud services I run for teams, and by the end you will deploy your first app to a K8s cluster, expose it with a Service and Ingress, scale it, and ship a zero-downtime rolling update.

Control PlaneAPI ServerSchedulerController Manageretcd (cluster state)Worker Node 1kubeletPodPodWorker Node 2kubeletPodPodkubectlyou + manifests
Kubernetes cluster anatomy: kubectl sends your manifests to the control plane's API server, which schedules Pods onto worker nodes managed by kubelet.

What is a Kubernetes cluster, and what are pods, deployments, and services?

A Kubernetes cluster is a group of machines (nodes) that run your containers under a shared brain called the control plane. You never place containers on a specific server by hand; you describe the desired state in YAML, and Kubernetes continuously works to make reality match that description. That declarative model is the whole reason Kubernetes basics feel different from running Docker by hand. The core objects you will use to deploy your first app are:

  • Pod — the smallest deployable unit. A Pod wraps one (occasionally a few tightly coupled) containers plus their shared network and storage. Pods are disposable and get a fresh IP every time they restart, so you rarely create them directly.
  • Deployment — the object you actually write. It declares "run N replicas of this image" and manages a ReplicaSet that keeps exactly that many Pods alive, recreating any that crash.
  • Service — a stable network identity. Because Pod IPs churn, a Service gives a fixed virtual IP and DNS name that load-balances across the current Pods behind a label selector.
  • Ingress — an HTTP router at the edge of the cluster that maps a hostname and path to a Service, so external users reach your app over ports 80 and 443.

The control plane's API server is the front door for every request, the scheduler decides which node a new Pod lands on, etcd stores the entire cluster state, and each node's kubelet makes sure its assigned Pods are actually running. You interact with all of it through one command-line tool, kubectl.

How do you set up a managed Kubernetes cluster and connect kubectl?

Running your own control plane is real work — etcd backups, certificate rotation, upgrades. For a first app, use a managed Kubernetes cluster from any major provider (GKE, EKS, AKS, or DigitalOcean Kubernetes); the provider operates the control plane for you and you pay only for the worker nodes. Create a small two-node cluster in the provider console or CLI, then wire up your local tools. First confirm kubectl is installed and talking to the cluster:

# check the client and cluster versions
kubectl version

# your provider CLI writes cluster credentials into ~/.kube/config
# (example: DigitalOcean)
doctl kubernetes cluster kubeconfig save my-first-cluster

# confirm the nodes are Ready
kubectl get nodes

A healthy response lists your worker nodes with a Ready status. The kubeconfig file that your provider CLI writes is what points kubectl at the right cluster, so keep it safe — it holds cluster admin credentials. If you provision clusters repeatedly, define them as code instead of clicking through a console; that is where an Infrastructure as Code with Terraform workflow pays off, giving you a reproducible cluster you can destroy and rebuild on demand.

Push your image somewhere the cluster can pull it

The cluster's nodes pull your container image from a registry, not from your laptop. Tag and push the image you built in the Docker guide to a registry the cluster can reach (Docker Hub, GitHub Container Registry, or your provider's registry):

docker tag my-app:latest registry.example.com/my-app:1.0.0
docker push registry.example.com/my-app:1.0.0

How do you write a Deployment, Service, and Ingress manifest?

Three manifests take you from an image to a public URL. Start with the Deployment, which tells Kubernetes to run three replicas of your image and how to health-check them. Save it as deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
        - name: my-app
          image: registry.example.com/my-app:1.0.0
          ports:
            - containerPort: 8080
          resources:
            requests:
              cpu: "100m"
              memory: "128Mi"
            limits:
              cpu: "500m"
              memory: "256Mi"
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10

The selector.matchLabels must match the Pod template's labels — that link is how the Deployment finds and owns its Pods, and how the Service will find them next. The readinessProbe is what makes rolling updates safe: Kubernetes only sends traffic to a Pod once /health returns 200, and it uses the same signal to know an old Pod can be retired. Resource requests help the scheduler place Pods; limits stop one Pod from starving its neighbours.

Next, a Service of type ClusterIP gives those three Pods one stable in-cluster address. Save it as service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: my-app
spec:
  type: ClusterIP
  selector:
    app: my-app
  ports:
    - port: 80
      targetPort: 8080

The Service's selector (app: my-app) is the same label the Deployment stamps on every Pod, so the Service automatically load-balances across whichever Pods currently exist — no IP addresses hard-coded anywhere. Finally, an Ingress exposes the Service to the internet over HTTP/HTTPS. Save it as ingress.yaml (this assumes an ingress controller such as ingress-nginx is installed in the cluster):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: my-app
                port:
                  number: 80
Deploymentreplicas: 3ReplicaSetkeeps 3 alivePod (app: my-app)Pod (app: my-app)Pod (app: my-app)Serviceselector: app=my-app
The ownership chain: a Deployment manages a ReplicaSet, the ReplicaSet keeps three Pods running, and a Service routes traffic to all Pods that match the app label.

How do you deploy the app and scale it on the cluster?

With the three manifests written, applying them is a single command per file — or all at once if they live in the same folder:

# apply everything in the current directory
kubectl apply -f .

# watch the Pods come up
kubectl get pods -w

# confirm the Deployment reports 3/3 ready
kubectl get deployment my-app

# inspect the Service and Ingress
kubectl get service my-app
kubectl get ingress my-app

Once kubectl get pods shows three Pods in Running state and the Ingress lists an external address, your app is live at the host you configured. When traffic grows, scaling is one command because the Deployment already owns the replica count:

# scale up to 6 replicas immediately
kubectl scale deployment my-app --replicas=6

# or let Kubernetes scale on CPU automatically between 3 and 10 Pods
kubectl autoscale deployment my-app --min=3 --max=10 --cpu-percent=70

The second command creates a HorizontalPodAutoscaler that adds Pods when average CPU crosses 70% and removes them when load drops — the elasticity that makes a Kubernetes cluster worth the setup. Every new Pod inherits the same image, labels, and probes, and the Service picks it up automatically the moment it passes its readiness check. A few commands worth memorizing while you learn:

  • kubectl logs deploy/my-app — stream logs from the Deployment's Pods.
  • kubectl describe pod <name> — see events and the reason a Pod is stuck (image pull errors, failing probes).
  • kubectl exec -it <pod> -- sh — open a shell inside a running container to debug.

How do rolling updates work in Kubernetes for zero-downtime deploys?

The payoff of a Deployment is the rolling update: shipping a new image without ever taking the app offline. When you change the image tag, Kubernetes brings up new Pods, waits for each to pass its readiness probe, shifts traffic to it, and only then terminates an old Pod — repeating until every replica runs the new version. Trigger it by setting a new image and watch the rollout:

# roll out a new image version
kubectl set image deployment/my-app my-app=registry.example.com/my-app:1.1.0

# watch the rollout progress Pod by Pod
kubectl rollout status deployment/my-app

# if the new version misbehaves, roll straight back
kubectl rollout undo deployment/my-app

You control the pace with the Deployment's strategy block. The defaults — maxUnavailable: 25% and maxSurge: 25% — mean Kubernetes never drops more than a quarter of capacity and never runs more than 125% of it mid-update, so users see no downtime. Combined with the readiness probe, a broken image simply never receives traffic, and kubectl rollout undo reverts to the previous ReplicaSet in seconds. This is why teams pair Kubernetes with CI: a pipeline builds the image, pushes it, and runs kubectl set image automatically on every merge.

Start (v1)Pod v1Pod v1Pod v1Rolling (mixed)Pod v2Pod v1v1 drainingDone (v2)Pod v2Pod v2Pod v2maxSurgereplace
A zero-downtime rolling update in Kubernetes: new v2 Pods start and pass readiness checks before old v1 Pods drain, so capacity never drops and users never see an outage.

Conclusion

You have covered the Kubernetes basics that matter: what a cluster, pod, deployment, service, and ingress each do; how to point kubectl at a managed K8s cluster; the three manifests that deploy your first app; and how scaling and rolling updates keep it fast and always-on. Pick one containerized app this week, apply a Deployment and Service to a small managed cluster, and practise a rolling update until kubectl rollout undo feels routine. When you are ready to run Kubernetes in production — autoscaling, ingress, monitoring, and CI-driven deploys — get in touch or explore my DevOps and cloud services and the cloud infrastructure case studies in my portfolio to see how a hardened cluster fits your team.

Frequently Asked Questions

A Kubernetes cluster is a group of machines that run your containers under a shared control plane. You describe the desired state in YAML, and Kubernetes continuously schedules, restarts, and load-balances containers across the nodes to match it, so you never place workloads on servers by hand.

A container is a single running image. A Pod is the smallest unit Kubernetes schedules and can hold one or more tightly coupled containers that share a network address and storage. In most apps a Pod wraps exactly one container, and Kubernetes manages Pods, not containers directly.

Yes. Kubernetes orchestrates container images, so you should first know how to build and run one. Containerize your app with Docker, push the image to a registry, and only then deploy it to a cluster with the manifests shown in this guide.

A Deployment runs and maintains a set of Pods from your image, handling replicas, restarts, and rolling updates. A Service gives those Pods a single stable IP and DNS name and load-balances traffic to them. You almost always need both: the Deployment to run the app and the Service to reach it.

An Ingress is an HTTP and HTTPS router at the edge of the cluster. It maps a hostname and URL path to a backend Service, letting external users reach your app on ports 80 and 443. It requires an ingress controller, such as ingress-nginx, to be installed in the cluster.

For a first app, use a managed cluster such as GKE, EKS, AKS, or DigitalOcean Kubernetes. The provider runs the control plane, etcd backups, and upgrades, and you pay only for worker nodes. Self-hosting the control plane is significant operational work best left until you truly need it.

Your cloud provider's CLI writes cluster credentials into your kubeconfig file at ~/.kube/config. Run the provider command that saves those credentials, then verify the connection with kubectl get nodes. A list of nodes in Ready status confirms kubectl is pointed at the right cluster.

Push your container image to a registry, write a Deployment manifest referencing that image, add a Service and an Ingress, then run kubectl apply -f on the manifests. Check kubectl get pods until the Pods are Running and the Ingress shows an external address.

Run kubectl scale deployment my-app --replicas=6 to set a fixed count, or create a HorizontalPodAutoscaler with kubectl autoscale to add and remove Pods automatically based on CPU. New Pods inherit the same image and probes, and the Service starts routing to them once they pass readiness checks.

A rolling update replaces Pods gradually when you ship a new image. Kubernetes starts new Pods, waits for each to pass its readiness probe, shifts traffic to it, then retires an old Pod, repeating until all replicas run the new version. Users experience no downtime during the swap.

Run kubectl rollout undo deployment/my-app to revert to the previous ReplicaSet within seconds. Kubernetes keeps a revision history, so you can also roll back to a specific earlier revision. A readiness probe helps by preventing a broken new version from ever receiving traffic.

Pods are disposable, so each new Pod gets a fresh cluster IP. That is exactly why you put a Service in front of them: the Service provides a stable virtual IP and DNS name and load-balances across whichever Pods currently match its label selector, so callers never track Pod IPs.

A readiness probe tells Kubernetes when a Pod is ready to receive traffic, usually via an HTTP health endpoint. The Service only routes to Pods that pass it, and rolling updates use it to confirm a new Pod is healthy before retiring an old one, which is what makes deploys zero-downtime.

Use apps/v1 for Deployments and ReplicaSets, v1 for Services, and networking.k8s.io/v1 for Ingress. These are the stable API versions on current Kubernetes releases in 2026. Older beta versions like extensions/v1beta1 were removed years ago and no longer apply.

A CI/CD pipeline builds your container image, pushes it to a registry, and then updates the cluster with kubectl set image or a manifest apply on every merge. Kubernetes handles the rolling update, so shipping to production becomes an automatic, low-risk step in the pipeline.