Testing Microservices with RabbitMQ using Signadot Sandboxes

Introduction
Asynchronous microservices are hard to test: if two versions of a consumer read from the same queue, they compete for messages. Spinning up a separate broker per branch is slow and pricey.
Signadot Sandboxes solve this with request-level isolation. Keep a single RabbitMQ, but route messages only to the intended version (sandbox) of your consumer using a sandbox routing key. Each sandboxed consumer has its own queue binding; baseline traffic remains untouched while you test safely in parallel.
What you will accomplish:
- Set up a RabbitMQ-based microservices application
- Use routing keys + selective consumption to isolate sandbox traffic
- Deploy services to Kubernetes
- Create Signadot sandboxes for isolated testing
- Test message routing between baseline and sandbox environments
Time required: 45-60 minutes
Prerequisites
Before starting, ensure you have:
- Signadot CLI installed - Follow the installation guide
- Docker Desktop running locally
- kubectl configured to access your Kubernetes cluster
- Python 3.8+ and pip
- RabbitMQ knowledge - Basic understanding of exchanges, queues, and routing
Architecture Overview
- A topic exchange fans out each message to both baseline and sandbox queues.
- OTel propagates the sandbox routing key automatically as W3C baggage.
- Selective consumption in consumers:
- Baseline consumer processes messages with no routing key, and skips messages whose key belongs to an active sandbox of this same service. It still processes messages carrying keys for other servicesβ sandboxes or inactive/unknown keys.
- Sandbox consumer processes only messages with its own sandbox key.
This gives isolation without multiple brokers: everyone receives the message, but only the right consumer acts on it.
The idea in one picture
Baseline and sandbox consumers each have their own queue bound to the same exchange. Routing keys in headers determine who should act.
Project structure
You can scaffold this from your own repo or adapt from Signadot examples.
Step 1 β Get the project
Clone the example repository from GitHub:
Inside the repo youβll find:
- publisher/ β Flask app with
/publish
and/events
. Publishes to a topic exchange and logs events to Redis. - consumer/ β Python consumer that declares its own queue, binds to the exchange, and processes messages.
- k8s/ β manifests for RabbitMQ, Redis, publisher, consumer, and a namespace.
- signadot/ β sandbox specs for publisher/consumer and a RouteGroup to bind them under a single routing key.
Step 2 β Build and push images
If youβll test a dev version in a sandbox, also push a :dev
tag.
Step 3 β Deploy the baseline stack
Create namespace and infra:
Deploy services:
Verify pods:
Step 4 β Connect to Signadot Locally
Use Signadot Local Connect to reach cluster services directly from your local machine:
Send a baseline message (no sandbox key):
Check:
You should see baseline processing.

Step 5 β Create sandboxes and a RouteGroup
Spin up forked workloads for publisher-v2 and consumer-v2:
Create a RouteGroup that ties them together under one routing key / URL (optional):
Step 6 β Prove isolation
Baseline message (no header β baseline handles it):

Sandbox message (explicit header β sandbox handles it):

Watch logs side-by-side:
- Baseline:

- Sandbox (replace name with your sandbox consumer deployment)

Expected outcome:
- Baseline processes only messages without a sandbox key or routing key of sandboxes of other services
- Sandbox processes only messages with its own key
Whatβs Happening Under the Hood
- Context propagation: When you hit a sandbox URL or pass
sd-routing-key
, the publisher attaches that key to message headers. - Fan-out delivery: RabbitMQβs topic exchange routes messages to both baseline and sandbox queues (via their bindings).
- Selective consumption:
- Baseline consumer accepts messages without a sandbox key; it skips messages with a key that belongs to an active sandbox.
- Sandbox consumer processes only messages whose
sd-routing-key
equals its sandbox ID.
- No message loss: Each sandbox has its own queue bound to the exchange. Messages always have a target consumer; baseline only βavoidsβ messages while a matching sandbox is active.
Conclusion
Youβve built and deployed a minimal RabbitMQ publisher/consumer stack, forked services into Signadot sandboxes, and confirmed that sandbox routing keys isolate messages in a shared RabbitMQ. This approach scales beyond this demo:
- Works with Kafka, Pub/Sub, or SQS (using analogous header/attribute + selective consumption patterns)
- Supports multiple sandboxes concurrently (e.g., per PR)
- Integrates naturally with CI/CD to spin up ephemeral test envs for each change
For deeper dives, see:
Happy testing!
Join our 1000+ subscribers for the latest updates from Signadot