On this page
How to Deploy Your First App to Kubernetes
A step-by-step tutorial to deploy a containerized app to Kubernetes with a Deployment and Service, using minikube and kubectl.
Quick answer
- Deploying to Kubernetes means applying a Deployment (what to run) and a Service (how to reach it).
- Run a local cluster with minikube,
kubectl applyyour manifests, and port-forward to see the app.- A working deploy shows
Runningpods and a reachable endpoint; that is your success signal.
Before you start
You need Docker or another container tool, kubectl, and a local cluster. Install minikube, then start a cluster:
minikube start
kubectl cluster-infoHow to verify it worked: cluster-info prints your control-plane and CoreDNS URLs without errors.
Step 1 — Write the Deployment
Create deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-app
spec:
replicas: 2
selector:
matchLabels:
app: hello
template:
metadata:
labels:
app: hello
spec:
containers:
- name: hello
image: nginxdemos/hello:plain-text
ports:
- containerPort: 80This declares two replicas of a small demo web server. The selector and the pod labels must match, or the Deployment cannot manage its pods.
Step 2 — Write the Service
Create service.yaml:
apiVersion: v1
kind: Service
metadata:
name: hello-service
spec:
selector:
app: hello
ports:
- port: 80
targetPort: 80The Service selects the pods labeled app: hello and exposes their port 80.
Step 3 — Apply and verify
kubectl apply -f deployment.yaml -f service.yaml
kubectl get pods
kubectl get service hello-serviceHow to verify it worked: get pods shows two pods in Running state, and get service lists hello-service with a port mapping. If a pod is stuck in ImagePullBackOff, the image name or tag is wrong.
Step 4 — Reach the app
For a local cluster, forward a port to the Service:
kubectl port-forward service/hello-service 8080:80Open http://localhost:8080 in a browser. You should see the demo server’s plain-text response.
Step 5 — See self-healing in action
Delete one pod and watch Kubernetes replace it:
kubectl delete pod -l app=hello
kubectl get pods -wA new pod appears to restore the declared replica count — the reconciliation loop from What Is Kubernetes? working as designed.
Where this bites vibecoders
The classic AI-generated deploy uses
latesttags, no resource limits, and no readiness probe, so it appears to deploy but serves 502s or gets evicted under load. After your first apply, checkkubectl describe pod— not justget pods— to see events, image errors, and probe failures that explain what is actually wrong.
Where AI coding assistants get this wrong
- Using
latestimage tags that make rollbacks impossible to reason about. - Omitting resource requests/limits, inviting eviction or node starvation.
- Mismatching the Service selector and the pod labels so nothing routes.
- Forgetting a readiness probe, sending traffic before the app can respond.
Checklist
- Pin exact image tags and make the image available to the cluster.
- Match the Deployment selector and pod labels exactly.
- Set resource requests and limits on every container.
- Add readiness and liveness probes.
- Verify with
kubectl describeandkubectl logs, not just a greenapply.
FAQ
What is the difference between a Deployment and a Pod?
A pod runs containers; a Deployment manages pods, declaring how many replicas should exist and which image they run. You almost never create pods directly — you create a Deployment and let it own the pods.
Why can’t I reach my app after applying?
The most common causes are a Service selector that does not match pod labels, a pod not yet Running, or a port mismatch. Check kubectl get pods and kubectl describe service to trace the path.
Do I need a Service to reach my pods?
Pods have their own IPs, but those change when pods are replaced. A Service gives a stable address that load-balances across matching pods. Always expose pods through a Service.
Related topics
- What Is Kubernetes?
- How to Write a Secure Dockerfile
- Docker vs Podman: What’s the Difference?
- How to Set Up GitOps With Argo CD