Ravindra BagaleCourses & study guides मराठी

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.

What you will build in this project

  1. A net8.0 minimal API with /health.
  2. Multi-stage Dockerfile sdk then aspnet.
  3. Image dotnet-api:1.0 on 8080.
  4. 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.

Program.csminimal APIDockerfilesdk then aspnetdocker builddotnet-api:1.0run -p 8080curl /health
Picture 1: from fat SDK box to slim aspnet container

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.

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.

Lab: try at home

  1. Write Program.cs and csproj.
  2. Write multi-stage Dockerfile.
  3. Build dotnet-api:1.0.
  4. Run on 8080 and curl.
  5. 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.