Salta ai contenuti

Container files

Questi contenuti non sono ancora disponibili nella tua lingua.

Aspire provides APIs to inject files and directories into containers, enabling you to configure containerized resources with custom configuration files, scripts, certificates, and other assets. There are two complementary APIs:

  • WithContainerFiles: Injects files into containers at development time when they start during aspire run.
  • PublishWithContainerFiles: Copies files from one resource’s container into another resource’s container as build artifacts at publish time during aspire publish.

The WithContainerFiles extension method creates or updates files and directories inside a container at a specified destination path. It supports three approaches depending on your needs: inline entries for declarative file definitions, a source path for copying from the host file system, and an async callback for dynamic file generation.

Use the inline entries overload to declaratively define files and directories using ContainerFileSystemItem objects. This is useful when file contents are known at build time or can be expressed as string literals.

AppHost.cs
var builder = DistributedApplication.CreateBuilder(args);
builder.AddContainer("myapp", "myapp:latest")
.WithContainerFiles("/app/config", [
new ContainerFile
{
Name = "appsettings.json",
Contents = """
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
}
}
"""
},
new ContainerDirectory
{
Name = "scripts",
Entries = [
new ContainerFile
{
Name = "init.sh",
Contents = "#!/bin/bash\necho 'Initializing...'",
Mode = UnixFileMode.UserRead
| UnixFileMode.UserWrite
| UnixFileMode.UserExecute
}
]
}
]);
builder.Build().Run();

In the preceding example:

  • A JSON configuration file is created at /app/config/appsettings.json with the specified contents.
  • A nested scripts directory is created at /app/config/scripts/ containing an executable shell script.

Use the source path overload to copy files from a directory on the host machine into the container. This is useful when you have existing configuration files or assets on disk.

AppHost.cs
var builder = DistributedApplication.CreateBuilder(args);
builder.AddContainer("myapp", "myapp:latest")
.WithContainerFiles("/app/config", "./config-files");
builder.Build().Run();

Unless the source path is a rooted (absolute) path, it’s interpreted as relative to the AppHost project directory. All files in the source directory are copied to the destination path in the container at startup.

Use the callback overload to generate files dynamically when the container starts. The callback receives a ContainerFileSystemCallbackContext that provides access to the IServiceProvider and the resource’s IResource model, enabling you to resolve services or inspect the app model.

AppHost.cs
var builder = DistributedApplication.CreateBuilder(args);
builder.AddContainer("worker", "worker:latest")
.WithContainerFiles("/app/config",
async (context, cancellationToken) =>
{
var config = new
{
MachineName = Environment.MachineName,
Timestamp = DateTime.UtcNow
};
return
[
new ContainerFile
{
Name = "runtime-config.json",
Contents = JsonSerializer.Serialize(config)
}
];
});
builder.Build().Run();

The callback is invoked each time the container starts, so the generated files always reflect the current state.

The WithContainerFiles API uses a type hierarchy rooted at the abstract ContainerFileSystemItem class. Each type represents a different kind of file system entry.

ContainerFile represents a standard file. Set either Contents (a string) or SourcePath (an absolute path on the host) to provide the file data — the two are mutually exclusive.

AppHost.cs
// File with inline contents
var configYaml = new ContainerFile
{
Name = "config.yaml",
Contents = "key: value"
};
// File sourced from the host file system
var dataCsv = new ContainerFile
{
Name = "data.csv",
SourcePath = "/path/to/data.csv"
};

Set ContinueOnError to true to allow the container to start even if creating this particular file fails:

AppHost.cs
var optionalJson = new ContainerFile
{
Name = "optional-config.json",
Contents = "{}",
ContinueOnError = true
};

ContainerDirectory represents a directory that can contain nested ContainerFileSystemItem entries, allowing you to build arbitrary directory trees.

AppHost.cs
var certsDir = new ContainerDirectory
{
Name = "certs",
Entries = [
new ContainerFile
{
Name = "ca.pem",
SourcePath = "/path/to/ca.pem"
},
new ContainerDirectory
{
Name = "private",
Entries = [
new ContainerFile
{
Name = "server.key",
SourcePath = "/path/to/server.key",
Mode = UnixFileMode.UserRead
}
]
}
]
};

You can also populate a ContainerDirectory from files on disk using the static GetFileSystemItemsFromPath method:

AppHost.cs
var assetsDir = new ContainerDirectory
{
Name = "assets",
Entries = ContainerDirectory.GetFileSystemItemsFromPath(
"/path/to/assets",
searchOptions: SearchOption.AllDirectories)
};

ContainerOpenSSLCertificateFile represents a PEM-encoded public certificate. In addition to placing the certificate file in the container, Aspire automatically creates an OpenSSL-compatible symlink ([subject hash].[n]) in the same directory — equivalent to running openssl rehash. This enables containers that use OpenSSL for certificate validation to discover the certificate automatically.

AppHost.cs
builder.AddContainer("myapp", "myapp:latest")
.WithContainerFiles("/certs", [
new ContainerOpenSSLCertificateFile
{
Name = "ca-cert.pem",
Contents = pemCertificateString
}
]);

All WithContainerFiles overloads accept optional parameters to control file ownership and permissions.

The defaultOwner and defaultGroup parameters set the default UID and GID applied to all created files and directories. Both default to 0 (root) when not specified. You can override ownership on individual items using the Owner and Group properties on any ContainerFileSystemItem.

AppHost.cs
builder.AddContainer("myapp", "myapp:latest")
.WithContainerFiles("/app/data",
[
new ContainerFile
{
Name = "shared.txt",
Contents = "shared data"
},
new ContainerFile
{
Name = "user-only.txt",
Contents = "private data",
Owner = 1000,
Group = 1000
}
],
defaultOwner: 33, // www-data
defaultGroup: 33);

In this example, shared.txt inherits the default owner/group of 33, while user-only.txt overrides with UID/GID 1000.

The umask parameter controls default permissions by subtracting (masking) permission bits from the base defaults. Without an explicit Mode set on an item:

  • Directories start with 0777 (read/write/execute for all) and have the umask subtracted
  • Files start with 0666 (read/write for all) and have the umask subtracted

The default umask is 0022, which results in:

  • Directories: 0755 (owner: rwx, group: r-x, others: r-x)
  • Files: 0644 (owner: rw-, group: r—, others: r—)

You can set Mode directly on individual items to override the umask-based default:

AppHost.cs
builder.AddContainer("myapp", "myapp:latest")
.WithContainerFiles("/app/scripts",
[
new ContainerFile
{
Name = "run.sh",
Contents = "#!/bin/bash\necho 'Running'",
Mode = UnixFileMode.UserRead
| UnixFileMode.UserWrite
| UnixFileMode.UserExecute
}
],
umask: UnixFileMode.OtherRead
| UnixFileMode.OtherWrite
| UnixFileMode.OtherExecute);

For containers with ContainerLifetime.Persistent, changing the contents of container file entries causes the container to be recreated. Ensure any data written through WithContainerFiles is idempotent for a given app model configuration to avoid unintended container restarts.

The PublishWithContainerFiles method copies files from one resource’s container into another resource’s container during aspire publish. This is the preferred approach for injecting files into containers at publish time.

A key use case is embedding single-page application (SPA) or static JavaScript frontends into a reverse proxy or web server container for production deployment. During development, frontend apps like Vite or React typically run as standalone dev servers. In production, however, the compiled static assets are often served by a backend API or a dedicated web server like Nginx. PublishWithContainerFiles bridges this gap by copying the built frontend output into the serving container as part of the publish process — no manual file copying or multi-stage Dockerfile required.

AppHost.cs
var builder = DistributedApplication.CreateBuilder(args);
var frontend = builder.AddViteApp("frontend", "../frontend");
var api = builder.AddProject<Projects.Api>("api")
.PublishWithContainerFiles(frontend, "./wwwroot");
builder.Build().Run();

In this example:

  1. The frontend resource builds inside its container, producing compiled JavaScript, CSS, and HTML.
  2. During publish, Aspire copies those files from the frontend container into the api container at ./wwwroot.
  3. The resulting api container includes both the API code and the frontend static assets, ready to serve the full application.

You can also embed frontend assets into a dedicated reverse proxy container:

AppHost.cs
var builder = DistributedApplication.CreateBuilder(args);
var frontend = builder.AddViteApp("frontend", "../frontend");
var nginx = builder.AddYarp("gateway")
.PublishWithStaticFiles(frontend);
builder.Build().Run();

This produces a self-contained Nginx container that serves the frontend application, with no external volume mounts or runtime file copying needed.

PublishWithContainerFiles only applies in publish mode — it has no effect during aspire run. The destination resource must implement IContainerFilesDestinationResource (such as ProjectResource), and the source resource must implement IResourceWithContainerFiles.

By default, the source resource exports files from its container based on its configured output paths. Use WithContainerFilesSource to specify which path inside the source container to copy from:

AppHost.cs
var frontend = builder.AddViteApp("frontend", "../frontend")
.WithContainerFilesSource("/app/dist");
var api = builder.AddProject<Projects.Api>("api")
.PublishWithContainerFiles(frontend, "./wwwroot");

Use ClearContainerFilesSources to remove any previously configured source paths before adding new ones.