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.
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.
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.
| Word | What it means in plain English |
|---|---|
| Message | A small class that holds the news, like "order placed" with an order id. |
| Producer | The code that sends or publishes the message. It is the person posting the letter. |
| Consumer | The 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.
| Action | Meaning | Real-life picture |
|---|---|---|
| Publish | Broadcast to anyone who is interested. | Shouting an announcement in a classroom. Many can hear it. |
| Send | Deliver 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.CoreThe 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
Steps
Producer
Publishes OrderPlaced
MassTransit
Serializes and routes
Broker
Stores in a queue
Consumer
Receives and reacts
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.
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
Steps
Message
Published by your code
RabbitMQ Exchange
Fans out by type
RabbitMQ Queue
Consumer reads here
Azure Topic
Fans out by type
Azure Subscription
Consumer reads here
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.
| Question | RabbitMQ | Azure Service Bus |
|---|---|---|
| Who runs the server? | You do (or a host you pick). | Microsoft, fully managed. |
| Best for | Self-hosting, low cost, any cloud. | Teams already on Azure. |
| Cost model | Server cost you control. | Pay for what you use. |
| Fan-out unit | Exchange 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.
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.
- A customer clicks "Buy". The controller calls
Publishwith anOrderPlacedmessage. - MassTransit serializes the message and hands it to the broker (RabbitMQ exchange or Azure topic).
- The broker stores it safely in a queue or subscription.
OrderPlacedConsumerwakes up, reads the message, and does the work.- 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
UsingRabbitMqtoUsingAzureServiceBus. - 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
- MassTransit Documentation — the official docs and getting-started guides.
- Azure Service Bus Configuration (MassTransit) — how to set up the Azure transport.
- Using MassTransit with RabbitMQ and Azure Service Bus (Milan Jovanović) — a clear community walkthrough.
- MassTransit RabbitMQ and Azure Service Bus: Is It Worth a Commercial License (Anton Martyniuk) — a thoughtful look at the new license.
Related Patterns
Request-Response Messaging Pattern With MassTransit in .NET
Learn the request-response messaging pattern with MassTransit in .NET using IRequestClient, timeouts, and multiple response types with simple examples.
Implementing the Saga Pattern with MassTransit in .NET
Learn the Saga pattern in .NET with MassTransit state machines — states, events, correlation, persistence, retries, and compensation, explained in simple, friendly steps.
MassTransit with RabbitMQ and Azure Service Bus: Is It Worth a Commercial License?
MassTransit went commercial in v9. See how it works with RabbitMQ and Azure Service Bus, what the new license costs, and whether it is worth paying for.
MassTransit Outbox Pattern with EF Core and MongoDB in .NET
Learn the transactional outbox pattern in .NET using MassTransit with EF Core and MongoDB so your database and message broker never fall out of sync.
Event-Driven Architecture in .NET with RabbitMQ: A Beginner's Guide
Learn event-driven architecture in .NET with RabbitMQ using simple words, real-life examples, exchanges, queues, and clean async C# code you can copy.
Complete Guide to Amazon SQS and Amazon SNS with MassTransit
A friendly, step-by-step guide to messaging in .NET using Amazon SQS, Amazon SNS, and MassTransit — queues, topics, consumers, retries, and dead-letter handling.