
Table of Contents
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.
kubectl at it, then apply a Deployment manifest that runs your container image as Pods. Add a Service to give those Pods a stable network identity and an Ingress to route external HTTP traffic to it.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 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.
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.