Cloud Architecture

Kubernetes for Developers: Deploy, Scale, and Manage

Master Kubernetes for developers. Learn to deploy, scale, and manage containerized applications with practical examples and architectural insights.

Khader Vali August 15, 2026 17 min read

Kubernetes for Developers: Deploying, Scaling, and Managing Containers

As senior software engineers, we’ve all been there: the excitement of a new feature, the careful crafting of code, and then the inevitable hurdle of getting it into production. In the world of modern application development, that hurdle often involves containers. And when you’re dealing with containers at scale, you quickly realize that you need a powerful orchestrator. Enter Kubernetes.

At Khadervali.com, we believe in empowering developers with the tools and knowledge to build robust, scalable systems. This deep dive into Kubernetes is designed specifically for developers—those of us who spend our days writing code, but also need to understand how our applications live and breathe in production environments. We’ll demystify Kubernetes, breaking down its core concepts, showing you how to deploy your applications, scale them effortlessly, and manage their lifecycle with confidence.

Forget the fear of complex infrastructure; Kubernetes, when understood from a developer’s perspective, becomes an incredibly powerful ally. It frees you from much of the operational burden, allowing you to focus on what you do best: building amazing software. Let’s embark on this journey to master Kubernetes, transforming it from a buzzword into an essential part of your developer toolkit.

Why Kubernetes? The Evolution of Application Deployment

Before we dive into the “how,” let’s briefly touch upon the “why.” Understanding the problem Kubernetes solves is crucial for appreciating its power.

From Monoliths to Microservices, and the Container Revolution

For decades, applications were often built as monolithic units. A single codebase, a single deployment artifact. While simpler to manage initially, monoliths often struggled with scalability, maintainability, and agility as they grew. This led to the rise of microservices—small, independent services communicating over well-defined APIs. Each microservice could be developed, deployed, and scaled independently.

However, microservices introduced a new challenge: managing numerous small services with their own dependencies and environments. This is where containers, particularly Docker, revolutionized deployment. Containers package an application and all its dependencies (libraries, configuration files, environment variables) into a single, isolated unit. This ensures consistency across different environments—from a developer’s laptop to staging and production.

Containers solved the “it works on my machine” problem, but created another: “How do I run thousands of containers across hundreds of servers efficiently? How do I ensure they’re healthy, restart them if they fail, scale them up during peak load, and update them without downtime?” This is the “container orchestration problem,” and Kubernetes is the leading solution.

The Orchestration Problem Solved

Imagine manually managing hundreds or thousands of containers:

  • Deployment: Placing containers on available servers.
  • Scaling: Adding or removing container instances based on demand.
  • Self-healing: Detecting and replacing failed containers.
  • Load Balancing: Distributing traffic evenly among healthy instances.
  • Configuration Management: Injecting environment variables or configuration files.
  • Service Discovery: Allowing containers to find and communicate with each other.
  • Rolling Updates: Deploying new versions without downtime.

Attempting this manually for any non-trivial application quickly becomes a nightmare. Kubernetes automates these tasks, providing a robust, extensible platform for running containerized workloads.

Kubernetes Core Concepts: Your Developer’s Glossary

To effectively work with Kubernetes, we need to understand its fundamental building blocks. Think of these as the vocabulary you’ll use to describe your application’s desired state to the cluster.

Cluster, Nodes, and Pods: The Hierarchy

Cluster:
The highest level of abstraction. A Kubernetes cluster is a set of machines (physical or virtual) that work together to run your containerized applications. It consists of at least one master node and one or more worker nodes.
Node:
A single machine within the cluster. Nodes are where your applications actually run. There are two types:
  • Master Node (Control Plane): Manages the cluster. It schedules applications, maintains the cluster’s desired state, and handles communication. Developers typically interact with the control plane via kubectl.
  • Worker Node: Runs the actual workloads (your containers). Each worker node has a container runtime (like Docker), a Kubelet (agent for communication with the master), and a Kube-proxy (for network proxying).
Pod:
The smallest, most fundamental deployable unit in Kubernetes. A Pod represents a single instance of a running process in your cluster. It encapsulates one or more containers (which are always co-located and co-scheduled), storage resources, a unique network IP, and options that govern how the container(s) should run.

A common pattern is to have one container per Pod. However, for “sidecar” patterns (e.g., a logging agent or a network proxy running alongside your main application container), multiple containers can share a Pod’s resources and network namespace.

Kubernetes for Developers: Deploy, Scale, and Manage
Generated Image

Deployments: Managing Your Application Instances

While Pods are the basic unit, you rarely create them directly. Instead, you use a higher-level abstraction called a Deployment. A Deployment is responsible for declaring the desired state of your application (e.g., “I want 3 replicas of my my-app container running”).

Key responsibilities of a Deployment:

  • Creating and updating ReplicaSets: A ReplicaSet ensures a specified number of Pod replicas are running at any given time. Deployments manage ReplicaSets, allowing for declarative updates.
  • Rolling Updates: Deployments orchestrate seamless updates to your application by gradually replacing old Pods with new ones, ensuring zero downtime.
  • Rollbacks: If a new deployment goes wrong, you can easily roll back to a previous stable version.

Deployments are ideal for stateless applications, like web servers or API services, where individual Pods are interchangeable.

Services: Exposing Your Application

Pods are ephemeral. They can be created, destroyed, and rescheduled with new IPs. How do other applications or external users consistently access your application if its IP keeps changing?

This is where a Service comes in. A Service is an abstract way to expose an application running on a set of Pods as a network service. Services provide a stable IP address and DNS name, acting as a load balancer that distributes traffic to the healthy Pods associated with it.

Common Service types:

  • ClusterIP: Exposes the Service on an internal IP in the cluster. Only reachable from within the cluster. Default type.
  • NodePort: Exposes the Service on each Node’s IP at a static port (the NodePort). Makes the service accessible from outside the cluster.
  • LoadBalancer: Exposes the Service externally using a cloud provider’s load balancer. This is the standard way to expose internet-facing services in cloud environments.
  • ExternalName: Maps the Service to a DNS name, not to a Pod.

ConfigMaps and Secrets: Configuration Management

Hardcoding configuration values directly into your container images is a bad practice. Kubernetes provides mechanisms to inject configuration data into your applications:

  • ConfigMaps: Used to store non-confidential configuration data as key-value pairs or entire configuration files. They can be mounted as files into Pods or injected as environment variables.
  • Secrets: Similar to ConfigMaps but designed for sensitive data like passwords, API keys, or certificates. Secrets are base64 encoded by default (not encrypted, so additional security measures like etcd encryption or external secret management are often used in production).

Volumes and Persistent Volume Claims (PVCs): Data Persistence

By default, containers and Pods are stateless. Any data written inside a container is lost when the Pod restarts or is deleted. For applications that need to store data persistently (e.g., databases, file storage), Kubernetes offers Volumes.

  • Volumes: A directory, possibly with some data in it, which is accessible to the containers in a Pod. The lifespan of a Volume is tied to the Pod.
  • PersistentVolume (PV): A piece of storage in the cluster that has been provisioned by an administrator or dynamically provisioned. It’s a resource in the cluster, independent of Pods.
  • PersistentVolumeClaim (PVC): A request for storage by a user. A PVC consumes PV resources. Developers typically create PVCs, requesting a certain size and access mode (e.g., ReadWriteOnce, ReadOnlyMany, ReadWriteMany). Kubernetes then binds the PVC to an available PV.

This abstraction allows developers to request storage without needing to know the underlying storage technology, providing a consistent interface across different storage backends (NFS, AWS EBS, GCE Persistent Disks, Azure Disk, etc.).

Namespaces: Logical Isolation

As your cluster grows and hosts multiple applications or teams, you need a way to organize and isolate resources. Namespaces provide a mechanism for doing this. They are virtual clusters within a physical cluster. Resources within a Namespace are isolated from resources in other Namespaces.

Common uses for Namespaces:

  • Separating environments (e.g., development, staging, production).
  • Separating teams or projects.
  • Preventing naming collisions.

When you create resources (Pods, Deployments, Services), they typically live within a specific Namespace. The default Namespace is default.

kubectl: Your Command-Line Interface

kubectl is the command-line tool for interacting with your Kubernetes cluster’s API server. It allows you to run commands against Kubernetes clusters, inspect cluster resources, and deploy applications. It’s your primary interface as a developer.

Kubernetes Architecture: A Developer’s Perspective

While you don’t need to be a Kubernetes administrator, a basic understanding of its architecture helps in debugging and understanding how your applications are managed.

Imagine the Kubernetes cluster as a highly automated factory:

  1. The Control Plane (The Factory Management)

    This is the “brain” of the cluster, often running on dedicated master nodes. It makes global decisions about the cluster, like scheduling workloads, detecting and responding to cluster events (e.g., starting a new Pod when a node fails), and storing the cluster’s state.

    • Kube-API Server: The front end of the Kubernetes control plane. All communication (from kubectl, other control plane components, or worker nodes) goes through the API server. It exposes the Kubernetes API.
    • etcd: A highly available, consistent, key-value store used as Kubernetes’ backing store for all cluster data. It stores the desired state and current state of the cluster. Think of it as the cluster’s single source of truth.
    • Kube-Scheduler: Watches for newly created Pods with no assigned node and selects a node for them to run on. It considers resource requirements, hardware/software/policy constraints, affinity/anti-affinity specifications, and more.
    • Kube-Controller-Manager: Runs various controllers that watch the shared state of the cluster through the API server and make changes attempting to move the current state towards the desired state. Examples include the Node Controller (notices when nodes go down), Replication Controller (maintains the correct number of Pods for a ReplicaSet), Endpoints Controller, and Service Account & Token Controllers.
  2. Worker Nodes (The Factory Floor)

    These are the machines where your actual applications (Pods) run. Each worker node has the necessary components to run and manage containers.

    • Kubelet: An agent that runs on each node in the cluster. It ensures that containers are running in a Pod. It takes instructions from the API server and manages Pods on its node.
    • Kube-proxy: A network proxy that runs on each node. It maintains network rules on nodes, allowing network communication to your Pods from inside or outside the cluster. It handles load balancing for Services.
    • Container Runtime (e.g., containerd, Docker): The software responsible for running containers. Kubelet uses the container runtime to pull images, start containers, and stop containers.

When you execute kubectl apply -f my-app.yaml, you’re telling the Kube-API Server your desired state. The API server updates etcd. The Scheduler notices a new Pod needs to be scheduled, finds a suitable worker node, and tells the Kubelet on that node to start the Pod. The Kubelet then instructs the Container Runtime to pull the image and run the container. The Kube-proxy ensures traffic can reach it.

Getting Started: Your First Kubernetes Deployment

Let’s get our hands dirty. We’ll deploy a simple Nginx web server, expose it, and scale it. For local development, tools like Minikube or Kind are excellent. For this example, assume you have a Kubernetes cluster running (e.g., Minikube, a cloud cluster, or Docker Desktop’s Kubernetes).

Prerequisites

  • Docker (or another container runtime) installed.
  • Minikube or Kind (for local development) or access to a cloud Kubernetes cluster.
  • kubectl installed and configured to connect to your cluster.

Let’s verify kubectl is connected:

kubectl cluster-info
kubectl get nodes

You should see information about your cluster and its nodes.

Step 1: Create a Docker Image (Optional, using Nginx for simplicity)

For this example, we’ll use the official Nginx image. If you had your own application, you would build a Docker image for it:

# Dockerfile for a simple Node.js app (example)
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

Then build and push to a registry:

docker build -t your-registry/your-app:v1.0.0 .
docker push your-registry/your-app:v1.0.0

Step 2: Define a Deployment

We’ll create a YAML file, nginx-deployment.yaml, to describe our desired Nginx deployment. This file tells Kubernetes:

  • We want a Deployment named nginx-deployment.
  • It should manage Pods with the app: nginx label.
  • We want 3 replicas (instances) of our application.
  • Each Pod should run a container named nginx using the nginx:latest Docker image.
  • The container listens on port 80.
# nginx-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3 # We want 3 instances of our application
  selector:
    matchLabels:
      app: nginx # Selects Pods with the label app: nginx
  template:
    metadata:
      labels:
        app: nginx # Label applied to Pods created by this Deployment
    spec:
      containers:
      - name: nginx # Name of the container
        image: nginx:latest # Docker image to use
        ports:
        - containerPort: 80 # Port the container listens on

Step 3: Apply the Deployment

Use kubectl to create the Deployment resource in your cluster:

kubectl apply -f nginx-deployment.yaml

You should see output like: deployment.apps/nginx-deployment created.

Now, let’s check the status of our deployment and pods:

kubectl get deployments
kubectl get pods -l app=nginx # Get pods with the label app: nginx

You should see 3 Pods in a Running state.

Step 4: Define a Service to Expose Nginx

Our Pods are running, but they’re not accessible from outside the cluster yet. Let’s create a Service to expose our Nginx deployment. We’ll use a NodePort type for local testing or a LoadBalancer type for cloud deployments.

Create nginx-service.yaml:

# nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  selector:
    app: nginx # Selects Pods with the label app: nginx
  ports:
    - protocol: TCP
      port: 80 # Service's port
      targetPort: 80 # Pod's port
  type: NodePort # Or LoadBalancer for cloud providers

Step 5: Apply the Service

kubectl apply -f nginx-service.yaml

Verify the Service:

kubectl get services

You’ll see nginx-service with a CLUSTER-IP, and for NodePort, a PORT(S) entry like 80:3XXXX/TCP. The 3XXXX is the NodePort.

To access Nginx:

  • If using Minikube: minikube service nginx-service will open it in your browser.
  • If using a cloud provider with LoadBalancer: kubectl get services will show an external IP under EXTERNAL-IP.
  • If using NodePort: Find your cluster node’s IP address and access it via http://<NODE_IP>:<NODE_PORT>.

You should see the “Welcome to Nginx!” page.

Step 6: Scaling Your Application

One of Kubernetes’ most powerful features is scaling. Let’s scale our Nginx deployment to 5 replicas:

kubectl scale deployment/nginx-deployment --replicas=5

Check the pods again:

kubectl get pods -l app=nginx

You’ll see 5 Pods running. Kubernetes automatically created and scheduled the new Pods.

To scale down:

kubectl scale deployment/nginx-deployment --replicas=1

Step 7: Cleaning Up

When you’re done, delete the resources:

kubectl delete -f nginx-service.yaml
kubectl delete -f nginx-deployment.yaml

Or delete by name:

kubectl delete service nginx-service
kubectl delete deployment nginx-deployment

Beyond the Basics: Advanced Developer Features

The previous section covered the fundamentals. Now, let’s explore some more advanced features that developers frequently leverage to build resilient, configurable, and observable applications.

ConfigMaps and Secrets in Action

Let’s say your application needs a specific environment variable or a configuration file. We’ll use a ConfigMap to provide it.

# my-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_MESSAGE: "Hello from Kubernetes!"
  APP_COLOR: "blue"
  config.properties: |
    server.port=8080
    database.url=jdbc:mysql://db-service:3306/mydb
kubectl apply -f my-configmap.yaml

Now, let’s modify our deployment to use this ConfigMap. We can inject APP_MESSAGE and APP_COLOR as environment variables and mount config.properties as a file.

# my-app-deployment.yaml (snippet focusing on config usage)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-deployment
spec:
  # ... (selector, replicas)
  template:
    # ... (metadata.labels)
    spec:
      containers:
      - name: my-app-container
        image: your-registry/your-app:v1.0.0 # Your actual app image
        ports:
        - containerPort: 8080
        env: # Inject as environment variables
        - name: MESSAGE
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: APP_MESSAGE
        - name: COLOR
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: APP_COLOR
        volumeMounts: # Mount config.properties as a file
        - name: config-volume
          mountPath: /etc/config # Path inside the container
      volumes:
      - name: config-volume
        configMap:
          name: app-config
          items:
          - key: config.properties
            path: config.properties # The file name inside mountPath

Your application can now read MESSAGE and COLOR from its environment and access /etc/config/config.properties.

Secrets work similarly, but they are base64 encoded. You create them like this:

# Create from literal values
kubectl create secret generic db-credentials --from-literal=username=admin --from-literal=password=supersecret

# Create from files
echo -n 'admin' > ./username.txt
echo -n 'supersecret' > ./password.txt
kubectl create secret generic db-credentials --from-file=./username.txt --from-file=./password.txt

Then you can mount them or inject them as environment variables in your Deployment YAML, just like ConfigMaps.

Liveness and Readiness Probes: Ensuring Application Health

Kubernetes knows if a container has crashed, but what if your application is running but stuck in a loop, out of memory, or otherwise unhealthy? Probes tell Kubernetes about your application’s health.

  • Liveness Probe: Tells Kubernetes when to restart a container. If the liveness probe fails, Kubernetes kills the container, and the container is subject to its restart policy.
  • Readiness Probe: Tells Kubernetes when a container is ready to start serving traffic. If the readiness probe fails, the Pod’s IP address is removed from the Endpoints of all Services, and traffic is no longer routed to it. Once it passes, it’s re-added.

Common probe types: HTTP GET, TCP Socket, Exec command.

# Deployment snippet with probes
spec:
  template:
    spec:
      containers:
      - name: my-app-container
        image: your-app:latest
        ports:
        - containerPort: 8080
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 15 # Wait 15s before first check
          periodSeconds: 20    # Check every 20s
          timeoutSeconds: 5    # Max 5s for response
          failureThreshold: 3  # After 3 failures, restart container
        readinessProbe:
          httpGet:
            path: /ready
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
          timeoutSeconds: 3
          failureThreshold: 2

Rolling Updates and Rollbacks: Zero-Downtime Deployments

One of the biggest benefits of Deployments is their ability to perform rolling updates, gradually replacing Pods of the old version with Pods of the new version, ensuring continuous availability. If something goes wrong, you can easily roll back.

To update your image (e.g., from v1.0.0 to v1.1.0):

kubectl set image deployment/my-app-deployment my-app-container=your-registry/your-app:v1.1.0

Kubernetes will gradually terminate old Pods and spin up new ones. You can watch the rollout status:

kubectl rollout status deployment/my-app-deployment

If you discover an issue, you can roll back to the previous version:

kubectl rollout undo deployment/my-app-deployment

This is invaluable for maintaining application stability and agility.

Horizontal Pod Autoscaler (HPA): Automatic Scaling

Manually scaling your application is fine for planned events, but what about unexpected traffic spikes? The Horizontal Pod Autoscaler (HPA) automatically scales the number of Pod replicas in a Deployment or ReplicaSet based on observed CPU utilization or other select metrics.

To enable HPA, you need to define resource requests for your containers:

# Deployment snippet with resource requests
spec:
  template:
    spec:
      containers:
      - name: my-app-container
        image: your-app:latest
        resources:
          requests:
            cpu: "100m" # 100 millicores (0.1 CPU core)
            memory: "128Mi" # 128 mebibytes

Then, create an HPA resource:

kubectl autoscale deployment my-app-deployment --cpu-percent=50 --min=2 --max=10

This command creates an HPA that targets my-app-deployment. It will try to maintain an average CPU utilization of 50% across its Pods, with a minimum of 2 and a maximum of 10 replicas.

kubectl get hpa

This will show you the current status of your autoscaler.

Ingress: Managing External Access

For exposing multiple services under a single external IP address and providing advanced routing capabilities (like path-based or host-based routing, SSL termination), you use an Ingress.

An Ingress is a collection of rules that allow inbound connections to reach cluster services. It requires an Ingress Controller (like Nginx Ingress Controller, Traefik, GKE Ingress) to be running in your cluster.

# my-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:

Written by

Khader Vali

Senior Software Engineer specializing in cloud architecture, real-time systems, and enterprise-scale applications.

Share this article

Related Articles

Micro-Frontends with Webpack Module Federation

Oct 06, 2024 · 2 min read

Building Centralized Component Libraries in Monorepos

Oct 18, 2024 · 2 min read

Kubernetes for Developers: Deploy, Scale, Manage Containers

Aug 11, 2026 · 17 min read