Add Dockerfiles to your .NET app model

With .NET Aspire it's possible to specify a Dockerfile to build when the app host is started using either the AddDockerfile or WithDockerfile extension methods.

Add a Dockerfile to the app model

In the following example the AddDockerfile extension method is used to specify a container by referencing the context path for the container build.

var builder = DistributedApplication.CreateBuilder(args);

var container = builder.AddDockerfile(
    "mycontainer", "relative/context/path");

Unless the context path argument is a rooted path the context path is interpreted as being relative to the app host projects directory (where the AppHost *.csproj folder is located).

By default the name of the Dockerfile which is used is Dockerfile and is expected to be within the context path directory. It's possible to explicitly specify the Dockerfile name either as an absolute path or a relative path to the context path.

This is useful if you wish to modify the specific Dockerfile being used when running locally or when the app host is deploying.

var builder = DistributedApplication.CreateBuilder(args);

var container = builder.ExecutionContext.IsRunMode
    ? builder.AddDockerfile(
          "mycontainer", "relative/context/path", "Dockerfile.debug")
    : builder.AddDockerfile(
          "mycontainer", "relative/context/path", "Dockerfile.release");

Customize existing container resources

When using AddDockerfile the return value is an IResourceBuilder<ContainerResource>. .NET Aspire includes many custom resource types that are derived from ContainerResource.

Using the WithDockerfile extension method it's possible to continue using these strongly typed resource types and customize the underlying container that is used.

var builder = DistributedApplication.CreateBuilder(args);

var pgsql = builder.AddPostgres("pgsql")
                   .WithDockerfile("path/to/context")
                   .WithPgAdmin();

Pass build arguments

The WithBuildArg method can be used to pass arguments into the container image build.

var builder = DistributedApplication.CreateBuilder(args);

var container = builder.AddDockerfile("mygoapp", "relative/context/path")
                       .WithBuildArg("GO_VERSION", "1.22");

The value parameter on the WithBuildArg method can be a literal value (boolean, string, int) or it can be a resource builder for a parameter resource. The following code replaces the GO_VERSION with a parameter value that can be specified at deployment time.

var builder = DistributedApplication.CreateBuilder(args);

var goVersion = builder.AddParameter("goversion");

var container = builder.AddDockerfile("mygoapp", "relative/context/path")
                       .WithBuildArg("GO_VERSION", goVersion);

Build arguments correspond to the ARG command in Dockerfiles. Expanding the preceding example, this is a multi-stage Dockerfile which specifies specific container image version to use as a parameter.

# Stage 1: Build the Go program
ARG GO_VERSION=1.22
FROM golang:${GO_VERSION} AS builder
WORKDIR /build
COPY . .
RUN go build mygoapp.go

# Stage 2: Run the Go program
FROM mcr.microsoft.com/cbl-mariner/base/core:2.0
WORKDIR /app
COPY --from=builder /build/mygoapp .
CMD ["./mygoapp"]

Note

Instead of hardcoding values into the container image, it's recommended to use environment variables for values that frequently change. This avoids the need to rebuild the container image whenever a change is required.

Pass build secrets

In addition to build arguments it's possible to specify build secrets using WithBuildSecret which are made selectively available to individual commands in the Dockerfile using the --mount=type=secret syntax on RUN commands.

var builder = DistributedApplication.CreateBuilder(args);

var accessToken = builder.AddParameter("accesstoken", secret: true);

var container = builder.AddDockerfile("myapp", "relative/context/path")
                       .WithBuildSecret("ACCESS_TOKEN", accessToken);

For example, consider the RUN command in a Dockerfile which exposes the specified secret to the specific command:

# The helloworld command can read the secret from /run/secrets/ACCESS_TOKEN
RUN --mount=type=secret,id=ACCESS_TOKEN helloworld

Caution

Caution should be exercised when passing secrets in build environments. This is often done when using a token to retrieve dependencies from private repositories or feeds before a build. It is important to ensure that the injected secrets are not copied into the final or intermediate images.