Waqas Ahmad — Software Architect & Technical Consultant - Available USA, Europe, Global

Waqas Ahmad — Software Architect & Technical Consultant

Specializing in

Distributed Systems

.NET ArchitectureCloud-Native ArchitectureAzure Cloud EngineeringAPI ArchitectureMicroservices ArchitectureEvent-Driven ArchitectureDatabase Design & Optimization

👋 Hi, I'm Waqas — a Software Architect and Technical Consultant specializing in .NET, Azure, microservices, and API-first system design..
I help companies build reliable, maintainable, and high-performance backend platforms that scale.

Experienced across engineering ecosystems shaped by Microsoft, the Cloud Native Computing Foundation, and the Apache Software Foundation.

Available for remote consulting (USA, Europe, Global) — flexible across EST, PST, GMT & CET.

services
Article

Kubernetes Basics for .NET Developers

Pods, Deployments, Services. Running .NET on AKS and local Kubernetes.

services
Read the article

Introduction

This guidance is relevant when the topic of this article applies to your system or design choices; it breaks down when constraints or context differ. I’ve applied it in real projects and refined the takeaways over time (as of 2026).

Running .NET apps in production at scale usually means containers and an orchestrator—and Kubernetes is the dominant choice (AKS, EKS, GKE, or on-prem). This article is for .NET developers new to Kubernetes: we cover Pods, Deployments, Services, ConfigMaps and Secrets, health checks and resource limits, and how to run .NET on Kubernetes locally and on AKS. For architects and tech leads, understanding these basics matters when you move from “run on a VM” to orchestrated containers and need scheduling, scaling, and self-healing.

For a deeper overview of this topic, explore the full Cloud-Native Architecture guide.

Decision Context

  • System scale: Containerised apps (e.g. .NET) from a few pods to many; single cluster or multi-environment. Applies when you’re moving from “run on a VM” to orchestrated containers.
  • Team size: Dev and ops (or platform); someone must own manifests, secrets, and cluster health. Works when the team can use kubectl and maintain YAML (or Helm).
  • Time / budget pressure: Fits when you need scaling, self-healing, or portability; breaks down when the team has no container or Kubernetes experience—then start with managed Kubernetes (e.g. AKS) and simple deployments.
  • Technical constraints: .NET in Docker; Kubernetes (or AKS, EKS, GKE); ConfigMaps, Secrets, health checks. Assumes you can build container images and deploy to a cluster.
  • Non-goals: This article does not optimise for non-container workloads or for deep cluster operations; it focuses on .NET developers getting apps onto Kubernetes.

What is Kubernetes and why use it?

Kubernetes is a container orchestrator. It takes your containerised applications (Docker images) and runs them across a cluster of machines, handling:

Capability What it does
Scheduling Decides which node runs which container
Scaling Runs multiple replicas; scales up/down based on load
Self-healing Restarts crashed containers; replaces unhealthy nodes
Load balancing Distributes traffic across replicas
Rolling updates Deploys new versions without downtime
Service discovery Containers find each other by name, not IP
Config management Injects config and secrets into containers

Why .NET developers should care: Your .NET API runs in a Docker container. Kubernetes runs that container, ensures it stays running, scales it when traffic increases, and routes requests to it.

Pods, Deployments, and Services

Resource Purpose Analogy
Pod Runs one or more containers A single instance of your app
Deployment Manages a set of Pods The “desired state” manager
Service Exposes Pods to the network A stable address for your app

Pod: Smallest deployable unit; one or more containers sharing network and storage. For .NET apps, typically one container per Pod.

Deployment: Manages replicas of Pods; handles rolling updates and rollback. You define the desired state (3 replicas); Kubernetes maintains it.

Service: Stable network identity for Pods (ClusterIP, LoadBalancer, NodePort). Pods are ephemeral; Services provide a stable address.

# Deployment example
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-api
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-api
  template:
    metadata:
      labels:
        app: my-api
    spec:
      containers:
      - name: api
        image: myregistry.azurecr.io/my-api:latest
        ports:
        - containerPort: 8080

ConfigMaps and Secrets

ConfigMaps store non-sensitive configuration (app settings, feature flags). Secrets store sensitive data (connection strings, API keys). Both can be mounted as environment variables or files.

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: my-api-config
data:
  ASPNETCORE_ENVIRONMENT: "Production"
  Logging__LogLevel__Default: "Information"
# secret.yaml (values are base64-encoded)
apiVersion: v1
kind: Secret
metadata:
  name: my-api-secrets
type: Opaque
data:
  ConnectionStrings__Default: U2VydmVyPW15c2VydmVyO0RhdGFiYXNlPW15ZGI=

Using ConfigMap and Secret in a Deployment

spec:
  containers:
  - name: api
    image: myregistry.azurecr.io/my-api:v1.0.0
    envFrom:
    - configMapRef:
        name: my-api-config
    - secretRef:
        name: my-api-secrets

Important: Base64 is not encryption. In production, use Azure Key Vault with the CSI Secrets Store Driver or Managed Identity.

Running .NET on Kubernetes

To run a .NET app on Kubernetes:

  1. Containerise your app (Dockerfile)
  2. Build and push to a registry (ACR, Docker Hub)
  3. Create Kubernetes manifests (Deployment, Service)
  4. Apply with kubectl apply -f

Dockerfile for ASP.NET Core

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyApi/MyApi.csproj", "MyApi/"]
RUN dotnet restore "MyApi/MyApi.csproj"
COPY . .
RUN dotnet publish -c Release -o /app/publish

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /app/publish .
EXPOSE 8080
ENV ASPNETCORE_URLS=http://+:8080
ENTRYPOINT ["dotnet", "MyApi.dll"]

Build and push

docker build -t myregistry.azurecr.io/my-api:v1.0.0 .
az acr login --name myregistry
docker push myregistry.azurecr.io/my-api:v1.0.0

Health checks and resource limits

Health probes

Probe Purpose If it fails
Liveness Is the container alive? Kubernetes restarts it
Readiness Is it ready for traffic? Kubernetes stops sending traffic
Startup Has it started? Delays liveness/readiness
livenessProbe:
  httpGet:
    path: /health/live
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 10

readinessProbe:
  httpGet:
    path: /health/ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5

Resource requests and limits

resources:
  requests:
    memory: "128Mi"
    cpu: "100m"
  limits:
    memory: "512Mi"
    cpu: "1000m"

Running Kubernetes locally

Tool Description
Docker Desktop Built-in Kubernetes (enable in settings)
minikube Runs a single-node cluster in a VM
kind Kubernetes IN Docker (fast startup)
k3d Lightweight k3s in Docker

Deploying to AKS

# Create AKS cluster
az aks create --resource-group my-rg --name my-aks --node-count 3

# Get credentials
az aks get-credentials --resource-group my-rg --name my-aks

# Attach ACR
az aks update --resource-group my-rg --name my-aks --attach-acr myregistry

# Deploy
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml

Enterprise best practices

1. Use namespaces to isolate workloads (dev, staging, prod).

2. Always set resource requests and limits so the scheduler can make good decisions.

3. Use health probes (liveness, readiness, startup) so Kubernetes can detect unhealthy Pods.

4. Store secrets in Azure Key Vault, not Kubernetes Secrets (base64 is not encryption).

5. Use Managed Identity for Azure access instead of storing credentials.

6. Enable autoscaling (HPA for Pods, Cluster Autoscaler for nodes).

7. Use Ingress for HTTP routing instead of multiple LoadBalancers.

8. Monitor with Prometheus/Grafana or Azure Monitor.

Common issues and troubleshooting

Issue Symptoms Fix
ImagePullBackOff Pod stuck pulling image Check image name/tag; verify registry auth
CrashLoopBackOff Pod keeps restarting Check logs (kubectl logs); app is crashing
Pending Pod Pod stuck in Pending Not enough resources; check node capacity
Readiness failing Pod not receiving traffic Check health endpoint; verify dependencies
OOMKilled Container killed Increase memory limit; check for leaks

Debugging commands

kubectl get pods
kubectl describe pod <pod-name>
kubectl logs <pod-name>
kubectl exec -it <pod-name> -- /bin/sh
kubectl get events --sort-by=.metadata.creationTimestamp

You can also explore more patterns in the Cloud-Native Architecture resource page.

Summary

Kubernetes runs containerised apps with scheduling, scaling, and self-healing; .NET apps run as Pods, Deployments manage replicas and rolling updates, Services expose them with a stable network name. Skipping health checks or resource limits makes the cluster unable to manage your app well; using ConfigMaps and Secrets (and not plain env vars for secrets) keeps config and security under control. Next, containerise your .NET app, write a Deployment and Service, set liveness and readiness probes and resource requests/limits, then deploy to AKS (or your cluster) and use kubectl to inspect and debug.

Position & Rationale

I use Kubernetes (or AKS) when we need orchestration: multiple replicas, rolling updates, self-healing, and a single place to define config and secrets. I prefer managed Kubernetes (AKS, EKS, GKE) over self-managed so we don’t run the control plane. For .NET I run one process per container and one main container per Pod unless we have a clear sidecar need. I set resource requests and limits and liveness/readiness probes so the scheduler and load balancer can make good decisions. I avoid running stateful or persistent data in Pods without a proper storage abstraction (PVC, StatefulSet) and I don’t put secrets in environment variables in plain text—use Kubernetes Secrets or a vault integration.

Trade-Offs & Failure Modes

Kubernetes adds YAML and operational concepts (Pods, Deployments, Services); you gain portability and orchestration. Managed Kubernetes reduces control-plane burden but ties you to a cloud provider. Failure modes: Pods without resource limits (one hungry pod can starve others); missing or wrong readiness probes (traffic sent to pods that aren’t ready); secrets in env as plain text; ImagePullBackOff or CrashLoopBackOff without a runbook.

What Most Guides Miss

Most guides show Pod/Deployment/Service but don’t stress readiness vs liveness—readiness controls when the Pod gets traffic; liveness controls when it gets restarted. Getting them wrong leads to restarts or traffic to broken pods. Another gap: resource requests and limits—if you don’t set them, the scheduler can overcommit and nodes run out of memory. Debugging (kubectl logs, describe, events) is often mentioned but not as a first resort when “it doesn’t work.”

Decision Framework

  • If you’re deploying a .NET app → Containerise it; create a Deployment (replicas, image, env) and a Service (ClusterIP or LoadBalancer).
  • For config → ConfigMaps for non-sensitive; Secrets for sensitive (or integrate with Key Vault).
  • For health → Set liveness and readiness probes so Kubernetes can restart and route traffic correctly.
  • For resources → Set requests and limits (CPU, memory) so the scheduler and node don’t overcommit.
  • When things fail → Use kubectl get/describe/logs/events to see why Pods are Pending, CrashLoopBackOff, or not receiving traffic.

Key Takeaways

  • Pods run containers; Deployments manage replica count and rolling updates; Services give a stable network name.
  • Use ConfigMaps and Secrets for config; set liveness and readiness and resource limits.
  • For .NET: one container per Pod unless you have a sidecar; use AKS (or similar) for managed Kubernetes.
  • Debug with kubectl get pods, describe pod, logs, and events when Pods don’t start or receive traffic.

When I Would Use This Again — and When I Wouldn’t

I’d use Kubernetes (AKS) again for any production .NET workload that needs scaling, rolling updates, and a standard place to run containers. I’d use the same pattern: Deployment + Service, ConfigMaps/Secrets, health checks, and resource limits. I wouldn’t run stateful data in Pods without a proper storage and backup story. I also wouldn’t adopt Kubernetes for a single small app with no scaling or portability need—simpler hosting (App Service, single VM) can be enough.

For production-grade Azure systems, I offer consulting on cloud architecture, scalability, and cloud-native platform design.

services
Frequently Asked Questions

Frequently Asked Questions

What is a Pod?

A Pod is the smallest deployable unit in Kubernetes; it runs one or more containers that share network and storage. Usually one container per Pod for .NET apps.

What is a Deployment?

A Deployment manages a set of Pod replicas: desired count, rolling updates, rollback. You define a template (container image, env, ports); Kubernetes creates Pods to match.

What is a Kubernetes Service?

A Service gives Pods a stable network name and IP; other Pods or external clients reach them via the Service. Types: ClusterIP (internal), LoadBalancer (external), NodePort.

What is the difference between ClusterIP and LoadBalancer?

ClusterIP is internal-only (accessible within the cluster). LoadBalancer creates a cloud load balancer with an external IP. Use ClusterIP for internal APIs; LoadBalancer for external APIs.

How do I configure my .NET app in Kubernetes?

Use ConfigMaps for non-sensitive config and Secrets for sensitive data. Reference them with envFrom or mount as files. For production, use Azure Key Vault with the CSI driver.

What are liveness and readiness probes?

Liveness probe: Is the container alive? If it fails, Kubernetes restarts the container. Readiness probe: Is the container ready for traffic? If it fails, Kubernetes stops sending traffic.

How do I scale my app in Kubernetes?

Manually: kubectl scale deployment my-api --replicas=5. Automatically: Use Horizontal Pod Autoscaler (HPA) to scale based on CPU.

What is AKS?

Azure Kubernetes Service (AKS) is Azure’s managed Kubernetes. Azure manages the control plane; you manage worker nodes.

Why is my Pod stuck in ImagePullBackOff?

The image cannot be pulled. Check image name/tag; verify registry auth with imagePullSecrets or ACR integration.

Why is my Pod in CrashLoopBackOff?

The container is crashing on startup. Check logs with kubectl logs <pod-name>.

What is the difference between requests and limits?

Requests are guaranteed resources. Limits are maximum resources. If memory limit is exceeded, the container is OOMKilled.

How do I run Kubernetes locally?

Use Docker Desktop, minikube, kind, or k3d. Docker Desktop is easiest for Windows/Mac.

What is an Ingress?

An Ingress routes HTTP traffic to Services based on host/path. Use one Ingress controller instead of multiple LoadBalancers.

services
Related Guides & Resources

services
Related services