Skip to content
Hosting Operations6 min read

Kubernetes Deployment Tutorial: Working App in 15 Minutes

Deploy a working containerized app in 15 minutes with this kubernetes deployment tutorial. Copy the YAML, fix common failures, and verify pods fast.

Written by Abdul AbrorTechnical Hosting Support Engineer
Kubectl terminal showing a deployment rollout, three running pods, and YAML manifest open beside a local cluster.
On this page

TL;DR — Key takeaways

  • A Kubernetes Deployment keeps a specified number of identical pods running and replaces failed containers without manual restarts.
  • `kubectl apply -f deployment.yaml` is idempotent, so you can run the same command again after edits.
  • Pending pods are usually a capacity, storage, or selector problem, so check `kubectl describe pod` before changing cluster size.
  • Roll back a bad release with `kubectl rollout undo deployment/web` instead of deleting the Deployment.
  • Use `kubectl port-forward deployment/web 8080:80` to test locally before adding a Service.

Most kubernetes deployment tutorial pages start with theory. This one starts with a container, a YAML file, and a working endpoint. If you already have kubectl installed and a cluster you can reach, you are about 15 minutes away from a verified Deployment.

I have watched support tickets turn around the moment someone stops re-creating the cluster and starts reading the right three commands. The steps below use a local cluster but work the same against managed Kubernetes with a valid kubeconfig.

Kubernetes Deployment: what is it actually doing?

A Deployment is a controller that maintains a specified number of identical pods. It creates a ReplicaSet, replaces crashed containers, and handles rolling updates when the pod template changes.

You do not manage the pods directly. The Deployment watches the current state, compares it to the desired state in the manifest, and moves the cluster toward that state. A bare pod gives you no restart guarantee; a Deployment does.

  • Desired state: replicas, image, ports
  • Current state: running pods, ready conditions
  • Fix actions: create, restart, or roll back pods

Prerequisites: what do you need before starting?

You do not need a cloud account. A local cluster from kind, Minikube, or k3d is enough for a first deployment.

Install kubectl, bring up a cluster, and confirm it is reachable with `kubectl cluster-info`. Use `kubectl get nodes` to verify at least one node is Ready before you write any YAML.

  • kubectl installed and on PATH
  • A running cluster and a valid kubeconfig context
  • A container image that is already pushed or pullable

Write the working YAML in 15 minutes

The manifest below creates three nginx pods behind one Deployment. Copy it into `deployment.yaml` and adjust only the image or container port if your app differs.

Save this as `deployment.yaml`: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: web spec: replicas: 3 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 ```

  • `apiVersion: apps/v1` is required for Deployment objects.
  • selector.matchLabels must match template.metadata.labels, or the apply fails.
  • containerPort documents the port; it does not publish the service by itself.

Which command deploys the app?

Run `kubectl apply -f deployment.yaml` from the directory containing the manifest. Apply is idempotent, so you can run the same command again after edits.

Then watch the rollout instead of guessing: ```bash kubectl rollout status deployment/web kubectl get pods -l app=web ```

A healthy rollout ends with three running pods and `deployment web successfully rolled out`.

Pending or CrashLoopBackOff pods: what do you check first?

Start with the pod, not the cluster. `kubectl describe pod` shows events for scheduling failures, image pull errors, and probe failures. `kubectl logs deployment/web` shows application output after the container starts.

Pending usually means the scheduler cannot find CPU, memory, or storage. CrashLoopBackOff usually means the container exits shortly after starting, often because the command is wrong, the config is missing, or the readiness probe fails.

  • Pending: check resources, PVC, nodeSelector, and taints.
  • ImagePullBackOff: check image name, registry credentials, and tag.
  • CrashLoopBackOff: check logs, command, environment, and volume mounts.

So what if the pod is running but the app is not reachable?

Running pods are not the same as exposed pods. Use port-forward first because it does not require a Service or cloud load balancer.

Run `kubectl port-forward deployment/web 8080:80` and open `http://localhost:8080`. If that works, the Deployment and pod are fine; the missing piece is networking, not compute.

  • Test after every rollout with port-forward before debugging Service or Ingress.
  • A Service only becomes necessary when other pods or external clients need a stable access point.

Safe updates when you already have a working Deployment

Do not delete the Deployment to update it. Change the image or replicas in the YAML and run `kubectl apply` again, or use `kubectl set image deployment/web nginx=nginx:latest`.

Before changing images, save the last known-good YAML with `kubectl get deployment web -o yaml > web-good.yaml`. That file is your rollback reference if the new image fails.

  • Apply a small change first; avoid editing replicas, probes, and image in one step.
  • Use `kubectl rollout status deployment/web` after each change.

Rolling back a broken release without deleting the Deployment

Use `kubectl rollout undo deployment/web` to go back to the previous revision. Add `--to-revision=1` when you need a specific older revision from `kubectl rollout history deployment/web`.

Rollout undo is safer than deleting and reapplying because it reuses the existing ReplicaSet. Keep the Deployment name and labels unchanged during the rollback.

  • Check history before big rollbacks.
  • Undo one change at a time if multiple revisions were applied quickly.

When should you add a Service instead of port-forward?

Port-forward is for interactive debugging. A Service gives pods a stable DNS name and IP so other workloads can reach them.

Create a Service when an Ingress, another pod, or a production client needs to connect without a developer terminal. Use ClusterIP inside the cluster, NodePort for quick external tests, and LoadBalancer only when your provider supports it.

  • ClusterIP: internal stable access.
  • NodePort: external test access on a node port.
  • LoadBalancer: cloud-managed external IP.

Quick-reference summary table for the next failed rollout

Bookmark this section for the next failed rollout. The commands below assume the Deployment is named `web`.

| Symptom | First command | | --- | --- | | Pods Pending | `kubectl describe pod -l app=web` | | CrashLoopBackOff | `kubectl logs deployment/web` | | App not reachable | `kubectl port-forward deployment/web 8080:80` | | Bad rollout | `kubectl rollout undo deployment/web` |

Quick troubleshooting checklist

  • Verify kubectl can reach a cluster with `kubectl cluster-info`.
  • Confirm the container image exists locally or in a reachable registry.
  • Create a YAML file with kind Deployment, selector, replicas, and containerPort.
  • Apply the manifest with `kubectl apply -f deployment.yaml`.
  • Watch rollout with `kubectl rollout status deployment/web`.
  • Inspect failing pods with `kubectl describe pod` and `kubectl logs`.
  • Test the app with `kubectl port-forward deployment/web 8080:80`.
  • Save the last known-good YAML before changing images or replica counts.
  • Roll back with `kubectl rollout undo deployment/web` if health checks fail.

FAQ

Can I follow this kubernetes deployment tutorial on a local cluster?

Yes. Use kind, Minikube, or k3d with kubectl installed. The same `kubectl apply -f deployment.yaml` command works against any reachable cluster, including managed Kubernetes, as long as your kubeconfig points to the right context.

Why does my pod stay in Pending after I apply the manifest?

Pending usually means the scheduler cannot place the pod. Check `kubectl describe pod` for events about insufficient CPU, memory, persistent volume claims, or node selectors. Adding worker capacity or removing a restrictive node selector usually clears it; do not delete the Deployment itself.

How do I undo a bad deployment update?

Run `kubectl rollout undo deployment/web`. Add `--to-revision=1` to return to a specific revision shown by `kubectl rollout history deployment/web`. This reuses the previous ReplicaSet, which is safer than deleting and reapplying the YAML.