Deploying C# on Kubernetes: A Practical Guide for .NET Developers
\n\nModern development in C# (.NET) increasingly requires not just writing quality code, but also the ability to properly deploy an application in a production environment. Kubernetes (K8s) has become the de facto standard for container orchestration. In this article, we will walk through the step-by-step process of deploying a C# application on Kubernetes — from containerization to configuring Ingress.
\n\n1. Preparing the .NET Application and Docker Image
\n\nBefore getting into Kubernetes, your C# application must be packaged into a Docker image. Let's consider a simple ASP.NET Core Web API.
\n\nCreate the project:
\ndotnet new webapi -n MyK8sApp\ncd MyK8sApp\n\nAdd a simple endpoint (file Program.cs):
var builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\napp.MapGet("/", () => "Hello from C# on Kubernetes!");\n\napp.Run();\n\nNow let's create a Dockerfile in the project root:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base\nWORKDIR /app\nEXPOSE 8080\n\nFROM mcr.microsoft.com/dotnet/sdk:8.0 AS build\nWORKDIR /src\nCOPY ["MyK8sApp.csproj", "."]\nRUN dotnet restore\nCOPY . .\nRUN dotnet publish -c Release -o /app/publish\n\nFROM base AS final\nWORKDIR /app\nCOPY --from=build /app/publish .\nENTRYPOINT ["dotnet", "MyK8sApp.dll"]\n\nBuild and push the image to Docker Hub or a local registry:
\ndocker build -t yourdockerhub/my-k8s-app:v1 .\ndocker push yourdockerhub/my-k8s-app:v1\n\nImportant: In ASP.NET Core 8+, the application listens on port 8080 by default, which is ideal for Kubernetes.
\n\n2. Basic YAML Manifests for Kubernetes
\n\nKubernetes manages resources through YAML files. To run a C# application, we will need at least a Deployment and a Service.
\n\n2.1. Deployment (deployment.yaml)
\nThe Deployment is responsible for maintaining the desired number of Pod replicas.
\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: my-k8s-app-deployment\nspec:\n replicas: 3\n selector:\n matchLabels:\n app: my-k8s-app\n template:\n metadata:\n labels:\n app: my-k8s-app\n spec:\n containers:\n - name: my-k8s-app\n image: yourdockerhub/my-k8s-app:v1\n ports:\n - containerPort: 8080\n resources:\n requests:\n memory: "64Mi"\n cpu: "250m"\n limits:\n memory: "128Mi"\n cpu: "500m"\n livenessProbe:\n httpGet:\n path: /health\n port: 8080\n initialDelaySeconds: 5\n periodSeconds: 10\n\nKey points:
\n- \n
replicas: 3— 3 instances of the application \n livenessProbe— health check (it is recommended to add a/healthendpoint in C#) \n requests/limitsresources help the K8s scheduler \n
2.2. Service (service.yaml)
\nThe Service provides a stable entry point to the Pods.
\napiVersion: v1\nkind: Service\nmetadata:\n name: my-k8s-app-service\nspec:\n selector:\n app: my-k8s-app\n ports:\n - protocol: TCP\n port: 80\n targetPort: 8080\n type: ClusterIP\n\nClusterIP is an internal service, accessible only within the cluster. For external access, we use Ingress.
3. Configuring Ingress and ConfigMap
\n\n3.1. Ingress (ingress.yaml)
\nIngress provides external access to the service via a domain name.
\napiVersion: networking.k8s.io/v1\nkind: Ingress\nmetadata:\n name: my-k8s-app-ingress\n annotations:\n nginx.ingress.kubernetes.io/rewrite-target: /\nspec:\n rules:\n - host: myapp.example.com\n http:\n paths:\n - path: /\n pathType: Prefix\n backend:\n ser