back to blog & resources
Blog

|

November 3, 2025

Local Development with Dapr PubSub and .NET Aspire

You’ve made the jump to .NET Aspire, and now you need to ensure this great experience results in production ready systems. Let’s catch up on the current state of .NET Aspire and Dapr with a sample project that makes use of the pubsub building block.

Designing multi-service architectures is something that takes careful planning. Not only do you have to ensure that services are exchanging data they understand, but when you go to production, everything has to scale and remain secure.

Let’s imagine a scenario: You’d like to propose a new distributed technology and pattern to others in your organization using a locally runnable demo built with .NET Aspire.
Producing something like this while keeping all the pieces easy to assemble is often a lot of work. In these scenarios, it’s natural to want prebuilt tools to speed things along and avoid any risks from hand-rolling your own distributed patterns.

Dapr and Aspire - better together

If you’re already familiar with Aspire, then you might not realize that you have an easy path to trying out and adopting Dapr by using the Aspire Dapr NuGet package.

We covered some of the basics about Aspire in a blog post last year. Since that blog post, many developers have integrated Dapr with their Aspire based projects to gain capabilities beyond application definition and local orchestration.

While Aspire helps you work and collaborate by giving you great onboarding and a consistently reproducible environment, it leaves many important details up to you. Details that, if not addressed early on, can result in reliability and scaling issues that require last-minute and often challenging design changes.

Inspiring discussion

With adoption of the integration NuGet growing, and as maintainers of Dapr, we’d like to go beyond the basics and engage with the community around the Dapr and Aspire developer experience.

To accomplish this, we’ve prepared a demo project for you to play with: https://github.com/diagrid-labs/dapr-aspire-pub-sub-demo

Dapr .NET Aspire pub-sub demo

The solution features two different pubsub brokers and an application that takes messages arriving from one and publishes them to the other. The main goal of this solution is to inspire discussion around the integration and offer an easy way to try out Dapr with Aspire.

Once you clone and run the repository, take some time to explore the code. The sample application demonstrates:

  • How Dapr and Aspire can work together to simplify distributed application development
  • Dapr's publish/subscribe pattern and its ability to work with multiple message brokers
  • How to configure Dapr components within an Aspire application

An important note: A prerequisite for running this project is to ensure that you have installed the Dapr CLI and initialized Dapr prior to running your Aspire project.

The sample sets up Eclipse Mosquitto (mqtt) and RabbitMQ (amqp) message brokers along with a listener project that gets started with its own Dapr sidecar process.
Messages that get published to the Mosquitto broker using Dapr pubsub are consumed and modified by a worker which then publishes the result to RabbitMQ also using Dapr pubsub.

(The Dapr pubsub building block is just one of Dapr’s many distributed system abstractions that you might already be familiar with.)

Let’s take a look at some of the code in the AppHost Program.cs.

var mqtt = builder
    .AddContainer("mqtt", "eclipse-mosquitto")
    .WithBindMount(Path.Join("resources", "mosquitto", "config"), "/mosquitto/config")
    .WithVolume("dapr-aspire-demo-mqtt", "/mosquitto/data")
    .WithEndpoint(1883, 1883)
;

Here, Aspire is used to configure an Eclipse Mosquitto MQTT broker. Two configuration files are bound, and the service is made visible on port `1883`:

var amqp = builder
    .AddRabbitMQ("amqp", amqpUsername, amqpPassword, port: 5672)
    .WithBindMount(Path.Join("resources", "rabbitmq", "definitions.json"), "/opt/definitions.json")
    .WithBindMount(Path.Join("resources", "rabbitmq", "rabbitmq.config"),
"/etc/rabbitmq/rabbitmq.config")
    .WithManagementPlugin()
;

Similarly, we set up our RabbitMQ broker, along with some configuration that makes our demo a little more convenient.

Next, let’s have a look at our event receiver Aspire setup.

var eventReceiver = builder
    .AddProject<EventReceiver>("event-receiver")
    .WithDaprSidecar(new DaprSidecarOptions
    {
        LogLevel = "debug",
        ResourcesPaths = [ Path.Join(executingPath, "resources", "dapr") ],
    })
    .WaitFor(mqtt)
    .WaitFor(amqp)
;

This registers the event receiver, which is what processes our messages. It’s also where Aspire is instructed to run a daprd process using the WithDaprSidecar method. This adds Dapr environment variables, which get picked up by the Dapr .NET SDK while also ensuring that the Dapr process itself is started with the right arguments.

If you’re already familiar with Aspire, one thing you might notice is that the Aspire definition for the listener project doesn’t have any WithReference calls to connect it with the brokers.
This is intentional, as part of leveraging Dapr is to handle communication between dependencies through its sidecar-based design.

Messages are first published to Mosquitto via MQTT protocol

To test this application, you can trigger periodic messages using the message-sender command to enqueue a message for processing. The message sender custom command - discussed below - uses the Dapr executable directly to enqueue messages.

The event receiver project consumes messages via MQTT

The event receiver project takes incoming MQTT messages and modifies them. This is where the business logic lives in the event receiver. We also make sure to decorate our minimal API endpoint to tell the Dapr messaging SDK which pubsub to use.

host.MapPost("demo", async (
    [FromServices] DaprClient daprClient,
    [FromBody] string message
) =>
{
    var response = message switch
    {
        "Beat..." => $"Heart {message}",
        _ => $"Received: {message}",
    };

    await daprClient.PublishEventAsync("amqp-pubsub", "demo", response);

    return TypedResults.Ok();

}).WithTopic("mqtt-pubsub", "demo");

The event receiver app publishes the result of its work on an AMQP topic.

Once processing is complete, a new message is created and sent to the AMQP-backed pubsub (RabbitMQ).

Observing the messages

Use the following steps to observe the messages as they arrive in RabbitMQ:

  • Trigger a message using the custom publish message command in our Aspire project

Custom Aspire button for publishing messages
  • Use the link provided in Aspire to navigate to your RabbitMQ dashboard. Don’t worry if your port is different. Aspire manages them for us.

Aspire dashboard entry for our RabbitMQ server
  • At the RabbitMQ login screen, use `demo` for both the username and the password.

RabbitMQ dashboard login screen
  • Finally, go to the “Queues and Streams” area in RabbitMQ and click “Get Message(s)” to see the message we sent.

Reviewing published messages in the RabbitMQ dashboard

Share your thoughts

This demo offers just enough to see Dapr and Aspire working seamlessly together, and gives you an opportunity to try out the experience.

There’s a lot we’d like to learn from the community.  Please share your experience with the current Dapr Aspire integration:

  • Questions around the relationship between Dapr and Aspire, as well as how you can use them together effectively
  • Any comments or notes from trying out the demo project
  • Anything you’d like to see improved
  • General questions around Dapr and Aspire project architecture

To do this, join us in the #dotnet-aspire channel on the Dapr Discord Server where we have an active community talking about using Aspire and Dapr.

Finally, if you’re still learning Dapr and want more resources, head over to Dapr University, a free cloud-based sandbox environment to learn about the Dapr basics, Dapr Workflow, and Dapr Agents.

No items found.