ASP.NET Core 10 Docker Tutorial: Containerize Your Tested Minimal API
Docker is the next delivery boundary for the API you tested in the previous lesson. A container is not a substitute for tests, configuration, or secure operations; it is a repeatable packaging unit. In this lesson, you will build a multi-stage image for an ASP.NET Core 10 Minimal API, run it locally, and validate the same endpoint from outside the container.
Prerequisites
- Complete Lesson 6: test endpoint, authentication, and EF Core paths.
- Install Docker Desktop and the .NET 10 SDK.
- Have the API project and its test project available locally.
Follow the full sequence on the .NET and ASP.NET Core learning track.
Working outcome
You will produce an image with build tools left behind in the build stage, a runtime-only final stage, an explicit port, and a non-root application user. The final image can answer a health request locally.
Use a multi-stage Dockerfile
Copy project files before the remaining source files so dependency restore can be cached independently. Name the stages: it makes later changes safer than relying on a numeric index. Use the SDK only to restore, build, and publish; copy the published output into the ASP.NET runtime image.
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY ["Orders.Api/Orders.Api.csproj", "Orders.Api/"]
RUN dotnet restore "Orders.Api/Orders.Api.csproj"
COPY . .
WORKDIR "/src/Orders.Api"
RUN dotnet publish -c Release -o /app/publish /p:UseAppHost=false
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
WORKDIR /app
EXPOSE 8080
ENV ASPNETCORE_URLS=http://+:8080
RUN adduser --disabled-password --gecos "" appuser
USER appuser
COPY --from=build /app/publish .
ENTRYPOINT ["dotnet", "Orders.Api.dll"]
Keep the build context small
Docker sends the build context before it can execute instructions. Do not send build output, local secrets, IDE folders, test results, or source-control metadata unless a build genuinely needs them. Add a .dockerignore file beside the Dockerfile.
**/bin/
**/obj/
.git/
.vs/
TestResults/
*.user
appsettings.Development.json
Do not put production settings or secrets in this file because they should not live in the context in the first place. Supply runtime configuration using your deployment platform’s secret and configuration mechanisms.
Build, run, and validate
docker build --pull -t orders-api:local .
docker run --rm -p 8080:8080 --name orders-api orders-api:local
curl -i http://localhost:8080/health
The response should be the one your application intentionally exposes. In a second terminal, inspect logs and confirm the process runs as the configured non-root user if your base image and platform support the command. Keep your integration tests in CI: a successful image build only proves packaging, not endpoint behavior.
Common mistakes
- Using an SDK image in production: it carries build tooling that the runtime does not require.
- Copying the whole repository before restore: ordinary source edits invalidate the restore cache.
- Publishing secrets into the image: image layers and registries are not a secret store.
- Assuming EXPOSE publishes a port: map a host port at runtime or configure your orchestrator.
- Running as root by habit: use the least privilege your application needs and test filesystem permissions.
Validation and production guidance
- Build from a clean checkout and confirm .dockerignore excludes local output.
- Run the container and exercise the tested health and authenticated endpoints with the expected environment configuration.
- Scan and regularly rebuild images so updated base-image fixes are incorporated.
- Pin a deliberate base-image strategy and record its provenance; test upgrades in CI.
- Set resource limits, read-only filesystem policy where compatible, health probes, and structured logs in the deployment manifest.
Docker’s multi-stage builds intentionally leave build tools and intermediate files out of the final stage. That separation improves distribution efficiency and reduces unnecessary runtime surface, but it does not replace patching or deployment policy.
Next lesson
Lesson 8 will deploy this container: configuration, registry, environment-specific settings, health checks, and a safe rollout. Continue only after the image builds reproducibly and your endpoint checks pass.