# Add Dockerfiles to your app model

With Aspire it's possible to specify a _Dockerfile_ to build when the [AppHost](/get-started/app-host/) is started using either the `AddDockerfile` or `WithDockerfile` extension methods.

These two methods serve different purposes:

- **`AddDockerfile`**: Creates a new container resource from an existing Dockerfile. Use this when you want to add a custom containerized service to your app model.
- **`WithDockerfile`**: Customizes an existing container resource (like a database or cache) to use a different Dockerfile. Use this when you want to modify the default container image for an Aspire component.

Both methods expect an existing Dockerfile in the specified context path—neither method creates a Dockerfile for you.

## When to use AddDockerfile vs WithDockerfile

Choose the appropriate method based on your scenario:

**Use `AddDockerfile` when:**

- You want to add a custom containerized service to your app model.
- You have an existing Dockerfile for a custom application or service.
- You need to create a new container resource that isn't provided by Aspire components.

**Use `WithDockerfile` when:**

- You want to customize an existing Aspire component (like PostgreSQL, Redis, etc.).
- You need to replace the default container image with a custom one.
- You want to maintain the strongly typed resource builder and its extension methods.
- You have specific requirements that the default container image doesn't meet.

## 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.

```csharp title="AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

var container = builder.AddDockerfile(
    "mycontainer", "relative/context/path");
```
```typescript title="apphost.ts"
import { createBuilder } from './.modules/aspire.js';

const builder = await createBuilder();

const container = await 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 AppHost project directory.

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 AppHost is deploying.

```csharp title="AppHost.cs"
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");
```
```typescript title="apphost.ts"
import { createBuilder } from './.modules/aspire.js';

const builder = await createBuilder();

const container = builder.executionContext.isRunMode
    ? await builder.addDockerfile(
          "mycontainer", "relative/context/path", "Dockerfile.debug")
    : await builder.addDockerfile(
          "mycontainer", "relative/context/path", "Dockerfile.release");
```
## Customize existing container resources

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

Using the `WithDockerfile` extension method it's possible to take an existing Aspire component (like PostgreSQL, Redis, or SQL Server) and replace its default container image with a custom one built from your own Dockerfile. This allows you to continue using the strongly typed resource types and their specific extension methods while customizing the underlying container.

```csharp title="AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

// This replaces the default PostgreSQL container image with a custom one
// built from your Dockerfile, while keeping PostgreSQL-specific functionality
var pgsql = builder.AddPostgres("pgsql")
    .WithDockerfile("path/to/context")
    .WithPgAdmin(); // Still works because it's still a PostgreSQL resource
```
```typescript title="apphost.ts"
import { createBuilder } from './.modules/aspire.js';

const builder = await createBuilder();

// This replaces the default PostgreSQL container image with a custom one
// built from your Dockerfile, while keeping PostgreSQL-specific functionality
const pgsql = await builder.addPostgres("pgsql")
    .withDockerfile("path/to/context")
    .withPgAdmin(); // Still works because it's still a PostgreSQL resource
```
## Pass build arguments

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

```csharp title="AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

var container = builder.AddDockerfile("mygoapp", "relative/context/path")
    .WithBuildArg("GO_VERSION", "1.22");
```
```typescript title="apphost.ts"
import { createBuilder } from './.modules/aspire.js';

const builder = await createBuilder();

const container = await 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](/fundamentals/external-parameters/). The following code replaces the `GO_VERSION` with a parameter value that can be specified at deployment time.

```csharp title="AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

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

var container = builder.AddDockerfile("mygoapp", "relative/context/path")
    .WithBuildArg("GO_VERSION", goVersion);
```
```typescript title="apphost.ts"
import { createBuilder } from './.modules/aspire.js';

const builder = await createBuilder();

const goVersion = builder.addParameter("goversion");

const container = await builder.addDockerfile("mygoapp", "relative/context/path")
    .withBuildArg("GO_VERSION", goVersion);
```
Build arguments correspond to the [`ARG` command](https://docs.docker.com/build/guide/build-args/) in _Dockerfiles_. Expanding the preceding example, this is a multi-stage _Dockerfile_ which specifies specific container image version to use as a parameter.

```dockerfile title="Dockerfile"
# 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.

```csharp title="AppHost.cs"
var builder = DistributedApplication.CreateBuilder(args);

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

var container = builder.AddDockerfile("myapp", "relative/context/path")
    .WithBuildSecret("ACCESS_TOKEN", accessToken);
```
```typescript title="apphost.ts"
import { createBuilder } from './.modules/aspire.js';

const builder = await createBuilder();

const accessToken = builder.addParameter("accesstoken", { secret: true });

const container = await 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:

```dockerfile title="Dockerfile"
# 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.