Chapter 73: DevOps Projects Part 3: Docker Websites, APIs, Compose, Registry and ECR
73.5 Project 21: Dockerize a .NET minimal API
Friends, .NET APIs also ship in containers. Today we write a minimal API on net8.0, use a multi-stage Dockerfile with Alpine SDK and aspnet runtime, run on 8080, and prove /health.
मित्रांनो, .NET APIs सुद्धा containers मध्ये जातात. आज net8.0 minimal API, multi-stage Alpine SDK आणि aspnet runtime, port 8080, /health.
दोस्तों, .NET APIs भी containers में जाती हैं. आज net8.0 minimal API, multi-stage Alpine SDK और aspnet runtime, port 8080, /health.
What you will build in this project
- A net8.0 minimal API with /health.
- Multi-stage Dockerfile sdk then aspnet.
- Image dotnet-api:1.0 on 8080.
- curl proves health and hello.
1. Start from a real company story
At HDFC Bank technology in Mumbai, a ledger API was installed with the full Windows SDK on every box. Images were over 2 GB and deploys took 25 minutes. The ops lead asked every engineer to paste the exact docker images size line into the pull request before merge. That one habit cut surprise production rollbacks.
A failed cutover on salary weekend cost about ₹30 lakh in manual reconciliations.
Nisha switched to mcr.microsoft.com Alpine multi-stage images. Deploys dropped under 3 minutes.
Today we dockerize that .NET API the slim way.
Ravindra Bagale's Tip
Many students ship the SDK image to production. Use aspnet runtime in the final stage. Keep that habit. Write every command in your notebook. When something fails, copy the exact error line before you change anything else. Measure twice, change once.
खूप students SDK image production मध्ये पाठवतात. Final stage मध्ये aspnet runtime वापरा. सवय ठेवा. प्रत्येक command वहीमध्ये लिहा. चूक झाली तर आधी exact error कॉपी करा. दोनदा मोजा, एकदा बदला.
बहुत students SDK image production में भेजते हैं. Final stage में aspnet runtime इस्तेमाल करें. आदत रखिए. हर command कॉपी में लिखें. गलती हो तो पहले exact error कॉपी करें. दो बार मापें, एक बार बदलें.
2. Start and prepare
Step 1: Make project folder
Why: Need a clean place for the .NET minimal API. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: mkdir tree. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 1: mkdir
Type this:
mkdir -p /tmp/dotnet-api && echo ready
You will see:
ready
What it means: Folder.
Example 2: pwd
Type this:
cd /tmp/dotnet-api && pwd
You will see:
/tmp/dotnet-api
What it means: In place.
Example 3: empty
Type this:
ls /tmp/dotnet-api | wc -l
You will see:
0
What it means: Fresh.
Step 2: Write Program.cs
Why: Program.cs is the entry point. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: printf Program.cs. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 4: prog
Type this:
printf '%s\n' 'var b = WebApplication.CreateBuilder(args);' 'var app = b.Build();' 'app.MapGet("/health", () => Results.Json(new { ok = true }));' 'app.MapGet("/api/hello", () => Results.Json(new { msg = "ledger-api" }));' 'app.Run("http://0.0.0.0:8080");' > /tmp/dotnet-api/Program.cs && wc -l /tmp/dotnet-api/Program.cs
You will see:
5 /tmp/dotnet-api/Program.cs
What it means: Minimal API.
Example 5: grep
Type this:
grep health /tmp/dotnet-api/Program.cs
You will see:
MapGet("/health"
What it means: Route.
Example 6: hello
Type this:
grep ledger /tmp/dotnet-api/Program.cs
You will see:
ledger-api
What it means: Route.
Step 3: Write csproj
Why: csproj pins the framework. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: printf csproj. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 7: csproj
Type this:
printf '%s\n' '<Project Sdk="Microsoft.NET.Sdk.Web">' ' <PropertyGroup><TargetFramework>net8.0</TargetFramework><Nullable>enable</Nullable></PropertyGroup>' '</Project>' > /tmp/dotnet-api/dotnet-api.csproj && cat /tmp/dotnet-api/dotnet-api.csproj
You will see:
Sdk Web net8.0
What it means: Pinned TFM.
Example 8: tfm
Type this:
grep TargetFramework /tmp/dotnet-api/dotnet-api.csproj
You will see:
net8.0
What it means: net8.
Example 9: mode
Type this:
stat -c '%a' /tmp/dotnet-api/dotnet-api.csproj
You will see:
644
What it means: Safe.
3. Build the main pieces
Step 4: Write Dockerfile
Why: Dockerfile uses sdk then aspnet runtime. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: multi-stage FROM. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 10: df
Type this:
printf '%s\n' 'FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build' 'WORKDIR /src' 'COPY . .' 'RUN dotnet publish -c Release -o /out' 'FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine' 'WORKDIR /app' 'COPY --from=build /out .' 'EXPOSE 8080' 'USER app' 'ENTRYPOINT ["dotnet","dotnet-api.dll"]' > /tmp/dotnet-api/Dockerfile && head -4 /tmp/dotnet-api/Dockerfile
You will see:
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /out
What it means: Multi-stage.
Example 11: runtime
Type this:
grep aspnet /tmp/dotnet-api/Dockerfile
You will see:
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine
What it means: Slim runtime.
Example 12: user
Type this:
grep USER /tmp/dotnet-api/Dockerfile
You will see:
USER app
What it means: Non-root.
Step 5: Write dockerignore
Why: Context must stay lean. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: .dockerignore. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 13: ignore
Type this:
printf '%s\n' 'bin/' 'obj/' '.git' '*.md' '**/bin' '**/obj' > /tmp/dotnet-api/.dockerignore && cat /tmp/dotnet-api/.dockerignore
You will see:
bin/
obj/
.git
*.md
**/bin
**/obj
What it means: Lean.
Example 14: ls
Type this:
ls /tmp/dotnet-api
You will see:
Dockerfile
Program.cs
dotnet-api.csproj
.dockerignore
What it means: Ready.
Example 15: wc
Type this:
wc -l /tmp/dotnet-api/Dockerfile
You will see:
10 /tmp/dotnet-api/Dockerfile
What it means: Short.
Step 6: Build image
Why: build creates layers. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: docker build -t. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 16: build
Type this:
docker build -t dotnet-api:1.0 /tmp/dotnet-api
You will see:
Successfully tagged dotnet-api:1.0
What it means: Built.
Example 17: images
Type this:
docker images dotnet-api --format '{{.Repository}}:{{.Tag}}'
You will see:
dotnet-api:1.0
What it means: Listed.
Example 18: hist
Type this:
docker history dotnet-api:1.0 | head -3
You will see:
IMAGE ...
What it means: Layers.
4. Automate and verify
Step 7: Run container
Why: run starts Kestrel. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: docker run -d. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 19: run
Type this:
docker rm -f dotnet-api >/dev/null 2>&1; docker run -d --name dotnet-api -p 8080:8080 -e ASPNETCORE_URLS=http://+:8080 dotnet-api:1.0 && echo started
You will see:
started
What it means: Detached.
Example 20: ps
Type this:
docker ps --filter name=dotnet-api --format '{{.Names}} {{.Ports}}'
You will see:
dotnet-api 0.0.0.0:8080->8080/tcp
What it means: Mapped.
Example 21: wait
Type this:
sleep 3; echo up
You will see:
up
What it means: Warmup.
Step 8: Port map 8080
Why: Host 8080 reaches Kestrel. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: -p 8080:8080. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 22: port
Type this:
docker port dotnet-api
You will see:
8080/tcp -> 0.0.0.0:8080
What it means: Publish.
Example 23: name
Type this:
docker inspect -f '{{.Name}}' dotnet-api
You will see:
/dotnet-api
What it means: Name.
Example 24: status
Type this:
docker inspect -f '{{.State.Status}}' dotnet-api
You will see:
running
What it means: Running.
Step 9: curl health
Why: /health proves ready. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: curl 200. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 25: health
Type this:
curl -s http://127.0.0.1:8080/health
You will see:
{"ok":true}
What it means: Health.
Example 26: code
Type this:
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/health
You will see:
200
What it means: OK.
Example 27: hello
Type this:
curl -s http://127.0.0.1:8080/api/hello
You will see:
{"msg":"ledger-api"}
What it means: Route.
5. Harden and accept
Step 10: Logs
Why: Logs show listening. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: docker logs. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 28: logs
Type this:
docker logs dotnet-api 2>&1 | tail -5
You will see:
Now listening on...
What it means: Kestrel.
Example 29: follow
Type this:
echo 'docker logs -f dotnet-api'
You will see:
docker logs -f dotnet-api
What it means: Follow.
Example 30: env
Type this:
docker exec dotnet-api printenv ASPNETCORE_URLS
You will see:
http://+:8080
What it means: URL set.
Step 11: Exec
Why: exec for quick checks. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: docker exec. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 31: exec
Type this:
docker exec dotnet-api sh -c 'echo inside'
You will see:
inside
What it means: Shell.
Example 32: dll
Type this:
docker exec dotnet-api ls /app | head
You will see:
dotnet-api.dll
What it means: Published.
Example 33: who
Type this:
docker exec dotnet-api whoami
You will see:
app
What it means: Non-root.
Step 12: Acceptance checklist
Why: Nisha signs off when these pass. We do this before the next step so mistakes stay small and easy to undo. Read the output carefully; the numbers and names tell you if the machine is healthy today.
What: All green. Follow the numbered examples below. Type each command, read the exact output, and match the meaning line before you move on. That is how real teams learn a tool at work.
Example 34: smoke
Type this:
curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1:8080/health
You will see:
200
What it means: Smoke.
Example 35: mode
Type this:
stat -c '%a' /tmp/dotnet-api/Dockerfile
You will see:
644
What it means: Safe.
Example 36: Sign-off
Type this:
echo 'Nisha: PASS'
You will see:
Nisha: PASS
What it means: Done.
Check yourself
Q1. Final base image?
aspnet:8.0-alpine
Q2. Host port?
8080
Q3. Health path?
/health
Q4. Who fixed HDFC ledger API?
Nisha
Q5. TFM?
net8.0
Q6. Why not ship SDK?
Runtime image is smaller and safer
Friends, you dockerized a .NET API with a slim multi-stage image. Did you understand? Let us try it once more. Change the hello message, rebuild as 1.1, and switch the container.
मित्रांनो, तुम्ही slim multi-stage ने .NET API dockerize केली. hello बदलून 1.1 rebuild करा.
दोस्तों, आपने slim multi-stage से .NET API dockerize की. hello बदल 1.1 rebuild करें.
Lab: try at home
- Write Program.cs and csproj.
- Write multi-stage Dockerfile.
- Build dotnet-api:1.0.
- Run on 8080 and curl.
- Tag 1.1 after a text change.
Recap
- Pin net8.0 and Alpine tags.
- SDK builds; aspnet runs.
- COPY --from=build keeps the runtime small.
- ASPNETCORE_URLS sets the listen address.
- Non-root USER app.