diff --git a/src/Aspire.Hosting/ApplicationModel/OtlpExporterAnnotation.cs b/src/Aspire.Hosting/ApplicationModel/OtlpExporterAnnotation.cs
index c2698c7f188..abe67a95231 100644
--- a/src/Aspire.Hosting/ApplicationModel/OtlpExporterAnnotation.cs
+++ b/src/Aspire.Hosting/ApplicationModel/OtlpExporterAnnotation.cs
@@ -11,4 +11,8 @@ namespace Aspire.Hosting.ApplicationModel;
 [DebuggerDisplay("Type = {GetType().Name,nq}")]
 public class OtlpExporterAnnotation : IResourceAnnotation
 {
-}
\ No newline at end of file
+    /// 
+    /// Gets or sets the default protocol for the OTLP exporter.
+    /// 
+    public OtlpProtocol? RequiredProtocol { get; init; }
+}
diff --git a/src/Aspire.Hosting/OtlpConfigurationExtensions.cs b/src/Aspire.Hosting/OtlpConfigurationExtensions.cs
index 024cf9407fb..1c3be620536 100644
--- a/src/Aspire.Hosting/OtlpConfigurationExtensions.cs
+++ b/src/Aspire.Hosting/OtlpConfigurationExtensions.cs
@@ -32,6 +32,30 @@ public static void AddOtlpEnvironment(IResource resource, IConfiguration configu
         // Add annotation to mark this resource as having OTLP exporter configured
         resource.Annotations.Add(new OtlpExporterAnnotation());
 
+        RegisterOtlpEnvironment(resource, configuration, environment);
+    }
+
+    /// 
+    /// Configures OpenTelemetry in projects using environment variables.
+    /// 
+    /// The resource to add annotations to.
+    /// The configuration to use for the OTLP exporter endpoint URL.
+    /// The host environment to check if the application is running in development mode.
+    /// The protocol to use for the OTLP exporter. If not set, it will try gRPC then Http.
+    public static void AddOtlpEnvironment(IResource resource, IConfiguration configuration, IHostEnvironment environment, OtlpProtocol protocol)
+    {
+        ArgumentNullException.ThrowIfNull(resource);
+        ArgumentNullException.ThrowIfNull(configuration);
+        ArgumentNullException.ThrowIfNull(environment);
+
+        // Add annotation to mark this resource as having OTLP exporter configured with a required protocol
+        resource.Annotations.Add(new OtlpExporterAnnotation { RequiredProtocol = protocol });
+
+        RegisterOtlpEnvironment(resource, configuration, environment);
+    }
+
+    private static void RegisterOtlpEnvironment(IResource resource, IConfiguration configuration, IHostEnvironment environment)
+    {
         // Configure OpenTelemetry in projects using environment variables.
         // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/configuration/sdk-environment-variables.md
 
@@ -43,26 +67,13 @@ public static void AddOtlpEnvironment(IResource resource, IConfiguration configu
                 return;
             }
 
-            var dashboardOtlpGrpcUrl = configuration.GetString(KnownConfigNames.DashboardOtlpGrpcEndpointUrl, KnownConfigNames.Legacy.DashboardOtlpGrpcEndpointUrl);
-            var dashboardOtlpHttpUrl = configuration.GetString(KnownConfigNames.DashboardOtlpHttpEndpointUrl, KnownConfigNames.Legacy.DashboardOtlpHttpEndpointUrl);
-
-            // The dashboard can support OTLP/gRPC and OTLP/HTTP endpoints at the same time, but it can
-            // only tell resources about one of the endpoints via environment variables.
-            // If both OTLP/gRPC and OTLP/HTTP are available then prefer gRPC.
-            if (dashboardOtlpGrpcUrl != null)
-            {
-                SetOtelEndpointAndProtocol(context.EnvironmentVariables, dashboardOtlpGrpcUrl, "grpc");
-            }
-            else if (dashboardOtlpHttpUrl != null)
+            if (!resource.TryGetLastAnnotation(out var otlpExporterAnnotation))
             {
-                SetOtelEndpointAndProtocol(context.EnvironmentVariables, dashboardOtlpHttpUrl, "http/protobuf");
-            }
-            else
-            {
-                // No endpoints provided to host. Use default value for URL.
-                SetOtelEndpointAndProtocol(context.EnvironmentVariables, DashboardOtlpUrlDefaultValue, "grpc");
+                return;
             }
 
+            SetOtel(context, configuration, otlpExporterAnnotation.RequiredProtocol);
+
             // Set the service name and instance id to the resource name and UID. Values are injected by DCP.
             var dcpDependencyCheckService = context.ExecutionContext.ServiceProvider.GetRequiredService();
             var dcpInfo = await dcpDependencyCheckService.GetDcpInfoAsync(cancellationToken: context.CancellationToken).ConfigureAwait(false);
@@ -91,6 +102,42 @@ public static void AddOtlpEnvironment(IResource resource, IConfiguration configu
             }
         }));
 
+        static void SetOtel(EnvironmentCallbackContext context, IConfiguration configuration, OtlpProtocol? requiredProtocol)
+        {
+            var dashboardOtlpGrpcUrl = configuration.GetString(KnownConfigNames.DashboardOtlpGrpcEndpointUrl, KnownConfigNames.Legacy.DashboardOtlpGrpcEndpointUrl);
+            var dashboardOtlpHttpUrl = configuration.GetString(KnownConfigNames.DashboardOtlpHttpEndpointUrl, KnownConfigNames.Legacy.DashboardOtlpHttpEndpointUrl);
+
+            // Check if a specific protocol is required by the annotation
+            if (requiredProtocol is OtlpProtocol.Grpc)
+            {
+                SetOtelEndpointAndProtocol(context.EnvironmentVariables, dashboardOtlpGrpcUrl ?? DashboardOtlpUrlDefaultValue, "grpc");
+            }
+            else if (requiredProtocol is OtlpProtocol.HttpProtobuf)
+            {
+                SetOtelEndpointAndProtocol(context.EnvironmentVariables, dashboardOtlpHttpUrl ?? throw new InvalidOperationException("OtlpExporter is configured to require http/protobuf, but no endpoint was configured for ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL"), "http/protobuf");
+            }
+            else
+            {
+                // No specific protocol required, use the existing preference logic
+                // The dashboard can support OTLP/gRPC and OTLP/HTTP endpoints at the same time, but it can
+                // only tell resources about one of the endpoints via environment variables.
+                // If both OTLP/gRPC and OTLP/HTTP are available then prefer gRPC.
+                if (dashboardOtlpGrpcUrl is not null)
+                {
+                    SetOtelEndpointAndProtocol(context.EnvironmentVariables, dashboardOtlpGrpcUrl, "grpc");
+                }
+                else if (dashboardOtlpHttpUrl is not null)
+                {
+                    SetOtelEndpointAndProtocol(context.EnvironmentVariables, dashboardOtlpHttpUrl, "http/protobuf");
+                }
+                else
+                {
+                    // No endpoints provided to host. Use default value for URL.
+                    SetOtelEndpointAndProtocol(context.EnvironmentVariables, DashboardOtlpUrlDefaultValue, "grpc");
+                }
+            }
+        }
+
         static void SetOtelEndpointAndProtocol(Dictionary environmentVariables, string url, string protocol)
         {
             environmentVariables["OTEL_EXPORTER_OTLP_ENDPOINT"] = new HostUrl(url);
@@ -117,4 +164,25 @@ public static IResourceBuilder WithOtlpExporter(this IResourceBuilder b
         
         return builder;
     }
+
+    /// 
+    /// Injects the appropriate environment variables to allow the resource to enable sending telemetry to the dashboard.
+    /// 
+    ///   - It sets the OTLP endpoint to the value of the ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL environment variable.
 
+    ///   - It sets the service name and instance id to the resource name and UID. Values are injected by the orchestrator.
 
+    ///   - It sets a small batch schedule delay in development. This reduces the delay that OTLP exporter waits to sends telemetry and makes the dashboard telemetry pages responsive.
 
+    /// 
+    /// 
+    /// The resource type.
+    /// The resource builder.
+    /// The protocol to use for the OTLP exporter. If not set, it will try gRPC then Http.
+    /// The .
+    public static IResourceBuilder WithOtlpExporter(this IResourceBuilder builder, OtlpProtocol protocol) where T : IResourceWithEnvironment
+    {
+        ArgumentNullException.ThrowIfNull(builder);
+
+        AddOtlpEnvironment(builder.Resource, builder.ApplicationBuilder.Configuration, builder.ApplicationBuilder.Environment, protocol);
+
+        return builder;
+    }
 }
diff --git a/src/Aspire.Hosting/OtlpProtocol.cs b/src/Aspire.Hosting/OtlpProtocol.cs
new file mode 100644
index 00000000000..1f02e7b7251
--- /dev/null
+++ b/src/Aspire.Hosting/OtlpProtocol.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Aspire.Hosting;
+
+/// 
+/// Protocols available for OTLP exporters.
+/// 
+public enum OtlpProtocol
+{
+    /// 
+    /// A gRPC-based OTLP exporter.
+    /// 
+    Grpc,
+
+    /// 
+    /// Http/Protobuf-based OTLP exporter.
+    /// 
+    HttpProtobuf
+}
diff --git a/tests/Aspire.Hosting.Tests/WithOtlpExporterTests.cs b/tests/Aspire.Hosting.Tests/WithOtlpExporterTests.cs
new file mode 100644
index 00000000000..f6a966e0099
--- /dev/null
+++ b/tests/Aspire.Hosting.Tests/WithOtlpExporterTests.cs
@@ -0,0 +1,72 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Aspire.Hosting.Tests.Utils;
+using Aspire.Hosting.Utils;
+using Microsoft.AspNetCore.InternalTesting;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Aspire.Hosting.Tests;
+
+public class WithOtlpExporterTests
+{
+    [InlineData(default, "http://localhost:8889", null, "http://localhost:8889", "grpc")]
+    [InlineData(default, "http://localhost:8889", "http://localhost:8890", "http://localhost:8889", "grpc")]
+    [InlineData(default, null, "http://localhost:8890", "http://localhost:8890", "http/protobuf")]
+    [InlineData(OtlpProtocol.HttpProtobuf, "http://localhost:8889", "http://localhost:8890", "http://localhost:8890", "http/protobuf")]
+    [InlineData(OtlpProtocol.Grpc, "http://localhost:8889", "http://localhost:8890", "http://localhost:8889", "grpc")]
+    [InlineData(OtlpProtocol.Grpc, null, null, "http://localhost:18889", "grpc")]
+    [Theory]
+    public async Task OtlpEndpointSet(OtlpProtocol? protocol, string? grpcEndpoint, string? httpEndpoint, string expectedUrl, string expectedProtocol)
+    {
+        using var builder = TestDistributedApplicationBuilder.Create();
+
+        builder.Configuration["ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL"] = grpcEndpoint;
+        builder.Configuration["ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL"] = httpEndpoint;
+
+        var container = builder.AddResource(new ContainerResource("testSource"));
+
+        if (protocol is { } value)
+        {
+            container = container.WithOtlpExporter(value);
+        }
+        else
+        {
+            container = container.WithOtlpExporter();
+        }
+
+        using var app = builder.Build();
+
+        var serviceProvider = app.Services.GetRequiredService();
+
+        var config = await EnvironmentVariableEvaluator.GetEnvironmentVariablesAsync(
+            container.Resource,
+            serviceProvider: serviceProvider
+            ).DefaultTimeout();
+
+        Assert.Equal(expectedUrl, config["OTEL_EXPORTER_OTLP_ENDPOINT"]);
+        Assert.Equal(expectedProtocol, config["OTEL_EXPORTER_OTLP_PROTOCOL"]);
+    }
+
+    [Fact]
+    public async Task RequiredHttpOtlpThrowsExceptionIfNotRegistered()
+    {
+        using var builder = TestDistributedApplicationBuilder.Create();
+
+        builder.Configuration["ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL"] = null;
+
+        var container = builder.AddResource(new ContainerResource("testSource"))
+            .WithOtlpExporter(OtlpProtocol.HttpProtobuf);
+
+        using var app = builder.Build();
+
+        var serviceProvider = app.Services.GetRequiredService();
+
+        await Assert.ThrowsAsync(() =>
+            EnvironmentVariableEvaluator.GetEnvironmentVariablesAsync(
+                container.Resource,
+                serviceProvider: serviceProvider
+            ).DefaultTimeout()
+        );
+    }
+}