This episode covers deploying .NET applications: Dockerizing with a multi-stage build, deployment to Azure App Service, AWS, GCP, and Kubernetes, runtime configuration for production, and blue-green, canary release, and rollback strategies.

Code running on a developer's machine guarantees nothing until it runs in production. Good deployment is a process that is documented, repeatable, and recoverable when something goes wrong.
Containers are the answer to the "it works on my machine" problem. A Docker image wraps the application and all its dependencies into one unit that can run anywhere — a laptop, a server, or the cloud.
Episode 19 covers Dockerizing .NET applications, deployment to the cloud and Kubernetes, production configuration, and release strategies that minimize risk.
A .NET image should be built with multiple stages: the first stage compiles, the second contains only the runtime. The result is a small, safe image:
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY src/Toko.Api/Toko.Api.csproj .
RUN dotnet restore
COPY . .
RUN dotnet publish -c Release -o /out
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /out .
ENV ASPNETCORE_ENVIRONMENT=Production
ENTRYPOINT ["dotnet", "Toko.Api.dll"]The first stage uses the large SDK image to compile, then the runtime stage uses the slim aspnet:8.0 image — containing only the ASP.NET runtime. You already saw dotnet publish in episode 2; now its output is packaged into an image.
docker build -t toko-api:1.0 .
docker run -p 8080:8080 toko-api:1.0The docker run -p 8080:8080 toko-api:1.0 command maps the container port to the host port. Production configuration is provided through environment variables when the container is run.
All three major clouds have managed services for ASP.NET Core:
The common pattern: CI builds the image and pushes it to a registry (Azure Container Registry, ECR, or Artifact Registry), then the cloud platform pulls and runs it.
In Kubernetes, applications are declared with YAML manifests:
apiVersion: apps/v1
kind: Deployment
metadata:
name: toko-api
spec:
replicas: 3
selector:
matchLabels:
app: toko-api
template:
metadata:
labels:
app: toko-api
spec:
containers:
- name: toko-api
image: registry.contoh.id/toko-api:1.0
ports:
- containerPort: 8080
env:
- name: ASPNETCORE_ENVIRONMENT
value: ProductionApply it with kubectl:
kubectl apply -f deployment.yaml
kubectl rollout status deployment/toko-apiThe kubectl rollout status command waits for the deployment to finish. The replicas: 3 value runs three pods — if one dies, the rest keep serving.
In production, don't store secrets in the image. Provide them through environment variables or a secret store:
docker run -d -p 8080:8080 \
-e ASPNETCORE_ENVIRONMENT=Production \
-e ConnectionStrings__Default="Host=db;Database=toko;Username=app;Password=rahasia" \
toko-api:1.0The docker run -e ConnectionStrings__Default=... command overrides the appsettings.json configuration. The double __ format (which you learned in episode 8) maps an environment variable to the configuration hierarchy.
kubectl rollout undo deployment/toko-apiThe kubectl rollout undo command instantly restores the deployment to a previous revision. With these strategies, a problematic release never becomes a crisis.
Key takeaways:
dotnet publish produces container-ready output.In the next episode 20 we make sure a running application can be monitored: observability and monitoring — logging with Serilog, metrics and tracing with OpenTelemetry, health checks and monitoring endpoints, and incident response and production troubleshooting.