Table of Contents

Basic Tutorial: Containerizing Your First .NET Application

This step-by-step tutorial will guide you through containerizing a .NET application using JD.MSBuild.Containers, from a blank project to a running container.

What You'll Learn

  • Creating a new .NET Web API project
  • Installing and configuring JD.MSBuild.Containers
  • Generating a Dockerfile automatically
  • Building a Docker image
  • Running your application in a container
  • Testing the containerized application

Prerequisites

Ensure you have completed the Getting Started prerequisites:

  • .NET SDK 8.0 or later
  • Docker Desktop or compatible runtime
  • Basic terminal/command line knowledge

Step 1: Create a New Web API Project

Let's start by creating a simple ASP.NET Core Web API:

# Create a new directory for your project
mkdir MyFirstContainerApp
cd MyFirstContainerApp

# Create a new Web API project
dotnet new webapi -n MyFirstContainerApp
cd MyFirstContainerApp

# Verify the project builds
dotnet build

You should see output indicating a successful build:

Build succeeded.
    0 Warning(s)
    0 Error(s)

Step 2: Run the Application (Without Docker)

Before containerizing, let's verify the application works:

# Run the application
dotnet run

You should see output like:

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5000

Open another terminal and test the API:

curl http://localhost:5000/weatherforecast

You should receive a JSON response with weather data. Press Ctrl+C to stop the application.

Step 3: Add JD.MSBuild.Containers

Now let's add containerization support:

# Add the package
dotnet add package JD.MSBuild.Containers

You should see:

info : PackageReference for package 'JD.MSBuild.Containers' version '*' added to file 'MyFirstContainerApp.csproj'.

Step 4: Configure Docker Settings

Open MyFirstContainerApp.csproj in your favorite editor and add Docker properties:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    
    <!-- Docker Configuration -->
    <DockerEnabled>true</DockerEnabled>
    <DockerImageName>myfirstcontainerapp</DockerImageName>
    <DockerImageTag>v1</DockerImageTag>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="JD.MSBuild.Containers" Version="*" />
  </ItemGroup>
</Project>

What These Properties Mean:

  • DockerEnabled - Enables Docker integration
  • DockerImageName - Name of your Docker image (lowercase, no spaces)
  • DockerImageTag - Version tag for your image

Step 5: Generate the Dockerfile

Build your project to generate the Dockerfile:

dotnet build

You should see new output indicating Dockerfile generation:

Resolving Docker inputs...
Generating Dockerfile at /path/to/MyFirstContainerApp/Dockerfile
Dockerfile generated successfully

Step 6: Review the Generated Dockerfile

Let's examine what was generated:

cat Dockerfile

You should see a multi-stage Dockerfile similar to:

# This file was auto-generated by JD.MSBuild.Containers

FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 8080

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["MyFirstContainerApp.csproj", "./"]
RUN dotnet restore "MyFirstContainerApp.csproj"
COPY . .
RUN dotnet build "MyFirstContainerApp.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "MyFirstContainerApp.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "MyFirstContainerApp.dll"]

Understanding the Dockerfile:

  • Stage 1 (base): Sets up the runtime environment
  • Stage 2 (build): Compiles your application
  • Stage 3 (publish): Publishes the application
  • Stage 4 (final): Creates the minimal runtime image

Step 7: Build the Docker Image

Now let's build the Docker image. Update your .csproj to enable image building:

<PropertyGroup>
  <DockerEnabled>true</DockerEnabled>
  <DockerImageName>myfirstcontainerapp</DockerImageName>
  <DockerImageTag>v1</DockerImageTag>
  
  <!-- Enable automatic image building -->
  <DockerBuildImage>true</DockerBuildImage>
  <DockerBuildOnPublish>true</DockerBuildOnPublish>
</PropertyGroup>

Now publish to build the image:

dotnet publish

You should see Docker build output:

Building Docker image: myfirstcontainerapp:v1
docker build -t myfirstcontainerapp:v1 .
[+] Building 45.2s (17/17) FINISHED
 => [internal] load build definition from Dockerfile
 => [internal] load .dockerignore
 => [build] COPY ["MyFirstContainerApp.csproj", "./"]
 => [build] RUN dotnet restore
 => [build] COPY . .
 => [build] RUN dotnet build
 => [publish] RUN dotnet publish
 => [final] COPY --from=publish /app/publish .
 => exporting to image
Image built successfully: myfirstcontainerapp:v1

Verify the image was created:

docker images | grep myfirstcontainerapp

You should see:

myfirstcontainerapp   v1    abc123def456   1 minute ago   216MB

Step 8: Run Your Container

Start a container from your image:

docker run -d -p 8080:8080 --name myapp myfirstcontainerapp:v1

Explanation:

  • -d - Run in detached mode (background)
  • -p 8080:8080 - Map host port 8080 to container port 8080
  • --name myapp - Give the container a friendly name
  • myfirstcontainerapp:v1 - The image to run

Verify the container is running:

docker ps

You should see:

CONTAINER ID   IMAGE                        COMMAND                  STATUS          PORTS                    NAMES
abc123def456   myfirstcontainerapp:v1      "dotnet MyFirstConta…"   Up 10 seconds   0.0.0.0:8080->8080/tcp   myapp

Step 9: Test Your Containerized Application

Test the API endpoint:

curl http://localhost:8080/weatherforecast

You should receive the JSON weather data, just like before, but now it's running in a container!

Step 10: View Container Logs

Check the application logs:

docker logs myapp

You should see application startup logs:

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://[::]:8080
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.

Step 11: Stop and Remove the Container

When you're done testing:

# Stop the container
docker stop myapp

# Remove the container
docker rm myapp

# (Optional) Remove the image
docker rmi myfirstcontainerapp:v1

Complete Workflow Summary

Here's the complete workflow we just followed:

# 1. Create project
dotnet new webapi -n MyFirstContainerApp
cd MyFirstContainerApp

# 2. Add JD.MSBuild.Containers
dotnet add package JD.MSBuild.Containers

# 3. Configure Docker in .csproj
# (Edit file to add Docker properties)

# 4. Generate Dockerfile
dotnet build

# 5. Build Docker image
dotnet publish

# 6. Run container
docker run -d -p 8080:8080 --name myapp myfirstcontainerapp:v1

# 7. Test
curl http://localhost:8080/weatherforecast

# 8. Clean up
docker stop myapp
docker rm myapp

Troubleshooting

Issue: Dockerfile Not Generated

Symptom: Running dotnet build doesn't create a Dockerfile.

Solution: Verify Docker is enabled in your .csproj:

<DockerEnabled>true</DockerEnabled>

Issue: Docker Build Fails

Symptom: dotnet publish fails with Docker errors.

Solution:

  1. Verify Docker is running: docker ps
  2. Check Docker daemon is accessible: docker info
  3. Try building the Dockerfile manually: docker build -t test .

Issue: Port Already in Use

Symptom: Container fails to start with "port already allocated" error.

Solution: Use a different port:

docker run -d -p 8081:8080 --name myapp myfirstcontainerapp:v1
curl http://localhost:8081/weatherforecast

Issue: Container Exits Immediately

Symptom: Container shows "Exited (0)" status immediately after starting.

Solution: Check logs:

docker logs myapp

Common causes:

  • Application configuration errors
  • Missing environment variables
  • Port binding issues

What You've Learned

Congratulations! You've successfully:

✅ Created a .NET Web API project
✅ Added JD.MSBuild.Containers to your project
✅ Generated a Dockerfile automatically
✅ Built a Docker image from your application
✅ Ran your application in a container
✅ Tested the containerized application

Next Steps

Now that you understand the basics, explore:

Practice Exercise

Try these on your own:

  1. Change the image tag to dev instead of v1
  2. Add a custom health check endpoint and test it in the container
  3. Create a second endpoint and verify it works in the container
  4. Run multiple containers with different port mappings

Additional Resources