diff --git a/docs/core/containers/publish-configuration.md b/docs/core/containers/publish-configuration.md index ac8bf749ff2e4..235f339c4eb41 100644 --- a/docs/core/containers/publish-configuration.md +++ b/docs/core/containers/publish-configuration.md @@ -9,7 +9,7 @@ ms.date: 04/22/2025 In this reference article, you learn how to configure the container image generated when you publish a .NET app as a container. This article covers the various properties that you can set to control the image, the execution environment, and the commands that are run when the container starts. -## Configure container image +## Configure container properties You can control many aspects of the generated container through MSBuild properties. In general, if you can use a command in a _Dockerfile_ to set some configuration, you can do the same via MSBuild. @@ -18,45 +18,13 @@ You can control many aspects of the generated container through MSBuild properti There's no way of performing `RUN` commands with the .NET SDK. These commands are often used to install some OS packages or create a new OS user, or any number of arbitrary things. If you would like to keep using the .NET SDK container building feature, you can instead create a custom base image with these changes and then using this base image. For more information, see [`ContainerBaseImage`](#containerbaseimage). -### `ContainerArchiveOutputPath` - -To create a container image within a _tar.gz_ archive, use the `ContainerArchiveOutputPath` property. This feature is useful if your workflow isn't straightforward and requires that you, for example, run a scanning tool over your images before pushing them. Once the archive is created, you can move it, scan it, or load it into a local Docker toolchain. - -To publish to an archive, add the `ContainerArchiveOutputPath` property to your `dotnet publish` command, for example: - -```dotnetcli -dotnet publish \ - -p PublishProfile=DefaultContainer \ - -p ContainerArchiveOutputPath=./images/sdk-container-demo.tar.gz -``` - -You can specify either a folder name or a path with a specific file name. If you specify the folder name, the filename generated for the image archive file is named `$(ContainerRepository).tar.gz`. These archives can contain multiple tags inside them, only as single file is created for all `ContainerImageTags`. - -### Container image naming configuration - -Container images follow a specific naming convention. The name of the image is composed of several parts, the registry, optional port, repository, and optional tag and family. +## Flags that control the base image -```dockerfile -REGISTRY[:PORT]/REPOSITORY[:TAG[-FAMILY]] -``` +The following properties control which base image is used for your container and how it's selected: -For example, consider the fully qualified `mcr.microsoft.com/dotnet/runtime:8.0-alpine` image name: - -- `mcr.microsoft.com` is the registry (and in this case represents the Microsoft container registry). -- `dotnet/runtime` is the repository (but some consider this the `user/repository`). -- `8.0-alpine` is the tag and family (the family is an optional specifier that helps disambiguate OS packaging). - -Some properties described in the following sections correspond to managing parts of the generated image name. Consider the following table that maps the relationship between the image name and the build properties: - -| Image name part | MSBuild property | Example values | -| ----------------- | --------------------- | ----------------------- | -| `REGISTRY[:PORT]` | `ContainerRegistry` | `mcr.microsoft.com:443` | -| `PORT` | `ContainerPort` | `:443` | -| `REPOSITORY` | `ContainerRepository` | `dotnet/runtime` | -| `TAG` | `ContainerImageTag` | `8.0` | -| `FAMILY` | `ContainerFamily` | `-alpine` | - -The following sections describe the various properties that can be used to control the generated container image. +- [`ContainerBaseImage`](#containerbaseimage) +- [`ContainerFamily`](#containerfamily) +- [`ContainerRuntimeIdentifier(s)`](#containerruntimeidentifiers) ### `ContainerBaseImage` @@ -98,18 +66,6 @@ The preceding project configuration results in a final tag of `8.0-alpine` for a This field is free-form, and often can be used to select different operating system distributions, default package configurations, or any other _flavor_ of changes to a base image. This field is ignored when `ContainerBaseImage` is set. For more information, see [.NET container images](../docker/container-images.md). -### `ContainerPublishInParallel` - -For multi-RID containers, certain project types (like Blazor WebAssembly) may encounter build race conditions. To address this, starting with .NET SDK versions 8.0.408, 9.0.300, and 10.0, you can control the parallelism of the publish process using the `ContainerPublishInParallel` property. By default, publishing occurs in parallel for each Runtime Identifier (RID). Setting this property to `false` ensures sequential publishing, which increases stability but may take longer. - -```xml - - false - -``` - -For more information on multi-RID publishing, see [ContainerRuntimeIdentifier(s)](#containerruntimeidentifiers). - ### `ContainerRuntimeIdentifier(s)` The `ContainerRuntimeIdentifier` property specifies the OS and architecture for your container if the `ContainerBaseImage` supports multiple platforms. For example, the `mcr.microsoft.com/dotnet/runtime` image supports `linux-x64`, `linux-arm`, `linux-arm64`, and `win10-x64`. By default, this is set to the `RuntimeIdentifier` used when publishing the container. Typically, you don't need to set this property explicitly; instead, use the `-r` option with the `dotnet publish` command. If the chosen image doesn't support the specified `RuntimeIdentifier`, an error indicates the supported identifiers. @@ -131,49 +87,40 @@ To specify multiple container runtime identifiers for multi-architecture images, ``` > [!IMPORTANT] -> The `ContainerRuntimeIdentifiers` property must be a subset of the `RuntimeIdentifiers` property. If this condition isn't met, critical parts of the build pipeline may fail. +> The `ContainerRuntimeIdentifiers` property must be a subset of the `RuntimeIdentifiers` property. If this condition isn't met, critical parts of the build pipeline might fail. > > Setting multiple `ContainerRuntimeIdentifiers` results in a multi-architecture image being created. For more information, see [Multi-architecture images](#multi-architecture-images). For more information regarding the runtime identifiers supported by .NET, see [RID catalog](../rid-catalog.md). -### `ContainerRegistry` +#### Multi-architecture images -The container registry property controls the destination registry, the place that the newly created image is to be pushed to. By default it's pushed to the local Docker daemon, but you can also specify a remote registry. When using a remote registry that requires authentication, you authenticate using the well-known `docker login` mechanisms. For more information, See [authenticating to container registries](https://aka.ms/dotnet/containers/auth) for more details. For a concrete example of using this property, consider the following XML example: +Multi-architecture images enable a single container image to support multiple architectures, simplifying cross-platform development and deployment. The .NET SDK supports this through the `ContainerRuntimeIdentifiers` property. -```xml - - registry.mycorp.com:1234 - -``` +Beginning with SDK versions 8.0.405, 9.0.102, and 9.0.2xx, multi-RID container publishing is supported. When publishing with `/t:PublishContainer`: -This tooling supports publishing to any registry that supports the [Docker Registry HTTP API V2](https://distribution.github.io/distribution/spec/api/). This includes the following registries explicitly (and likely many more implicitly): +- If a single `RuntimeIdentifier` or `ContainerRuntimeIdentifier` is specified, a single-architecture container is generated as before. +- If no single `RuntimeIdentifier` is specified but multiple `RuntimeIdentifiers` or `ContainerRuntimeIdentifiers` are set, the SDK publishes the app for each specified RID and combines the resulting images into an [OCI Image Index](https://specs.opencontainers.org/image-spec/image-index/). This index allows multiple architecture-specific images to share a single name. -- [Azure Container Registry](https://azure.microsoft.com/products/container-registry) -- [Amazon Elastic Container Registry](https://aws.amazon.com/ecr/) -- [Google Artifact Registry](https://cloud.google.com/artifact-registry) -- [Docker Hub](https://hub.docker.com/) -- [GitHub Packages](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) -- [GitLab-hosted Container Registry](https://docs.gitlab.com/ee/user/packages/container_registry/) -- [Quay.io](https://quay.io/) +> [!NOTE] +> The `ContainerRuntimeIdentifiers` property must be a subset of the `RuntimeIdentifiers` property. For more information, see [ContainerRuntimeIdentifiers](#containerruntimeidentifiers). -For notes on working with these registries, see the [registry-specific notes](https://aka.ms/dotnet/containers/auth#notes-for-specific-registries). +This feature streamlines container workflows in mixed-architecture environments. For example, a developer on a `linux-x64` host can publish a container supporting both `linux-x64` and `linux-arm64`, enabling deployment to either architecture without changing image names or labels. -### `ContainerRepository` +The generated OCI Image Index is widely supported with modern container tooling, enhancing compatibility and ease of use. -The container repository is the name of the image itself, for example, `dotnet/runtime` or `my-app`. By default, the `AssemblyName` of the project is used. +## Flags that control generated-image-independent metadata -```xml - - my-app - -``` +The following properties control metadata and configuration that applies to the generated container image regardless of the target runtime identifier: -Image names consist of one or more slash-delimited segments, each of which can only contain lowercase alphanumeric characters, periods, underscores, and dashes, and must start with a letter or number. Any other characters result in an error being thrown. +- [`ContainerImageFormat`](#containerimageformat) +- [`ContainerImageTag`](#containerimagetag) +- [`ContainerLabel`](#containerlabel) +- [`ContainerRepository`](#containerrepository) ### `ContainerImageFormat` -Starting with .NET 8.0.405, you can use the `ContainerImageFormat` MSBuild property to specify the image format as either `Docker` or `OCI`. By default, the .NET tooling infers the format from the base image. For example, .NET base images use the Docker-specific format `application/vnd.docker.distribution.manifest.v2+json`. However, many modern tools prefer the OCI format `application/vnd.oci.image.manifest.v1+json`. To force a specific format, set the property as shown: +You can use the `ContainerImageFormat` MSBuild property to specify the image format as either `Docker` or `OCI`. By default, the .NET tooling infers the format from the base image. For example, .NET base images use the Docker-specific format `application/vnd.docker.distribution.manifest.v2+json`. However, many modern tools prefer the OCI format `application/vnd.oci.image.manifest.v1+json`. To force a specific format, set the property as shown: ```xml @@ -256,96 +203,31 @@ The `ContainerLabel` node has two attributes: For a list of labels that are created by default, see [default container labels](#default-container-labels). -## Configure container execution - -To control the execution of the container, you can use the following MSBuild properties. - -### `ContainerWorkingDirectory` - -The container working directory node controls the working directory of the container, the directory that commands are executed within if not other command is run. - -By default, the `/app` directory value is used as the working directory. - -```xml - - /bin - -``` - -### `ContainerPort` - -The container port adds Transmission Control Protocol (TCP) or User Datagram Protocol (UDP) ports to the list of known ports for the container. This enables container runtimes like Docker to map these ports to the host machine automatically. This is often used as documentation for the container, but can also be used to enable automatic port mapping. - -The `ContainerPort` node has two attributes: - -- `Include`: The port number to expose. -- `Type`: Defaults to `tcp`, valid values are either `tcp` or `udp`. - -```xml - - - -``` - -Starting with .NET 8, the `ContainerPort` is inferred when not explicitly provided based on several well-known ASP.NET environment variables: - -- `ASPNETCORE_URLS` -- `ASPNETCORE_HTTP_PORTS` -- `ASPNETCORE_HTTPS_PORTS` - -If these environment variables are present, their values are parsed and converted to TCP port mappings. These environment variables are read from your base image, if present, or from the environment variables defined in your project through `ContainerEnvironmentVariable` items. For more information, see [ContainerEnvironmentVariable](#containerenvironmentvariable). - -### `ContainerEnvironmentVariable` - -The container environment variable node allows you to add environment variables to the container. Environment variables are accessible to the app running in the container immediately, and are often used to change the run-time behavior of the running app. - -The `ContainerEnvironmentVariable` node has two attributes: - -- `Include`: The name of the environment variable. -- `Value`: The value of the environment variable. - -```xml - - - -``` - -For more information, see [.NET environment variables](../tools/dotnet-environment-variables.md). - -> [!NOTE] -> It's currently not possible to set environment variables from the .NET CLI when publishing a container image. For more information, see [GitHub: .NET SDK container builds](https://github.com/dotnet/sdk-container-builds/issues/451). - -### `LocalRegistry` - -The `LocalRegistry` MSBuild property specifies the local container tooling to use when pushing to local sources. Supported values are `docker` and `podman`. If not set, the SDK determines the tool based on availability: - -- If both `docker` and `podman` exist, and `docker` is an alias for `podman`, then `podman` is used. -- If only `docker` exists, `docker` is used. -- If only `podman` exists, `podman` is used. -- If neither exists, an error is thrown. +### `ContainerRepository` -To explicitly set the local registry tool, use the following configuration: +The container repository is the name of the image itself, for example, `dotnet/runtime` or `my-app`. By default, the `AssemblyName` of the project is used. ```xml - podman + my-app ``` -## Configure container commands - -By default, the container tools launch your app using either the generated AppHost binary for your app (if your app uses an AppHost), or the `dotnet` command plus your app's DLL. - -However, you can control how your app is executed by using some combination of `ContainerAppCommand`, `ContainerAppCommandArgs`, `ContainerDefaultArgs`, and `ContainerAppCommandInstruction`. +Image names consist of one or more slash-delimited segments, each of which can only contain lowercase alphanumeric characters, periods, underscores, and dashes, and must start with a letter or number. Any other characters result in an error being thrown. -These different configuration points exist because different base images use different combinations of the container `ENTRYPOINT` and `COMMAND` properties, and you want to be able to support all of them. The defaults should be useable for most apps, but if you want to customize your app launch behavior you should: +## Flags that control execution metadata -- Identify the binary to run and set it as `ContainerAppCommand` -- Identify which arguments are _required_ for your application to run and set them as `ContainerAppCommandArgs` -- Identify which arguments (if any) are _optional_ and are able to be overridden by a user and set them as `ContainerDefaultArgs` -- Set `ContainerAppCommandInstruction` to `DefaultArgs` +The following properties control runtime-specific execution behavior and multi-architecture image generation: -For more information, see the following configuration items. +- [`ContainerAppCommand`](#containerappcommand) +- [`ContainerAppCommandArgs`](#containerappcommandargs) +- [`ContainerAppCommandInstruction`](#containerappcommandinstruction) +- [`ContainerDefaultArgs`](#containerdefaultargs) +- [`ContainerEnvironmentVariable`](#containerenvironmentvariable) +- [`ContainerPort`](#containerport) +- [`ContainerPublishInParallel`](#containerpublishinparallel) +- [`ContainerUser`](#containeruser) +- [`ContainerWorkingDirectory`](#containerworkingdirectory) ### `ContainerAppCommand` @@ -383,6 +265,24 @@ The `ContainerAppCommandArgs` configuration has a single `Include` property, whi ``` +### `ContainerAppCommandInstruction` + +The app command instruction configuration helps control the way the `ContainerEntrypoint`, `ContainerEntrypointArgs`, `ContainerAppCommand`, `ContainerAppCommandArgs`, and `ContainerDefaultArgs` are combined to form the final command that is run in the container. This depends greatly on if an `ENTRYPOINT` is present in the base image. This property takes one of three values: `"DefaultArgs"`, `"Entrypoint"`, or `"None"`. + +- `Entrypoint`: + - In this mode, the entrypoint is defined by `ContainerAppCommand`, `ContainerAppCommandArgs`, and `ContainerDefaultArgs`. +- `None`: + - In this mode, the entrypoint is defined by `ContainerEntrypoint`, `ContainerEntrypointArgs`, and `ContainerDefaultArgs`. +- `DefaultArgs`: + - This is the most complex mode—if none of the `ContainerEntrypoint[Args]` items are present, the `ContainerAppCommand[Args]` and `ContainerDefaultArgs` are used to create the entrypoint and command. The base image entrypoint for base images that have it hard-coded to `dotnet` or `/usr/bin/dotnet` is skipped so that you have complete control. + - If both `ContainerEntrypoint` and `ContainerAppCommand` are present, then `ContainerEntrypoint` becomes the entrypoint, and `ContainerAppCommand` becomes the command. + +> [!NOTE] +> The `ContainerEntrypoint` and `ContainerEntrypointArgs` configuration items are deprecated as of .NET 8. + +> [!IMPORTANT] +> This is for advanced users-most apps shouldn't need to customize their entrypoint to this degree. For more information and if you'd like to provide use cases for your scenarios, see [GitHub: .NET SDK container builds discussions](https://github.com/dotnet/sdk-container-builds/discussions). + ### `ContainerDefaultArgs` This default args configuration item represents any user-overridable arguments for your app. This is a good way to provide defaults that your app might need to run in a way that makes it easy to start, yet still easy to customize. @@ -402,23 +302,60 @@ The `ContainerDefaultArgs` configuration has a single `Include` property, which ``` -### `ContainerAppCommandInstruction` +### `ContainerEnvironmentVariable` -The app command instruction configuration helps control the way the `ContainerEntrypoint`, `ContainerEntrypointArgs`, `ContainerAppCommand`, `ContainerAppCommandArgs`, and `ContainerDefaultArgs` are combined to form the final command that is run in the container. This depends greatly on if an `ENTRYPOINT` is present in the base image. This property takes one of three values: `"DefaultArgs"`, `"Entrypoint"`, or `"None"`. +The container environment variable node allows you to add environment variables to the container. Environment variables are accessible to the app running in the container immediately, and are often used to change the run-time behavior of the running app. -- `Entrypoint`: - - In this mode, the entrypoint is defined by `ContainerAppCommand`, `ContainerAppCommandArgs`, and `ContainerDefaultArgs`. -- `None`: - - In this mode, the entrypoint is defined by `ContainerEntrypoint`, `ContainerEntrypointArgs`, and `ContainerDefaultArgs`. -- `DefaultArgs`: - - This is the most complex mode—if none of the `ContainerEntrypoint[Args]` items are present, the `ContainerAppCommand[Args]` and `ContainerDefaultArgs` are used to create the entrypoint and command. The base image entrypoint for base images that have it hard-coded to `dotnet` or `/usr/bin/dotnet` is skipped so that you have complete control. - - If both `ContainerEntrypoint` and `ContainerAppCommand` are present, then `ContainerEntrypoint` becomes the entrypoint, and `ContainerAppCommand` becomes the command. +The `ContainerEnvironmentVariable` node has two attributes: + +- `Include`: The name of the environment variable. +- `Value`: The value of the environment variable. + +```xml + + + +``` + +For more information, see [.NET environment variables](../tools/dotnet-environment-variables.md). > [!NOTE] -> The `ContainerEntrypoint` and `ContainerEntrypointArgs` configuration items are deprecated as of .NET 8. +> It's currently not possible to set environment variables from the .NET CLI when publishing a container image. For more information, see [GitHub: .NET SDK container builds](https://github.com/dotnet/sdk-container-builds/issues/451). -> [!IMPORTANT] -> This is for advanced users-most apps shouldn't need to customize their entrypoint to this degree. For more information and if you'd like to provide use cases for your scenarios, see [GitHub: .NET SDK container builds discussions](https://github.com/dotnet/sdk-container-builds/discussions). +### `ContainerPort` + +The container port adds Transmission Control Protocol (TCP) or User Datagram Protocol (UDP) ports to the list of known ports for the container. This enables container runtimes like Docker to map these ports to the host machine automatically. This is often used as documentation for the container, but can also be used to enable automatic port mapping. + +The `ContainerPort` node has two attributes: + +- `Include`: The port number to expose. +- `Type`: Defaults to `tcp`, valid values are either `tcp` or `udp`. + +```xml + + + +``` + +Starting with .NET 8, the `ContainerPort` is inferred when not explicitly provided based on several well-known ASP.NET environment variables: + +- `ASPNETCORE_URLS` +- `ASPNETCORE_HTTP_PORTS` +- `ASPNETCORE_HTTPS_PORTS` + +If these environment variables are present, their values are parsed and converted to TCP port mappings. These environment variables are read from your base image, if present, or from the environment variables defined in your project through `ContainerEnvironmentVariable` items. For more information, see [ContainerEnvironmentVariable](#containerenvironmentvariable). + +### `ContainerPublishInParallel` + +For multi-RID containers, certain project types (like Blazor WebAssembly) might encounter build race conditions. To address this, starting with .NET SDK versions 8.0.408, 9.0.300, and 10.0, you can control the parallelism of the publish process using the `ContainerPublishInParallel` property. By default, publishing occurs in parallel for each Runtime Identifier (RID). Setting this property to `false` ensures sequential publishing, which increases stability but might take longer. + +```xml + + false + +``` + +For more information on multi-RID publishing, see [ContainerRuntimeIdentifier(s)](#containerruntimeidentifiers). ### `ContainerUser` @@ -458,43 +395,124 @@ Alternatively, you can set this value when calling `dotnet publish` from the com dotnet publish -p ContainerUser=root ``` -### Default container labels +### `ContainerWorkingDirectory` -Labels are often used to provide consistent metadata on container images. The built-in container tools provide some default labels to increase the quality of the generated images. All default label generation can be disabled by setting `ContainerGenerateLabels` to `false`. In addition, each default label has an individual enablement flag that can be set to `false` to disable that specific label. +The container working directory node controls the working directory of the container, the directory that commands are executed within if not other command is run. -Where possible, existing MSBuild properties provide the values for these labels. Other properties allow for explicit control of their values. +By default, the `/app` directory value is used as the working directory. -| Annotation | Default Value | Dedicated Property Name | Fallback Property Name | Enabled Property Name | Notes | -| ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | ---------------------------- | -------------------------- | ------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -| `org.opencontainers.image.created` and `org.opencontainers.artifact.created` | The [RFC 3339](https://tools.ietf.org/html/rfc3339#section-5.6) format of the current UTC DateTime | | | `ContainerGenerateLabelsImageCreated` | | -| `org.opencontainers.artifact.description` and `org.opencontainers.image.description` | | `ContainerDescription` | `Description` | `ContainerGenerateLabelsImageDescription` | | -| `org.opencontainers.image.authors` | | `ContainerAuthors` | `Authors` | `ContainerGenerateLabelsImageAuthors` | | -| `org.opencontainers.image.url` | | `ContainerInformationUrl` | `PackageProjectUrl` | `ContainerGenerateLabelsImageUrl` | | -| `org.opencontainers.image.documentation` | | `ContainerDocumentationUrl` | `PackageProjectUrl` | `ContainerGenerateLabelsImageDocumentation` | | -| `org.opencontainers.image.version` | | `ContainerVersion` | `PackageVersion` | `ContainerGenerateLabelsImageVersion` | | -| `org.opencontainers.image.vendor` | | `ContainerVendor` | | `ContainerGenerateLabelsImageVendor` | | -| `org.opencontainers.image.licenses` | | `ContainerLicenseExpression` | `PackageLicenseExpression` | `ContainerGenerateLabelsImageLicenses` | | -| `org.opencontainers.image.title` | | `ContainerTitle` | `Title` | `ContainerGenerateLabelsImageTitle` | | -| `org.opencontainers.image.base.name` | | `ContainerBaseImage` | | `ContainerGenerateLabelsImageBaseName` | | -| `org.opencontainers.image.base.digest` | | | | `ContainerGenerateLabelsImageBaseDigest` | This is the SHA digest of the chosen base image. Available from .NET SDK 9.0.100 onwards. | -| `org.opencontainers.image.source` | | `PrivateRepositoryUrl` | | `ContainerGenerateLabelsImageSource` | Only written if `PublishRepositoryUrl` is `true`. Also relies on Sourcelink infrastructure being part of the build. | -| `org.opencontainers.image.revision` | | `SourceRevisionId` | | `ContainerGenerateLabelsImageRevision` | Only written if `PublishRepositoryUrl` is `true`. Also relies on Sourcelink infrastructure being part of the build. | - -## Multi-architecture images +```xml + + /bin + +``` -Multi-architecture images enable a single container image to support multiple architectures, simplifying cross-platform development and deployment. The .NET SDK supports this through the `ContainerRuntimeIdentifiers` property. +## Flags that control the destination of the generated image -Beginning with SDK versions 8.0.405, 9.0.102, and 9.0.2xx, multi-RID container publishing is supported. When publishing with `/t:PublishContainer`: +The following properties control where the generated container image is stored or published: -- If a single `RuntimeIdentifier` or `ContainerRuntimeIdentifier` is specified, a single-architecture container is generated as before. -- If no single `RuntimeIdentifier` is specified but multiple `RuntimeIdentifiers` or `ContainerRuntimeIdentifiers` are set, the SDK publishes the app for each specified RID and combines the resulting images into an [OCI Image Index](https://specs.opencontainers.org/image-spec/image-index/). This index allows multiple architecture-specific images to share a single name. +- [`ContainerArchiveOutputPath`](#containerarchiveoutputpath) +- [`ContainerRegistry`](#containerregistry) +- [`LocalRegistry`](#localregistry) -> [!NOTE] -> The `ContainerRuntimeIdentifiers` property must be a subset of the `RuntimeIdentifiers` property. For more information, see [ContainerRuntimeIdentifiers](#containerruntimeidentifiers). +### `ContainerArchiveOutputPath` -This feature streamlines container workflows in mixed-architecture environments. For example, a developer on a `linux-x64` host can publish a container supporting both `linux-x64` and `linux-arm64`, enabling deployment to either architecture without changing image names or labels. +To create a container image within a _tar.gz_ archive, use the `ContainerArchiveOutputPath` property. This feature is useful if your workflow isn't straightforward and requires that you, for example, run a scanning tool over your images before pushing them. Once the archive is created, you can move it, scan it, or load it into a local Docker toolchain. -The generated OCI Image Index is widely supported with modern container tooling, enhancing compatibility and ease of use. +To publish to an archive, add the `ContainerArchiveOutputPath` property to your `dotnet publish` command, for example: + +```dotnetcli +dotnet publish \ + -p PublishProfile=DefaultContainer \ + -p ContainerArchiveOutputPath=./images/sdk-container-demo.tar.gz +``` + +You can specify either a folder name or a path with a specific file name. If you specify the folder name, the filename generated for the image archive file is named `$(ContainerRepository).tar.gz`. These archives can contain multiple tags inside them, only as single file is created for all `ContainerImageTags`. + +### `ContainerRegistry` + +The container registry property controls the destination registry, the place that the newly created image is to be pushed to. By default it's pushed to the local Docker daemon, but you can also specify a remote registry. When using a remote registry that requires authentication, you authenticate using the well-known `docker login` mechanisms. For more information, See [authenticating to container registries](https://aka.ms/dotnet/containers/auth) for more details. For a concrete example of using this property, consider the following XML example: + +```xml + + registry.mycorp.com:1234 + +``` + +This tooling supports publishing to any registry that supports the [Docker Registry HTTP API V2](https://distribution.github.io/distribution/spec/api/). This includes the following registries explicitly (and likely many more implicitly): + +- [Azure Container Registry](https://azure.microsoft.com/products/container-registry) +- [Amazon Elastic Container Registry](https://aws.amazon.com/ecr/) +- [Google Artifact Registry](https://cloud.google.com/artifact-registry) +- [Docker Hub](https://hub.docker.com/) +- [GitHub Packages](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) +- [GitLab-hosted Container Registry](https://docs.gitlab.com/ee/user/packages/container_registry/) +- [Quay.io](https://quay.io/) + +For notes on working with these registries, see the [registry-specific notes](https://aka.ms/dotnet/containers/auth#notes-for-specific-registries). + +### `LocalRegistry` + +The `LocalRegistry` MSBuild property specifies the local container tooling to use when pushing to local sources. Supported values are `docker` and `podman`. If not set, the SDK determines the tool based on availability: + +- If both `docker` and `podman` exist, and `docker` is an alias for `podman`, then `podman` is used. +- If only `docker` exists, `docker` is used. +- If only `podman` exists, `podman` is used. +- If neither exists, an error is thrown. + +To explicitly set the local registry tool, use the following configuration: + +```xml + + podman + +``` + +## Container image naming configuration + +Container images follow a specific naming convention. The name of the image is composed of several parts, the registry, optional port, repository, and optional tag and family. + +```dockerfile +REGISTRY[:PORT]/REPOSITORY[:TAG[-FAMILY]] +``` + +For example, consider the fully qualified `mcr.microsoft.com/dotnet/runtime:8.0-alpine` image name: + +- `mcr.microsoft.com` is the registry (and in this case represents the Microsoft container registry). +- `dotnet/runtime` is the repository (but some consider this the `user/repository`). +- `8.0-alpine` is the tag and family (the family is an optional specifier that helps disambiguate OS packaging). + +Some properties described in the following sections correspond to managing parts of the generated image name. Consider the following table that maps the relationship between the image name and the build properties: + +| Image name part | MSBuild property | Example values | +|-------------------|-----------------------------------------------|-------------------------| +| `REGISTRY[:PORT]` | [`ContainerRegistry`](#containerregistry) | `mcr.microsoft.com:443` | +| `PORT` | [`ContainerPort`](#containerport) | `:443` | +| `REPOSITORY` | [`ContainerRepository`](#containerrepository) | `dotnet/runtime` | +| `TAG` | [`ContainerImageTag`](#containerimagetag) | `8.0` | +| `FAMILY` | [`ContainerFamily`](#containerfamily) | `-alpine` | + +## Default container labels + +Labels are often used to provide consistent metadata on container images. The built-in container tools provide some default labels to increase the quality of the generated images. All default label generation can be disabled by setting `ContainerGenerateLabels` to `false`. In addition, each default label has an individual enablement flag that can be set to `false` to disable that specific label. + +Where possible, existing MSBuild properties provide the values for these labels. Other properties allow for explicit control of their values. + +| Annotation | Default value | Dedicated property name | Fallback property name | Enabled property name | Notes | +|------------|---------------|-------------------------|------------------------|-----------------------|-------| +| `org.opencontainers.image.created` and `org.opencontainers.artifact.created` | The [RFC 3339](https://tools.ietf.org/html/rfc3339#section-5.6) format of the current UTC DateTime | | | `ContainerGenerateLabelsImageCreated` | | +| `org.opencontainers.artifact.description` and `org.opencontainers.image.description` | | `ContainerDescription` | `Description` | `ContainerGenerateLabelsImageDescription` | | +| `org.opencontainers.image.authors` | | `ContainerAuthors` | `Authors` | `ContainerGenerateLabelsImageAuthors` | | +| `org.opencontainers.image.url` | | `ContainerInformationUrl` | `PackageProjectUrl` | `ContainerGenerateLabelsImageUrl` | | +| `org.opencontainers.image.documentation` | | `ContainerDocumentationUrl` | `PackageProjectUrl` | `ContainerGenerateLabelsImageDocumentation` | | +| `org.opencontainers.image.version` | | `ContainerVersion` | `PackageVersion` | `ContainerGenerateLabelsImageVersion` | | +| `org.opencontainers.image.vendor` | | `ContainerVendor` | | `ContainerGenerateLabelsImageVendor` | | +| `org.opencontainers.image.licenses` | | `ContainerLicenseExpression` | `PackageLicenseExpression` | `ContainerGenerateLabelsImageLicenses` | | +| `org.opencontainers.image.title` | | `ContainerTitle` | `Title` | `ContainerGenerateLabelsImageTitle` | | +| `org.opencontainers.image.base.name` | | `ContainerBaseImage` | |`ContainerGenerateLabelsImageBaseName` | | +| `org.opencontainers.image.base.digest` | | | | `ContainerGenerateLabelsImageBaseDigest` | This is the SHA digest of the chosen base image. Available from .NET SDK 9.0.100 onwards. | +| `org.opencontainers.image.source` | | `PrivateRepositoryUrl` | | `ContainerGenerateLabelsImageSource` | Only written if `PublishRepositoryUrl` is `true`. Also relies on Sourcelink infrastructure being part of the build. | +| `org.opencontainers.image.revision` | | `SourceRevisionId` | | `ContainerGenerateLabelsImageRevision` | Only written if `PublishRepositoryUrl` is `true`. Also relies on Sourcelink infrastructure being part of the build. | ## See also