Skip to content

Docker & Kubernetes Fundamentals for .NET Developers

Docker & Kubernetes Fundamentals for .NET Developers

Section titled “Docker & Kubernetes Fundamentals for .NET Developers”

If you’re building .NET APIs and deploying to the cloud, containers aren’t optional anymore. This guide covers the Docker and Kubernetes concepts you’ll use every week — no fluff, just the bits that matter.

  • Consistency: “Works on my machine” is dead. The container is the machine.
  • Isolation: Each service gets its own dependencies, runtime, and config.
  • Speed: Containers start in seconds, not minutes. CI/CD loves them.
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
COPY *.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /app
# Runtime stage
FROM mcr.microsoft.com/dotnet/aspnet:9.0
WORKDIR /app
COPY --from=build /app .
EXPOSE 8080
ENTRYPOINT ["dotnet", "MyApi.dll"]

Why multi-stage? The SDK image is ~900MB. The runtime image is ~220MB. Your final image should only carry what it needs to run.

Terminal window
docker build -t myapi:1.0 .
docker run -d -p 8080:8080 --name myapi myapi:1.0
docker logs myapi
docker exec -it myapi /bin/sh
docker compose up -d # multi-container setups
services:
api:
build: .
ports:
- "8080:8080"
environment:
- ConnectionStrings__Default=Server=db;Database=mydb;User=sa;Password=YourStrong!Passw0rd
depends_on:
- db
db:
image: mcr.microsoft.com/mssql/server:2022-latest
environment:
- ACCEPT_EULA=Y
- SA_PASSWORD=YourStrong!Passw0rd
ports:
- "1433:1433"

Docker vs Kubernetes — What Does Each Actually Do?

Section titled “Docker vs Kubernetes — What Does Each Actually Do?”

A common confusion: Docker and Kubernetes solve different problems and are typically used together.

ConcernDockerKubernetes
What it doesBuilds and runs containersOrchestrates containers across nodes
ScopeSingle hostCluster of hosts
ScalingManualAutomatic (HPA, replica sets)
Self-healing❌ No✅ Restarts failed pods
Service discoveryManual / Compose networksBuilt-in DNS (svc.cluster.local)
Load balancingManualBuilt-in (Services)
Best forLocal dev, simple deploysProduction, multi-service workloads
ConceptWhat It Does
PodSmallest deployable unit — one or more containers sharing network/storage
DeploymentManages pod replicas, rolling updates, rollbacks
ServiceStable network endpoint for a set of pods (ClusterIP, LoadBalancer, NodePort)
ConfigMapExternalised configuration (non-sensitive)
SecretExternalised sensitive data (base64 encoded, not encrypted by default)
IngressHTTP routing rules — host/path-based routing to services
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapi
spec:
replicas: 3
selector:
matchLabels:
app: myapi
template:
metadata:
labels:
app: myapi
spec:
containers:
- name: myapi
image: myregistry.azurecr.io/myapi:1.0
ports:
- containerPort: 8080
resources:
requests:
memory: "128Mi"
cpu: "250m"
limits:
memory: "256Mi"
cpu: "500m"
---
apiVersion: v1
kind: Service
metadata:
name: myapi-svc
spec:
selector:
app: myapi
ports:
- port: 80
targetPort: 8080
type: ClusterIP
Terminal window
kubectl get pods
kubectl describe pod <name>
kubectl logs <pod-name> -f
kubectl apply -f deployment.yaml
kubectl rollout status deployment/myapi
kubectl rollout undo deployment/myapi # instant rollback
kubectl port-forward svc/myapi-svc 8080:80

Always define liveness and readiness probes. Without them, Kubernetes can’t tell if your app is healthy:

livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 15
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 10

Docker:

  • Running as root — containers run as root by default. Add a USER instruction to your Dockerfile to drop privileges
  • Forgetting .dockerignore — without it, your entire bin/, obj/, and .git/ folder is sent as build context, bloating build time significantly
  • latest tag in productionlatest is mutable and unpredictable. Always tag images with a specific version or Git SHA

Kubernetes:

  • Secrets are not encrypted — base64 encoding is not encryption. Use Azure Key Vault with the CSI Secrets Store driver for production credentials
  • No resource limits — one unconstrained pod can starve all others on the same node. Always set resources.requests and resources.limits
  • ImagePullPolicy: IfNotPresent — Kubernetes won’t pull a newer image if a local copy exists. Use Always in CI/CD or pin to specific image versions
  • StatefulSets for stateful apps — if your pod needs persistent identity or ordered deployment (databases, queues), use StatefulSets, not Deployments
  • Use .dockerignore — Exclude bin/, obj/, .vs/, and node_modules/ to keep build context small.
  • Health check endpoints — Use Microsoft.Extensions.Diagnostics.HealthChecks and map /healthz and /ready.
  • Configuration — Kubernetes ConfigMaps and Secrets map naturally to .NET’s IConfiguration via environment variables.
  • Graceful shutdown — Handle IHostApplicationLifetime.ApplicationStopping so in-flight requests complete during pod termination.

These fundamentals cover ~80% of what you need for day-to-day container work. For production hardening — network policies, RBAC, service mesh — that’s a deeper dive.