RustHostingExtensions Methods
Hosting.IDistributedApplicationBuilder. AddRustApp(IDistributedApplicationBuilder, string, string)Section titled AddRustApp(IDistributedApplicationBuilder, string, string)extensionIResourceBuilder<RustAppResource>public static class RustHostingExtensions{ public static IResourceBuilder<RustAppResource> AddRustApp( this IDistributedApplicationBuilder builder, string name, string appDirectory) { // ... }}Parameters
builderIDistributedApplicationBuilderThe Hosting.IDistributedApplicationBuilder to add the resource to.namestringThe name of the resource.appDirectorystringThe working directory for cargo and the Docker build context used when publishing.Returns
IResourceBuilder<RustAppResource>A reference to the ApplicationModel.IResourceBuilder`1.Remarks
The resource runs cargo run in appDirectory. Cargo discovers the manifest from that directory by default; use WithCargoManifestPath to select another manifest. Cargo requires the two kinds of argument to be separated by --, so they are configured separately: WithCargoArgs adds arguments for cargo itself (before the separator) and WithArgs adds arguments for the application (after it).
Debugging is wired up automatically. In VS Code the resource is built with cargo build and the resulting binary is launched under a native debugger, so the cargo arguments are applied to the build rather than to cargo run.
Aspire configures the OTLP endpoint and development certificate environment variables. The Rust application must still enable the transport and TLS features required by its OpenTelemetry SDK and load native trust roots when using the development certificate. Rust does not read a port from the environment on its own, so bind to the port named by WithHttpEndpoint(env: ...) rather than a hard-coded one.
When publishing, a multi-stage Dockerfile is generated that builds the crate inside the container; the crate is never compiled on the host. If the app directory already contains a Dockerfile, that file is used instead. Call WithDockerfileBaseImage once with both arguments to override the build and runtime base images together; each call replaces the previous image configuration.
Examples
Add a Rust application to the AppHost and expose an HTTP endpoint:
var builder = DistributedApplication.CreateBuilder(args);
builder.AddRustApp("api", "../rust-api") .WithHttpEndpoint(env: "PORT") .WithCargoReleaseBuild();
builder.Build().Run();WithCargoArgs(IResourceBuilder<T>, string[])Section titled WithCargoArgs(IResourceBuilder<T>, string[])extensionIResourceBuilder<T>public static class RustHostingExtensions{ public static IResourceBuilder<T> WithCargoArgs<T>( this IResourceBuilder<T> builder, params string[] args) { // ... }}Parameters
builderIResourceBuilder<T>The resource builder.argsstring[]The cargo arguments to append before --.Returns
IResourceBuilder<T>A reference to the ApplicationModel.IResourceBuilder`1 for chaining.Remarks
WithCargo* options alone, so a target selection that has a dedicated method — WithCargoBinTarget, WithCargoExample, WithCargoPackage, WithCargoProfile, WithCargoReleaseBuild and WithCargoTarget — has to go through it rather than being passed here. WithCargoArgs(IResourceBuilder<T>, Action<RustCargoArgsCallbackContext>)Section titled WithCargoArgs(IResourceBuilder<T>, Action<RustCargoArgsCallbackContext>)extensionIResourceBuilder<T>public static class RustHostingExtensions{ public static IResourceBuilder<T> WithCargoArgs<T>( this IResourceBuilder<T> builder, Action<RustCargoArgsCallbackContext> callback) { // ... }}Parameters
builderIResourceBuilder<T>The resource builder.callbackAction<RustCargoArgsCallbackContext>A callback that computes cargo arguments at execution time.Returns
IResourceBuilder<T>A reference to the ApplicationModel.IResourceBuilder`1 for chaining.Remarks
WithCargoArgs(IResourceBuilder<T>, Func<RustCargoArgsCallbackContext, Task>)Section titled WithCargoArgs(IResourceBuilder<T>, Func<RustCargoArgsCallbackContext, Task>)extensionIResourceBuilder<T>public static class RustHostingExtensions{ public static IResourceBuilder<T> WithCargoArgs<T>( this IResourceBuilder<T> builder, Func<RustCargoArgsCallbackContext, Task> callback) { // ... }}Parameters
builderIResourceBuilder<T>The resource builder.callbackFunc<RustCargoArgsCallbackContext, Task>A callback that computes cargo arguments at execution time.Returns
IResourceBuilder<T>A reference to the ApplicationModel.IResourceBuilder`1 for chaining.Remarks
WithCargoBinTarget(IResourceBuilder<T>, string)Section titled WithCargoBinTarget(IResourceBuilder<T>, string)extensionIResourceBuilder<T>public static class RustHostingExtensions{ public static IResourceBuilder<T> WithCargoBinTarget<T>( this IResourceBuilder<T> builder, string binName) { // ... }}Parameters
builderIResourceBuilder<T>The resource builder.binNamestringThe binary target name, as declared by [[bin]] name in Cargo.toml.Returns
IResourceBuilder<T>A reference to the ApplicationModel.IResourceBuilder`1 for chaining.Remarks
--bin. Debugging and publishing also use it to work out which file cargo produces, so a package with several binaries must select one here (or set default-run in Cargo.toml). WithCargoExample(IResourceBuilder<T>, string)Section titled WithCargoExample(IResourceBuilder<T>, string)extensionIResourceBuilder<T>public static class RustHostingExtensions{ public static IResourceBuilder<T> WithCargoExample<T>( this IResourceBuilder<T> builder, string exampleName) { // ... }}Parameters
builderIResourceBuilder<T>The resource builder.exampleNamestringThe example name, as declared by a file or directory under examples/.Returns
IResourceBuilder<T>A reference to the ApplicationModel.IResourceBuilder`1 for chaining.Remarks
--example. Cargo writes examples to target/<profile>/examples/, and debugging and publishing both follow that layout. WithCargoFeatures(IResourceBuilder<T>, string[])Section titled WithCargoFeatures(IResourceBuilder<T>, string[])extensionIResourceBuilder<T>public static class RustHostingExtensions{ public static IResourceBuilder<T> WithCargoFeatures<T>( this IResourceBuilder<T> builder, params string[] features) { // ... }}Parameters
builderIResourceBuilder<T>The resource builder.featuresstring[]The features to enable.Returns
IResourceBuilder<T>A reference to the ApplicationModel.IResourceBuilder`1 for chaining.Remarks
WithCargoLocked(IResourceBuilder<T>, bool)Section titled WithCargoLocked(IResourceBuilder<T>, bool)extensionIResourceBuilder<T>Cargo.lock. public static class RustHostingExtensions{ public static IResourceBuilder<T> WithCargoLocked<T>( this IResourceBuilder<T> builder, bool locked = true) { // ... }}Parameters
builderIResourceBuilder<T>The resource builder.lockedbooloptionaltrue to add --locked; otherwise false.Returns
IResourceBuilder<T>A reference to the ApplicationModel.IResourceBuilder`1 for chaining.Remarks
--locked, which fails the build rather than updating Cargo.lock. Publishing already adds this whenever the crate has a lock file, so a published image cannot silently pick up dependency versions that were never committed; pass false to opt out. See https://doc.rust-lang.org/cargo/commands/cargo-build.html#manifest-options WithCargoManifestPath(IResourceBuilder<T>, string)Section titled WithCargoManifestPath(IResourceBuilder<T>, string)extensionIResourceBuilder<T>Cargo.toml cargo builds from. public static class RustHostingExtensions{ public static IResourceBuilder<T> WithCargoManifestPath<T>( this IResourceBuilder<T> builder, string manifestPath) { // ... }}Parameters
builderIResourceBuilder<T>The resource builder.manifestPathstringThe path to the manifest, absolute or relative to the app directory.Returns
IResourceBuilder<T>A reference to the ApplicationModel.IResourceBuilder`1 for chaining.Remarks
--manifest-path. Cargo otherwise discovers the manifest by searching upwards from the app directory, which is what most apps want, so this is only needed to point at a manifest somewhere else — for example the crate of one workspace member when the app directory is the workspace root. Publishing copies the app directory into the container image and rewrites the manifest path to match, so the manifest has to live inside the app directory and the path has to be relative to it. An absolute path is accepted when running and rejected when publishing.
WithCargoPackage(IResourceBuilder<T>, string)Section titled WithCargoPackage(IResourceBuilder<T>, string)extensionIResourceBuilder<T>public static class RustHostingExtensions{ public static IResourceBuilder<T> WithCargoPackage<T>( this IResourceBuilder<T> builder, string packageName) { // ... }}Parameters
builderIResourceBuilder<T>The resource builder.packageNamestringThe cargo package name.Returns
IResourceBuilder<T>A reference to the ApplicationModel.IResourceBuilder`1 for chaining.Remarks
--package. Required when the crate directory is a workspace whose default members include more than one package with a binary target, because the binary to run would otherwise be ambiguous. Library-only members are ignored, so an app crate beside library crates needs nothing. WithCargoProfile(IResourceBuilder<T>, string)Section titled WithCargoProfile(IResourceBuilder<T>, string)extensionIResourceBuilder<T>public static class RustHostingExtensions{ public static IResourceBuilder<T> WithCargoProfile<T>( this IResourceBuilder<T> builder, string profileName) { // ... }}Parameters
builderIResourceBuilder<T>The resource builder.profileNamestringThe profile name, for example dev, release, or a custom profile.Returns
IResourceBuilder<T>A reference to the ApplicationModel.IResourceBuilder`1 for chaining.Remarks
--profile, which takes precedence over WithCargoReleaseBuild because cargo rejects --profile and --release together. WithCargoReleaseBuild(IResourceBuilder<T>, bool)Section titled WithCargoReleaseBuild(IResourceBuilder<T>, bool)extensionIResourceBuilder<T>public static class RustHostingExtensions{ public static IResourceBuilder<T> WithCargoReleaseBuild<T>( this IResourceBuilder<T> builder, bool releaseBuild = true) { // ... }}Parameters
builderIResourceBuilder<T>The resource builder.releaseBuildbooloptionaltrue to add --release; otherwise false.Returns
IResourceBuilder<T>A reference to the ApplicationModel.IResourceBuilder`1 for chaining.Remarks
false to opt a published image out of --release. WithCargoTarget(IResourceBuilder<T>, string)Section titled WithCargoTarget(IResourceBuilder<T>, string)extensionIResourceBuilder<T>public static class RustHostingExtensions{ public static IResourceBuilder<T> WithCargoTarget<T>( this IResourceBuilder<T> builder, string target) { // ... }}Parameters
builderIResourceBuilder<T>The resource builder.targetstringThe target triple, for example x86_64-unknown-linux-musl.Returns
IResourceBuilder<T>A reference to the ApplicationModel.IResourceBuilder`1 for chaining.Remarks
--target. Cargo writes a cross-compiled binary to target/<triple>/<profile>/, and the generated Dockerfile follows that layout and adds the target's standard library to the build image with rustup target add. Aspire-generated Dockerfiles map native Linux x86_64, aarch64, 32-bit ARM, and 32-bit x86 targets to Docker Linux platforms. Docker's linux/arm platform represents the ARMv7 variant. The default build and runtime images support x86_64 and aarch64 musl targets. A 32-bit musl target needs a custom build image but can use the default runtime image. Other ABIs require custom build and runtime images configured together in one WithDockerfileBaseImage call because later calls replace the previous image configuration.
Custom images opt out of default-image compatibility checks, but the target must still map to a supported native Docker Linux platform. A custom build image must already contain any linker or native dependencies the target needs; WithDockerfileBaseImage changes images but does not install cross-compilation tooling. Other targets require an authored Dockerfile for publishing.