Dockerfile for a .NET 9 Web API Application

Occasionally, I want to build and run a .NET application within a Docker container.

I use VS Code on Linux, so I don’t have the easy option to add a Dockerfile that is in the full Visual Studio. And I can never remember exactly what to do, so here it is for my future reference.

There are samples on the dotnet/dotnet-docker/ GitHub repo. But it feels a bit cluttered, especially since the .dockerignore file is easily missed, and critically important.

If you get an error like either of the two below, it’s probably because you copied the obj directory into the container. This is very likely to happen on the computer you are developing on because the obj directory is created when you build or run the application. It is less likely to happen on a build server or CI/CD pipeline because the source code is usually cloned for a repository where the obj directory should not exist.

error NETSDK1047: Assets file '/source/obj/project.assets.json' doesn't have a target for 'net9.0/linux-x64'. Ensure that restore has run and that you have included 'net9.0' in the TargetFrameworks for your project.

error NETSDK1064: Package Microsoft.AspNetCore.OpenApi, version 9.0.9 was not found. It might have been deleted since NuGet restore. Otherwise, NuGet restore might have only partially completed, which might have been due to maximum path length restrictions.

The project.assets.json copied has at least two things wrong with it - it has the wrong target framework, and it has absolute paths to your host computer.

The easiest fix is to use a .dockerignore file to exclude the obj directory, and any other files/directories you don’t want copied into the container. But it’s easy to forget this step, especially on Linux, because a file starting with a dot is hidden by default.

In a future release of Docker, it should be possible to use an --exclude option with the COPY command, but that is not available yet.

A simple, working, but less efficient Dockerfile

But before I show the better way, here is a simple Dockerfile that works. This Dockerfile builds a .NET 9.0 Web API application. It is a little less efficient than it could be, but it does not suffer from the issue described above because the restore happens when publishing. It’s simple and works.

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /source

# Copy everything. This is less efficient
COPY --link . .
# Publish app - will get a warning if there is a .sln file in the directory.
# -c Release is now the default for dotnet publish, but if you are using .NET < 8.0, you should include it.
# A full restore is done as part of the publish. 
RUN dotnet publish -c Release -o /app 

# Change to the runtime image
FROM mcr.microsoft.com/dotnet/aspnet:9.0
EXPOSE 8080
WORKDIR /app
COPY --link --from=build /app .

ENTRYPOINT ["./WebApiDocker"]

To build it use -

docker build -t webapidocker:1.0 .

To run it, use -

docker run -it --rm -p 8080:8080 webapidocker:1.0

A more efficient Dockerfile, but needs a .dockerignore file

This Dockerfile is more efficient because it restores the NuGet packages before copying the rest of the source code. If you change your source code, but not your dependencies, the restore step will be its own layer and skipped on subsequent builds.

But it does require a .dockerignore file to skip the obj directory, and any other files you don’t want copied into the container.

FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /source
ARG TARGETARCH

COPY --link ./*.csproj .
RUN dotnet restore -a $TARGETARCH

COPY --link . . 
RUN dotnet publish -a $TARGETARCH --no-restore -c Release -o /app 

FROM mcr.microsoft.com/dotnet/aspnet:9.0
EXPOSE 8080
WORKDIR /app
COPY --link --from=build /app .

ENTRYPOINT ["./WebApiDocker"]

Here is the .dockerignore file I use -

**/bin/
**/obj/
**/.vscode/
Dockerfile
.dockerignore

Same commands to build and run as above.

comments powered by Disqus

Related