From d027ffc589a98cf903ff658fbf01f243f0978e4d Mon Sep 17 00:00:00 2001 From: Brandon Hunt <101275235+brandonh6k@users.noreply.github.com> Date: Sun, 16 Nov 2025 14:31:46 -0700 Subject: [PATCH 1/2] Update .NET guide to .NET 10 --- content/guides/dotnet/containerize.md | 2 +- content/guides/dotnet/develop.md | 12 ++++++------ content/guides/dotnet/run-tests.md | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/content/guides/dotnet/containerize.md b/content/guides/dotnet/containerize.md index 146874ca9d8c..f160d57a3ce4 100644 --- a/content/guides/dotnet/containerize.md +++ b/content/guides/dotnet/containerize.md @@ -60,7 +60,7 @@ Let's get started! ? What application platform does your project use? ASP.NET Core ? What's the name of your solution's main project? myWebApp -? What version of .NET do you want to use? 8.0 +? What version of .NET do you want to use? 10.0 ? What local port do you want to use to access your server? 8080 ``` diff --git a/content/guides/dotnet/develop.md b/content/guides/dotnet/develop.md index d5bea5491fd1..e6689567d49d 100644 --- a/content/guides/dotnet/develop.md +++ b/content/guides/dotnet/develop.md @@ -288,11 +288,11 @@ immediately reflected in the running container. Open `docker-dotnet-sample/src/Pages/Index.cshtml` in an IDE or text editor and update the student name text on line 13 from `Student name is` to `Student name:`. ```diff --

Student Name is @Model.StudentName

+-

Student name is @Model.StudentName

+

Student name: @Model.StudentName

``` -Save the changes to `Index.cshmtl` and then wait a few seconds for the application to rebuild. Refresh [http://localhost:8080](http://localhost:8080) in your browser and verify that the updated text appears. +Save the changes to `Index.cshtml` and then wait a few seconds for the application to rebuild. Refresh [http://localhost:8080](http://localhost:8080) in your browser and verify that the updated text appears. Press `ctrl+c` in the terminal to stop your application. @@ -307,19 +307,19 @@ The following is the updated Dockerfile. ```Dockerfile {hl_lines="10-13"} # syntax=docker/dockerfile:1 -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build ARG TARGETARCH COPY . /source WORKDIR /source/src RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app -FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS development +FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS development COPY . /source WORKDIR /source/src CMD dotnet run --no-launch-profile -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS final +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final WORKDIR /app COPY --from=build /app . ARG UID=10001 @@ -379,7 +379,7 @@ secrets: file: db/password.txt ``` -Your containerized application will now use the `mcr.microsoft.com/dotnet/sdk:8.0-alpine` image, which includes development tools like `dotnet test`. Continue to the next section to learn how you can run `dotnet test`. +Your containerized application will now use the `mcr.microsoft.com/dotnet/sdk:10.0-alpine` image, which includes development tools like `dotnet test`. Continue to the next section to learn how you can run `dotnet test`. ## Summary diff --git a/content/guides/dotnet/run-tests.md b/content/guides/dotnet/run-tests.md index 1e404c345965..4d3164488588 100644 --- a/content/guides/dotnet/run-tests.md +++ b/content/guides/dotnet/run-tests.md @@ -36,7 +36,7 @@ You should see output that contains the following. Starting test execution, please wait... A total of 1 test files matched the specified pattern. -Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms - /source/tests/bin/Debug/net8.0/tests.dll (net8.0) +Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms - /source/tests/bin/Debug/net10.0/tests.dll (net10.0) ``` To learn more about the command, see [docker compose run](/reference/cli/docker/compose/run/). @@ -50,7 +50,7 @@ The following is the updated Dockerfile. ```dockerfile {hl_lines="9"} # syntax=docker/dockerfile:1 -FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build ARG TARGETARCH COPY . /source WORKDIR /source/src @@ -58,12 +58,12 @@ RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app RUN dotnet test /source/tests -FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS development +FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS development COPY . /source WORKDIR /source/src CMD dotnet run --no-launch-profile -FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS final +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final WORKDIR /app COPY --from=build /app . ARG UID=10001 @@ -92,16 +92,16 @@ You should see output containing the following. #11 1.564 Determining projects to restore... #11 3.421 Restored /source/src/myWebApp.csproj (in 1.02 sec). #11 19.42 Restored /source/tests/tests.csproj (in 17.05 sec). -#11 27.91 myWebApp -> /source/src/bin/Debug/net8.0/myWebApp.dll -#11 28.47 tests -> /source/tests/bin/Debug/net8.0/tests.dll -#11 28.49 Test run for /source/tests/bin/Debug/net8.0/tests.dll (.NETCoreApp,Version=v8.0) +#11 27.91 myWebApp -> /source/src/bin/Debug/net10.0/myWebApp.dll +#11 28.47 tests -> /source/tests/bin/Debug/net10.0/tests.dll +#11 28.49 Test run for /source/tests/bin/Debug/net10.0/tests.dll (.NETCoreApp,Version=v10.0) #11 28.67 Microsoft (R) Test Execution Command Line Tool Version 17.3.3 (x64) #11 28.67 Copyright (c) Microsoft Corporation. All rights reserved. #11 28.68 #11 28.97 Starting test execution, please wait... #11 29.03 A total of 1 test files matched the specified pattern. #11 32.07 -#11 32.08 Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms - /source/tests/bin/Debug/net8.0/tests.dll (net8.0) +#11 32.08 Passed! - Failed: 0, Passed: 1, Skipped: 0, Total: 1, Duration: < 1 ms - /source/tests/bin/Debug/net10.0/tests.dll (net10.0) #11 DONE 32.2s ``` From 613c97585f55525951e241a4f54c2c2333478df9 Mon Sep 17 00:00:00 2001 From: Brandon Hunt <101275235+brandonh6k@users.noreply.github.com> Date: Tue, 18 Nov 2025 06:00:27 -0700 Subject: [PATCH 2/2] Add Docker Hardened Images support to .NET guide --- content/guides/dotnet/containerize.md | 105 ++++++++++++++++++++++++-- 1 file changed, 98 insertions(+), 7 deletions(-) diff --git a/content/guides/dotnet/containerize.md b/content/guides/dotnet/containerize.md index f160d57a3ce4..5cae147baaa6 100644 --- a/content/guides/dotnet/containerize.md +++ b/content/guides/dotnet/containerize.md @@ -39,12 +39,71 @@ $ git clone https://github.com/docker/docker-dotnet-sample ## Initialize Docker assets -Now that you have an application, you can use `docker init` to create the -necessary Docker assets to containerize your application. Inside the -`docker-dotnet-sample` directory, run the `docker init` command in a terminal. -`docker init` provides some default configuration, but you'll need to answer a -few questions about your application. Refer to the following example to answer -the prompts from `docker init` and use the same answers for your prompts. +Now that you have an application, you can create the necessary Docker assets to containerize it. You can choose between using the official .NET images or Docker Hardened Images (DHI). + +> [Docker Hardened Images (DHIs)](https://docs.docker.com/dhi/) are minimal, secure, and production-ready container base and application images maintained by Docker. DHI images are recommended for better security—they are designed to reduce vulnerabilities and simplify compliance. + +> **Note**: DHI for .NET 10 is not yet available. The following DHI example uses .NET 9. Check the [DHI catalog](https://hub.docker.com/hardened-images/catalog) for .NET 10 availability, or use the official image tab below for .NET 10. + +{{< tabs >}} +{{< tab name="Using Docker Hardened Images (.NET 9)" >}} + +Docker Hardened Images (DHIs) for .NET are available on [Docker Hub](https://hub.docker.com/hardened-images/catalog/dhi/aspnetcore). Unlike using the Docker Official Image, you must first mirror the image into your organization. Follow the instructions in the [DHI quickstart](/dhi/get-started/) to create a mirrored repository. + +Mirrored repositories must start with `dhi-`, for example: `FROM /dhi-aspnetcore:`. + +You can use `docker init` to generate Docker assets, then modify the Dockerfile to use DHI images: + +```console +$ docker init +Welcome to the Docker Init CLI! + +This utility will walk you through creating the following files with sensible defaults for your project: + - .dockerignore + - Dockerfile + - compose.yaml + - README.Docker.md + +Let's get started! + +? What application platform does your project use? ASP.NET Core +? What's the name of your solution's main project? myWebApp +? What version of .NET do you want to use? 9.0 +? What local port do you want to use to access your server? 8080 +``` + +Then update your Dockerfile to use DHI images: + +```dockerfile {title=Dockerfile} +# syntax=docker/dockerfile:1 + +FROM --platform=$BUILDPLATFORM /dhi-dotnet:9.0-alpine AS build +ARG TARGETARCH +COPY . /source +WORKDIR /source/src +RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ + dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app + +FROM /dhi-aspnetcore:9.0-alpine AS final +WORKDIR /app +COPY --from=build /app . +ARG UID=10001 +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + appuser +USER appuser +ENTRYPOINT ["dotnet", "myWebApp.dll"] +``` + +{{< /tab >}} +{{< tab name="Using the official .NET 10 image" >}} + +You can use `docker init` to create the necessary Docker assets. Inside the `docker-dotnet-sample` directory, run the `docker init` command in a terminal. `docker init` provides some default configuration, but you'll need to answer a few questions about your application. Refer to the following example to answer the prompts from `docker init` and use the same answers for your prompts. ```console $ docker init @@ -64,6 +123,37 @@ Let's get started! ? What local port do you want to use to access your server? 8080 ``` +This generates a Dockerfile using the official .NET 10 images from Microsoft Container Registry: + +```dockerfile {title=Dockerfile} +# syntax=docker/dockerfile:1 + +FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build +ARG TARGETARCH +COPY . /source +WORKDIR /source/src +RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ + dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app + +FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final +WORKDIR /app +COPY --from=build /app . +ARG UID=10001 +RUN adduser \ + --disabled-password \ + --gecos "" \ + --home "/nonexistent" \ + --shell "/sbin/nologin" \ + --no-create-home \ + --uid "${UID}" \ + appuser +USER appuser +ENTRYPOINT ["dotnet", "myWebApp.dll"] +``` + +{{< /tab >}} +{{< /tabs >}} + You should now have the following contents in your `docker-dotnet-sample` directory. @@ -78,7 +168,7 @@ directory. │ └── README.md ``` -To learn more about the files that `docker init` added, see the following: +To learn more about the files, see the following: - [Dockerfile](/reference/dockerfile.md) - [.dockerignore](/reference/dockerfile.md#dockerignore-file) - [compose.yaml](/reference/compose-file/_index.md) @@ -126,6 +216,7 @@ Related information: - [Dockerfile reference](/reference/dockerfile.md) - [.dockerignore file reference](/reference/dockerfile.md#dockerignore-file) - [Docker Compose overview](/manuals/compose/_index.md) + - [Docker Hardened Images](/dhi/) ## Next steps