Kubernetes Deployment: From Zero to Your First Live Application
If you have ever run your application on a virtual server or VPS, you know how exhausting managing scale, updates, and errors can be. Kubernetes changes this equation: you describe what your application should look like, and Kubernetes takes responsibility for bringing the system to that state and keeping it there. But the starting point is unclear for many developers: where exactly should you begin? In this article, we outline a practical, no-frills path for the Kubernetes deployment of your first application — from creating a Deployment to exposing the service outside the cluster.
We assume you have a Kubernetes cluster available (locally with Minikube, or a cloud cluster) and that the kubectl tool is installed on your system and connected to the cluster. If you don't have a cluster, minikube start is the fastest way to begin. All examples in this article use a simple Nginx application so you can stay focused on the core concepts.
Step One: Creating a Deployment — The Heart of Kubernetes Deployment
In Kubernetes, you don't run containers directly; instead, you define a Deployment that describes the desired state of your application. The Deployment automatically creates a ReplicaSet that keeps a specified number of Pods alive. If a Pod dies, the ReplicaSet immediately replaces it with a new one.
Create a file named deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
Key points in this file:
- replicas: 3 — meaning three copies of the application should always be running.
- selector.matchLabels — determines which Pods the Deployment manages. This label must match the Pod template label.
- resources — setting minimum and maximum resources helps Kubernetes distribute Pods across nodes more intelligently. The value
100mmeans 0.1 CPU core.
To apply this file to the cluster:
kubectl apply -f deployment.yaml
Check the status:
kubectl get deployments
kubectl get pods
After a few seconds, you should see three Pods with the Running status. If you deliberately delete one of the Pods (kubectl delete pod <pod-name>), Kubernetes will immediately create a new Pod — this is the power of self-healing.
Common Mistake: Forgetting the Selector
One of the most common errors in Kubernetes deployment is a mismatch between selector.matchLabels and the Pod template labels. If these don't match, the Deployment is created but doesn't manage any Pods. Always check errors with kubectl describe deployment nginx-deployment.
Step Two: Creating a Service — The Gateway to Pods
Pods in Kubernetes are ephemeral, and their IPs change each time they are created. To give your application a stable address, you need a Service. A Service is an abstraction layer that distributes traffic among a set of Pods.
Create the service.yaml file:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
In this definition:
- selector — specifies which Pods the Service should send traffic to (here, all Pods with the
app: nginxlabel). - port — the port on which the Service is available.
- targetPort — the port the container actually listens on.
- type: ClusterIP — meaning the Service is only accessible within the cluster. This is the default and most secure option.
Apply it:
kubectl apply -f service.yaml
kubectl get services
Now, from within the cluster, you can access the application at the address nginx-service. For a quick test, use a temporary Pod:
kubectl run test-pod --image=busybox --rm -it -- wget -qO- http://nginx-service
If you receive an HTML response from Nginx, the Service is working correctly.
Common Mistake: Incorrect targetPort
If your application listens on port 8080 but you set targetPort to 80, the Service will be created but you won't get any response. Always set targetPort to the actual container port, not a preferred one.
Step Three: Exposing the Application Outside the Cluster
So far, the application is only accessible within the cluster. To allow external users to connect, you have three main options:
Option 1: NodePort — Quick for Testing
By changing the Service type to NodePort, Kubernetes opens a port in the range 30000–32767 on every node and routes traffic on that port to the Service.
apiVersion: v1
kind: Service
metadata:
name: nginx-service-nodeport
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
nodePort: 30080
type: NodePort
After applying, it is accessible externally at http://<node-IP>:30080. This method is great for quick testing but not suitable for production, as ports are limited and hard to manage.
Option 2: LoadBalancer — Suitable for the Cloud
In cloud clusters (such as GKE, EKS, or AKS), changing the type to LoadBalancer causes the cloud provider to create a real Load Balancer with a public IP:
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
After a few minutes, kubectl get svc will show an EXTERNAL-IP that you can access directly from your browser. This method is suitable for simple public services.
Option 3: Ingress — Intelligent Routing
If you have multiple services and want to route traffic based on domain name or path, Ingress is the best choice. Ingress is a cluster-level entry layer that manages HTTP routing rules. First, you need to install an Ingress Controller (such as the NGINX Ingress Controller or Traefik). Then define an Ingress resource:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nginx-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
With this definition, all incoming traffic to app.example.com is routed to the nginx-service. Ingress also handles SSL/TLS management by defining tls in the spec.
Common Mistake: Forgetting the Ingress Controller
Many beginners define the Ingress resource but forget to install the Ingress Controller. Without a Controller, the Ingress resource has no effect. Before defining Ingress, make sure to install and run the Controller.
Step Four: Updating and Rolling Back
One of the main advantages of Kubernetes deployment is rolling updates without downtime. To update the Nginx version, simply change the image:
kubectl set image deployment/nginx-deployment nginx=nginx:1.28
Kubernetes gradually replaces old Pods with new ones. If something goes wrong, you can roll back to the previous version with the following command:
kubectl rollout undo deployment/nginx-deployment
You can also view the version history:
kubectl rollout history deployment/nginx-deployment
Summary and Next Steps
In this article, we walked through the complete cycle of Kubernetes deployment for a simple application: creating a Deployment to manage Pods, defining a Service for stable access, and exposing it externally using three methods — NodePort, LoadBalancer, and Ingress. This is the core foundation; from here, you can move on to more advanced topics such as ConfigMap and Secret for configuration management, PersistentVolume for durable data, and HorizontalPodAutoscaler for automatic scaling.
If you're looking for infrastructure where running Kubernetes is smooth and worry-free, ServerNet's cloud services can provide a suitable platform for hosting your cluster. But the most important step is hands-on practice: build a test cluster right now and run your first Deployment. After a few repetitions, this process will become second nature to you.