Skip to content
Guides · Recipes

How to Run RabbitMQ in a Cloud Sandbox

Updated October 27, 2026 .md

Short answer: Run the official rabbitmq:4-management Docker image in a Sandboxer microVM with both ports bound to 127.0.0.1. Reach AMQP on port 5672 from your laptop with sbx forward, and the management UI on a private HTTPS URL with sbx host --localhost. AMQP itself doesn't pass through hosted URLs. Persistent messages survived stop, resume and a broker restart.

What we ran#

Everything below ran on production on October 27, 2026, in one small sandbox (2 vCPU, 4 GB): RabbitMQ 4.4.0 on Erlang 29.1.2 from the official rabbitmq:4-management image. The laptop client was Python with pika. Timings are single runs, or 20 runs where we give a median. Yours will differ.

Step Result
sbx new --type small until ready 3.2 s
Pull the image (283 MB), start it, wait for the AMQP port 14 s
PerfTest in the sandbox, 1 KB persistent messages, 15 s 12,768 msg/s average, 5.8 ms median confirm latency
AMQP from a laptop through sbx forward connect 0.5 s, publish + get 250 ms median round trip
1,000 persistent publishes from the laptop, one confirm each 126.4 s
Management UI and HTTP API on a private hosted URL 401 without the token, 200 with token and RabbitMQ login
AMQP client against a hosted URL no AMQP reply, client timed out after 15 s
sbx stop --wait, then sbx resume 2.2 s and 1.5 s, memoryRestored: true, queue intact, forward kept working
Docker daemon restart broker back in 5 s, 999 persistent messages still queued

RabbitMQ has two front doors, and they need different routes. AMQP on port 5672 is a binary TCP protocol. The management UI and HTTP API on port 15672 are plain HTTP. Sandboxer's hosted URLs are HTTPS, so only the second one fits there.

What you reach Port Route from outside Tested
AMQP clients (pika, amqplib, Spring AMQP…) 5672 sbx forward --remote 5672, or ssh -L Yes, sbx forward
Management UI in a browser 15672 sbx host 15672 --localhost, private Yes
Management HTTP API (including publish) 15672 Same hosted URL, with RabbitMQ basic auth Yes
AMQP over the hosted HTTPS URL 5672 Doesn't work Yes, failed
Producers and consumers that run in the sandbox 5672 127.0.0.1:5672, no route needed Yes, PerfTest

1. Start RabbitMQ#

Docker is preinstalled in every sandbox. The Docker Hub page documents the image, its -management tag and the RABBITMQ_DEFAULT_USER/RABBITMQ_DEFAULT_PASS variables. Make a password on your laptop, then start the container. The image pull can take longer than the default 30-second sbx exec timeout, so pass --timeout:

sbx new --name rabbit --type small --no-env
id=sb_58f03i38vj    # the id that sbx new printed
RMQ_PASS=$(openssl rand -hex 12)

sbx exec $id --timeout 600 "docker run -d --name rabbit --hostname rabbit --restart unless-stopped \
  -e RABBITMQ_DEFAULT_USER=app -e RABBITMQ_DEFAULT_PASS=$RMQ_PASS \
  -p 127.0.0.1:5672:5672 -p 127.0.0.1:15672:15672 \
  -v rabbitdata:/var/lib/rabbitmq rabbitmq:4-management >/dev/null \
  && until docker exec rabbit rabbitmq-diagnostics -q check_port_connectivity >/dev/null 2>&1; do sleep 1; done; \
  docker exec rabbit rabbitmq-diagnostics -q server_version"
4.4.0

The password travels inside the command, so it's stored in the sandbox's command history (sbx history) and visible to docker inspect. That's fine for a throwaway broker in a sandbox you'll delete. For a password you care about, follow How do I pass secrets to a sandbox safely? instead.

Each flag has a job:

  • -p 127.0.0.1:…. Both ports listen inside the VM only. You decide below what reaches them, and how.
  • --hostname rabbit. RabbitMQ names its node and data directory after the hostname. A fixed name keeps the same data directory if the container is recreated.
  • -v rabbitdata:/var/lib/rabbitmq. Queues and persistent messages live in a named volume on the sandbox disk.
  • RABBITMQ_DEFAULT_USER. The image then creates app instead of guest. RabbitMQ only lets guest connect from localhost anyway (see its access control docs). Over the hosted URL, guest:guest got 401 not_authorized.
  • --restart unless-stopped. If a resume boots the sandbox from disk, Docker starts the broker again (section 6).

2. Connect an AMQP client from your laptop#

Forward a local port to the sandbox's port 5672. sbx forward runs over the same authenticated tunnel as sbx ssh and keeps running in the foreground, so give it its own terminal. We used local port 5673 to stay clear of any RabbitMQ on the laptop:

sbx forward $id --remote 5672 --local 5673
{"event":"forwarding","id":"sb_58f03i38vj","reverse":false,"local":5673,"remote":5672,"bind":"127.0.0.1"}

Any AMQP client on the laptop can now use amqp://app:<password>@127.0.0.1:5673/. Nothing is public. The MySQL recipe shows the plain ssh -L route, which works the same way for port 5672.

Save this as amqp_client.py. It declares a durable jobs queue and turns on publisher confirms, then measures a round trip or publishes a batch:

import os, statistics, sys, time
import pika

port = int(sys.argv[1]); mode = sys.argv[2]
params = pika.ConnectionParameters(
    "127.0.0.1", port, "/", pika.PlainCredentials("app", os.environ["RMQ_PASS"]))
t0 = time.perf_counter()
conn = pika.BlockingConnection(params)
print(f"connected in {(time.perf_counter()-t0)*1000:.0f} ms")
ch = conn.channel()
ch.confirm_delivery()
q = ch.queue_declare("jobs", durable=True)
print("jobs queue depth before:", q.method.message_count)
persistent = pika.BasicProperties(delivery_mode=pika.DeliveryMode.Persistent)

if mode == "rtt":
    ch.queue_declare("rtt", exclusive=True)
    times = []
    for i in range(21):
        t = time.perf_counter()
        ch.basic_publish("", "rtt", f"ping {i}".encode())
        method, _, body = ch.basic_get("rtt", auto_ack=True)
        times.append((time.perf_counter() - t) * 1000)
    times = times[1:]
    print(f"publish+confirm+get round trip: median {statistics.median(times):.0f} ms, min {min(times):.0f} ms over {len(times)}")
elif mode == "publish":
    n = int(sys.argv[3])
    t = time.perf_counter()
    for i in range(n):
        ch.basic_publish("", "jobs", f"job {i}".encode(), persistent)
    print(f"published {n} persistent messages with confirms in {time.perf_counter()-t:.1f} s")
elif mode == "get":
    method, props, body = ch.basic_get("jobs", auto_ack=True)
    print("got:", body.decode() if body else None)
q = ch.queue_declare("jobs", durable=True, passive=True)
print("jobs queue depth after:", q.method.message_count)
conn.close()
export RMQ_PASS    # the password from step 1
python amqp_client.py 5673 publish 1000
python amqp_client.py 5673 rtt
python amqp_client.py 5673 get
connected in 507 ms
jobs queue depth before: 0
published 1000 persistent messages with confirms in 126.4 s
jobs queue depth after: 1000
connected in 503 ms
jobs queue depth before: 1000
publish+confirm+get round trip: median 250 ms, min 250 ms over 20
jobs queue depth after: 1000
connected in 502 ms
jobs queue depth before: 1000
got: job 0
jobs queue depth after: 999

Two lessons from this run:

  • Waiting for each confirm over a long link is slow. The blocking client waits for every confirm before it sends the next message. That is about 126 ms per message on our link, so 1,000 messages took two minutes. Most of that time is the network between our laptop and the sandbox, which depends on where you are. For bulk work, confirm in batches or asynchronously, or run the producer next to the broker (section 3).
  • RabbitMQ 4 refuses transient, non-exclusive queues by default. Our first try declared the scratch queue with auto_delete=True and nothing else. The broker closed the connection: 541 INTERNAL_ERROR - Feature transient_nonexcl_queues is deprecated. Declare scratch queues exclusive=True, as the script now does, or make them durable. RabbitMQ's deprecated features list covers it.

3. Measure throughput inside the sandbox#

To see what the broker does without the network in the way, run RabbitMQ's own load tool, PerfTest, in the same sandbox. It ships as a Docker image. --network host lets it reach 127.0.0.1:5672:

sbx exec $id --timeout 400 "docker run --rm --network host pivotalrabbitmq/perf-test:latest \
  --uri amqp://app:$RMQ_PASS@127.0.0.1:5672 -x 1 -y 1 -s 1000 -z 15 -f persistent -c 100 -q 1000 2>&1 | tail -6"

That is one producer and one consumer, 1,000-byte persistent messages, up to 100 unconfirmed messages in flight, a prefetch of 1,000 and a 15-second run. PerfTest 2.25.0 printed:

test stopped (Reached time limit)
id: test-101634-126, sending rate avg: 12768 msg/s
id: test-101634-126, receiving rate avg: 12768 msg/s
id: test-101634-126, consumer latency min/median/75th/95th/99th/max 344/4091/6068/9962/29296/156461 µs
id: test-101634-126, confirm latency min/median/75th/95th/99th/max 1367/5824/7463/11148/15558/22092 µs

Compare that with the laptop run: 12,768 confirmed messages a second in the sandbox, versus about 8 a second with one-at-a-time confirms over the tunnel. If agents or workers produce and consume the messages, run them in the sandbox and use the tunnel for inspection and light traffic.

4. Open the management UI on a private URL#

The management plugin is HTTP, so it can have a hosted URL. It listens on 127.0.0.1 here, so pass --localhost and the sandbox relays to it:

sbx host $id 15672 --localhost --title "RabbitMQ management"
# prints https://fresh-airy-kayak-15672.box.sandboxer.com?_token=<token>; keep it secret

The URL is private by default. Behind it are two separate locks: Sandboxer's token, then RabbitMQ's own login. We checked each one with curl:

Request to the hosted URL Status Who answered
GET /api/overview, no token 401 "Access token required" page Sandboxer proxy, RabbitMQ never saw it
GET /?_token=<token> 302 to /, sets the __sbx_auth cookie Sandboxer proxy
Cookie, no RabbitMQ login 401, www-authenticate: Basic realm="RabbitMQ Management" RabbitMQ
Cookie plus -u guest:guest 401 not_authorized RabbitMQ
Cookie plus -u app:<password> 200, rabbitmq_version 4.4.0 RabbitMQ
Cookie, GET / 200, <title>RabbitMQ Management RabbitMQ

In a browser, open the printed link once. The proxy swaps the token for a cookie, and RabbitMQ shows its login page. Sign in as app.

The same URL serves the HTTP API, which can publish a message. That is handy for a quick test from a script that has no AMQP client:

curl -s -b jar -u app:$RMQ_PASS -H 'content-type: application/json' \
  -X POST "https://fresh-airy-kayak-15672.box.sandboxer.com/api/exchanges/%2F/amq.default/publish" \
  -d '{"properties":{"delivery_mode":2},"routing_key":"jobs","payload":"sent over https","payload_encoding":"string"}'
{"routed":true}

jar is the cookie file from the ?_token= request (curl -c jar). A few seconds later, GET /api/queues/%2F/jobs showed "messages":1000,"messages_persistent":1000: the 999 left from step 2, plus this one. RabbitMQ refreshes queue stats on a timer, so a read right after the publish still showed 999.

5. Why AMQP doesn't work on a hosted URL#

The hosting docs put services on https://<subdomain>-<port>.box.sandboxer.com. That URL is an HTTPS endpoint, not a raw TCP port. To check, we hosted port 5672 too, first private and then --public, and pointed AMQP at it:

  • A raw TLS connection to fresh-airy-kayak-5672.box.sandboxer.com:443 completed the handshake. When we sent the AMQP protocol header, nothing came back before our 10-second read timeout.
  • pika with TLS to the same host and port 443 failed with AMQPConnectorStackTimeout after 15 seconds.

Then we took that URL down with sbx host hide $id 5672. Use sbx forward or SSH for AMQP. If browsers need to talk to RabbitMQ, its Web STOMP and Web MQTT plugins run over WebSockets, which hosted URLs carry. We didn't test those plugins here.

6. Stop, resume and broker restarts#

We left sbx forward running and stopped the sandbox:

sbx stop $id --wait     # 2.2 s
sbx resume $id          # 1.5 s, memoryRestored: true
python amqp_client.py 5673 get
connected in 507 ms
jobs queue depth before: 1000
got: job 1
jobs queue depth after: 999

The same forward process carried the new connection. The management URL answered 200 with the cookie from before the stop. Memory was restored, so the broker never restarted: docker ps showed rabbit Up 4 minutes.

A resume boots from disk instead when no machine with the same CPU type is free. Then files stay and processes start fresh. We didn't force that case, but restarting the Docker daemon shows the same thing: the container stops, the restart policy brings it back, and RabbitMQ reloads its queues from the volume:

sbx exec $id --timeout 120 "sudo systemctl restart docker
until docker exec rabbit rabbitmq-diagnostics -q check_port_connectivity >/dev/null 2>&1; do sleep 1; done
docker ps --format '{{.Names}} {{.Status}}'; docker exec rabbit rabbitmqctl -q list_queues name durable messages"
rabbit Up 4 seconds
name	durable	messages
jobs	true	999

The broker was back in 5 seconds with all 999 messages. They survived because the queue is durable and the messages were published persistent (delivery_mode=2). Transient messages don't survive a broker restart. See snapshots for the resume rules.

7. Clean up#

Stop the forward with Ctrl-C, then delete the sandbox:

sbx delete $id --yes

After the delete, the API returned 404 for the sandbox, and the management URL returned 404. A small sandbox bills at half the default rate of $0.036/hour while it runs, and nothing while it's stopped.

FAQ#

Can I connect to RabbitMQ in a sandbox without SSH?#

Not over AMQP. Hosted URLs are HTTPS, and our AMQP client timed out against one. Use sbx forward, which needs only outbound HTTPS and the CLI, or ssh -L. For HTTP-only access, the management API can publish and read queues over the hosted URL.

Why does RabbitMQ close my connection with "transient_nonexcl_queues is deprecated"?#

RabbitMQ 4 refuses queues that are neither durable nor exclusive by default. Declare scratch queues with exclusive=True or make them durable. The error came from the broker, not from the sandbox.

Why is publishing from my laptop so slow?#

Each blocking publish waited for its confirm, so every message paid a full round trip through the tunnel, about 126 ms for us. In the sandbox, PerfTest reached 12,768 msg/s with up to 100 confirms in flight. Batch your confirms, or run producers in the sandbox.

Is a small sandbox enough?#

For a dev or test broker, yes. 2 vCPU and 4 GB handled 12,768 persistent 1 KB messages a second in our run. A deep backlog uses memory and disk, so watch the volume, and pick default for heavier loads.

Do I need to open a firewall port?#

No. sbx forward reaches 127.0.0.1:5672 inside the VM through the CLI's tunnel. sbx host sets up the route for the management port. RabbitMQ listened only on 127.0.0.1 the whole time.

More guides

Top AI sandbox platforms for code execution in 2026, ranked

For running AI-written code we rank Sandboxer, Freestyle and E2B first: each gives code its own VM with an API or SDK and can pause with memory. Pick Daytona for Windows or GPUs, Modal or Northflank for GPUs at scale, E2B or Northflank to run in your own cloud, and Vercel Sandbox if your app lives on Vercel.

Read the guide
Best sandboxes for running coding agents (Claude Code, Codex, Gemini CLI) in 2026, ranked

For coding agents we rank Sandboxer first: a full Ubuntu VM with Claude Code and Codex preinstalled, Docker, and stop-and-resume that keeps the agent's session in memory. Freestyle and Amp Orbs also keep memory across pauses. Pick E2B for prebuilt agent templates on open source, Docker Sandboxes for free local runs, and Codespaces if you live in GitHub.

Read the guide
Best Daytona sandbox alternatives in 2026, ranked

The best Daytona alternatives depend on why you're switching. For a full Linux microVM on every sandbox with memory-saving stop and Docker, we rank Sandboxer first. E2B suits open source and self-hosting, Modal massive short-job fleets, Northflank bring-your-own-cloud with GPUs, and Vercel Sandbox apps on Vercel.

Read the guide
Best E2B alternatives in 2026, ranked

If you're leaving E2B for longer sessions or a fuller machine, we rank Sandboxer first, then Freestyle: both give each sandbox a full Linux VM that keeps memory across pauses, with Docker and no short session cap. Pick Daytona for Windows or GPUs, Northflank for bring-your-own-cloud, and stay on E2B if you need open source.

Read the guide