Skip to main content
SEMastery

Using MassTransit with RabbitMQ and Azure Service Bus in .NET

Learn how MassTransit lets one set of .NET code run on both RabbitMQ and Azure Service Bus, with simple consumers, publishers, and config examples.

12 min readUpdated April 23, 2026

Using MassTransit with RabbitMQ and Azure Service Bus

Think about the postal system in your city. When you post a letter, you do not care whether the postman uses a bicycle, a bike, or a small van to carry it. You just write the address, drop the letter in the box, and trust that it reaches the right house. The way the letter travels is not your problem. The letter and the address are your only job.

This is exactly the idea behind MassTransit. In the world of software, messages are like letters. The thing that carries those messages is called a message broker. Two very popular brokers in the .NET world are RabbitMQ and Azure Service Bus. MassTransit is the friendly post office that sits in the middle. You write your message and your handler once, and MassTransit makes it work on either broker.

In this post we will learn what MassTransit does, why this "write once, run on either broker" idea is so useful, and how to set it up step by step. We will keep everything simple, with small code examples you can follow.

What problem does MassTransit solve?

Imagine you build an online shop. When someone places an order, you want to send them an email, update stock, and tell the warehouse to pack the box. You do not want the website to do all of this while the customer waits. Instead, the website should just announce: "A new order was placed!" Other parts of the system listen and do their own work.

This announcing and listening is called messaging. To pass these messages around, you need a broker. RabbitMQ and Azure Service Bus are both brokers, but they have very different APIs (the code you write to talk to them). If you write your code directly against RabbitMQ, and later your boss says "We are moving everything to Azure," you would have to rewrite a lot.

MassTransit sits above the broker. It gives you one clean programming model. Your code talks to MassTransit, and MassTransit talks to the broker. Switching brokers becomes a small change instead of a big rewrite.

MassTransit sits between your code and the broker

Notice that your code only ever talks to MassTransit. The broker behind it can change, and your code stays calm.

The three words you must know

Before we write code, let us learn three simple words. Once you know these, the rest is easy.

WordWhat it means in plain English
MessageA small class that holds the news, like "order placed" with an order id.
ProducerThe code that sends or publishes the message. It is the person posting the letter.
ConsumerThe code that receives the message and does the work. It is the house that opens the letter.

A message is just a normal C# class or record. A consumer is a class that implements IConsumer<T>, where T is the message type. A producer uses IPublishEndpoint to publish or ISendEndpoint to send. That is the whole core idea.

Publish versus send: a quick note

MassTransit gives you two ways to move a message, and beginners often mix them up. Here is the easy version.

ActionMeaningReal-life picture
PublishBroadcast to anyone who is interested.Shouting an announcement in a classroom. Many can hear it.
SendDeliver to one known queue.Posting a letter to one exact address.

Use publish when many parts of the system might care about an event (like "order placed"). Use send when you want one specific worker to do one specific job (like "process this payment").

Step 1: Install the packages

You always install the core MassTransit package. Then you add one package for the broker you want. You can have both installed and choose at runtime.

// Core package (always needed)
// dotnet add package MassTransit
 
// For RabbitMQ
// dotnet add package MassTransit.RabbitMQ
 
// For Azure Service Bus
// dotnet add package MassTransit.Azure.ServiceBus.Core

The clever part is that your consumers and messages do not depend on these transport packages. Only the setup code does.

Step 2: Write a message and a consumer

Let us write a tiny message. We will use a record because it is short and clear. Records are a friendly way to make simple data classes in C#.

namespace Shop.Contracts;
 
// This is the "letter". It is just data.
public record OrderPlaced
{
    public Guid OrderId { get; init; }
    public string CustomerEmail { get; init; } = string.Empty;
    public decimal Total { get; init; }
}

Now we write a consumer. This is the code that reacts when an OrderPlaced message arrives. It implements IConsumer<OrderPlaced>.

using MassTransit;
using Microsoft.Extensions.Logging;
using Shop.Contracts;
 
public class OrderPlacedConsumer : IConsumer<OrderPlaced>
{
    private readonly ILogger<OrderPlacedConsumer> _logger;
 
    public OrderPlacedConsumer(ILogger<OrderPlacedConsumer> logger)
    {
        _logger = logger;
    }
 
    public async Task Consume(ConsumeContext<OrderPlaced> context)
    {
        var message = context.Message;
 
        _logger.LogInformation(
            "New order {OrderId} for {Email}, total {Total}",
            message.OrderId, message.CustomerEmail, message.Total);
 
        // Here you could send an email, update stock, and so on.
        await Task.CompletedTask;
    }
}

The most important thing to notice: there is no RabbitMQ code and no Azure code here. This class will run, unchanged, on either broker. That is the gift MassTransit gives us.

How a message flows

Producer
MassTransit
Broker
Consumer

Steps

1

Producer

Publishes OrderPlaced

2

MassTransit

Serializes and routes

3

Broker

Stores in a queue

4

Consumer

Receives and reacts

From producer to consumer, through the broker

Step 3: Configure RabbitMQ

Now we wire everything up in Program.cs. We tell MassTransit which consumers exist and which broker to use. For RabbitMQ, we use UsingRabbitMq.

using MassTransit;
 
var builder = WebApplication.CreateBuilder(args);
 
builder.Services.AddMassTransit(x =>
{
    // Register all consumers in this assembly
    x.AddConsumer<OrderPlacedConsumer>();
 
    // Choose the RabbitMQ transport
    x.UsingRabbitMq((context, cfg) =>
    {
        cfg.Host("localhost", "/", h =>
        {
            h.Username("guest");
            h.Password("guest");
        });
 
        // Build the queues and bindings automatically
        cfg.ConfigureEndpoints(context);
    });
});
 
var app = builder.Build();
app.Run();

The line cfg.ConfigureEndpoints(context) is a small helper that does a lot. It looks at every consumer you registered and builds the right queues and bindings on RabbitMQ for you. You do not have to create exchanges and queues by hand.

Step 4: Configure Azure Service Bus

Here is the magic moment. To move to Azure Service Bus, we change only the transport block. The consumer registration and ConfigureEndpoints line stay the same.

using MassTransit;
 
builder.Services.AddMassTransit(x =>
{
    // Exactly the same consumer as before
    x.AddConsumer<OrderPlacedConsumer>();
 
    // The only real change: a different transport
    x.UsingAzureServiceBus((context, cfg) =>
    {
        var connectionString = builder.Configuration
            .GetConnectionString("AzureServiceBus");
 
        cfg.Host(connectionString);
 
        cfg.ConfigureEndpoints(context);
    });
});

Look closely. The consumer is identical. The endpoint configuration is identical. Only UsingRabbitMq became UsingAzureServiceBus, and the host details changed. That tiny difference is the whole point of this post.

Same code, two brokers, chosen at setup time

A clean trick is to read the broker choice from configuration. Then you can flip between RabbitMQ in local development and Azure Service Bus in production by changing one setting, with no code change at all.

Step 5: Publish a message

Now let us send some news into the system. In a controller or service, you ask for IPublishEndpoint and publish your message. MassTransit gives you this through normal dependency injection.

using MassTransit;
using Shop.Contracts;
 
public class OrdersController : ControllerBase
{
    private readonly IPublishEndpoint _publishEndpoint;
 
    public OrdersController(IPublishEndpoint publishEndpoint)
    {
        _publishEndpoint = publishEndpoint;
    }
 
    [HttpPost("orders")]
    public async Task<IActionResult> PlaceOrder()
    {
        await _publishEndpoint.Publish(new OrderPlaced
        {
            OrderId = Guid.NewGuid(),
            CustomerEmail = "[email protected]",
            Total = 999.00m
        });
 
        return Accepted();
    }
}

When this runs on RabbitMQ, MassTransit publishes to a RabbitMQ exchange. When it runs on Azure Service Bus, it publishes to an Azure topic. You did not write either of those details. MassTransit handled the broker-specific work for you.

How are the two brokers different under the hood?

Even though your code is the same, the brokers are not twins. It helps to know a little about how each one moves messages, so you can pick well.

Topology comparison

Message
RabbitMQ Exchange
RabbitMQ Queue
Azure Topic
Azure Subscription

Steps

1

Message

Published by your code

2

RabbitMQ Exchange

Fans out by type

3

RabbitMQ Queue

Consumer reads here

4

Azure Topic

Fans out by type

5

Azure Subscription

Consumer reads here

Where MassTransit puts your messages on each broker

On RabbitMQ, MassTransit creates exchanges and binds queues to them. RabbitMQ is fast, cheap to self-host, and very flexible. You run it yourself, which means you also patch and watch it.

On Azure Service Bus, MassTransit creates topics and subscriptions for published messages, and queues for sent messages. Azure Service Bus is fully managed by Microsoft, so there are no servers for you to maintain. It has built-in features like duplicate detection, scheduled delivery, message locking, and dead-lettering.

Here is a quick comparison to help you choose.

QuestionRabbitMQAzure Service Bus
Who runs the server?You do (or a host you pick).Microsoft, fully managed.
Best forSelf-hosting, low cost, any cloud.Teams already on Azure.
Cost modelServer cost you control.Pay for what you use.
Fan-out unitExchange to queue.Topic to subscription.

A peek at message delivery and retries

One lovely thing about MassTransit is that it does not just deliver messages. It also helps when things go wrong. If a consumer throws an error, MassTransit can retry. If it keeps failing, the message can be moved to an error queue (sometimes called a dead-letter queue) so it does not get lost.

You can turn on simple retries in the endpoint configuration. The example below tries three more times, waiting five seconds between each try.

cfg.ReceiveEndpoint("order-placed", e =>
{
    e.UseMessageRetry(r => r.Interval(3, TimeSpan.FromSeconds(5)));
    e.ConfigureConsumer<OrderPlacedConsumer>(context);
});

This retry code, like everything else, works the same on both brokers. You write the rule once and trust MassTransit to apply it correctly on RabbitMQ or Azure Service Bus.

What happens when a consumer fails

A word about licensing

It is fair to mention money before you build on a library. MassTransit changed its license recently, and you should plan for it.

MassTransit version 8 stays open-source under the Apache 2.0 license. It will keep getting security patches and important bug fixes through at least the end of 2026. So today, for learning and for many production apps, it is still free.

MassTransit version 9 moves to a commercial license. Paid plans start around 400 USD per month (or 4,000 USD per year) for small and medium businesses, with higher tiers for large enterprises. If you are choosing tools for a new project in 2026, factor this in. The good news is that the skills and code patterns you learn here carry over directly, whichever version you land on.

Local development tip

A common and friendly setup is: use RabbitMQ in Docker on your laptop, and Azure Service Bus in the cloud for production. RabbitMQ is easy to run locally with one Docker command, and it costs nothing. Because MassTransit hides the broker, your team writes and tests against RabbitMQ, then deploys to Azure Service Bus with a config change. Everyone is happy, and nobody rewrites code at the last minute.

Putting it all together

Let us trace one order from start to finish so the whole idea sticks.

  1. A customer clicks "Buy". The controller calls Publish with an OrderPlaced message.
  2. MassTransit serializes the message and hands it to the broker (RabbitMQ exchange or Azure topic).
  3. The broker stores it safely in a queue or subscription.
  4. OrderPlacedConsumer wakes up, reads the message, and does the work.
  5. If the work fails, MassTransit retries, and finally dead-letters the message if needed.

At no point did your business code know or care which broker was used. That separation is what keeps large systems easy to change.

Quick recap

  • A message is a small data class. A producer sends it. A consumer (which implements IConsumer<T>) reacts to it.
  • MassTransit sits between your code and the broker, giving you one clean programming model.
  • The same consumers and messages run on both RabbitMQ and Azure Service Bus. You only change the transport line, from UsingRabbitMq to UsingAzureServiceBus.
  • Use publish to broadcast events to many listeners, and send to deliver to one known queue.
  • ConfigureEndpoints(context) builds the queues, exchanges, topics, and subscriptions for you automatically.
  • MassTransit also handles retries and error queues, so failing messages are not silently lost.
  • MassTransit v8 is still free under Apache 2.0 through at least end of 2026. v9 is commercial, starting around 400 USD per month.
  • A popular setup is RabbitMQ locally in Docker and Azure Service Bus in production, switched by configuration.

References and further reading

Related Patterns