Skip to content
Guides · Recipes

How to Run Redpanda (Kafka API) in a Cloud Sandbox

Updated October 27, 2026 .md

Short answer: Run the official Redpanda image in a Sandboxer microVM with two listeners, one for containers and one advertised as localhost:19092, then reach it from your laptop with sbx forward. Kafka is TCP, so it doesn't go through a hosted HTTPS URL; Redpanda Console does. rpk produced 100,000 messages in about 0.2 seconds.

What we ran#

Everything below ran on production on October 27, 2026, in one default sandbox (4 vCPU, 8 GB). Redpanda is a single-binary broker that speaks the Kafka protocol, so Kafka clients and tools work against it unchanged. The latest image was Redpanda v26.2.6. The laptop client was Python confluent-kafka 2.16.0. Timings are single runs or three back-to-back runs; yours will differ.

Step Result
sbx new until ready 3.2 s
Pull Redpanda, start it, wait until rpk cluster health is healthy 8 s
Produce 100,000 short messages with rpk inside the sandbox 187 to 222 ms
Consume the same 100,001 messages with rpk 174 ms
Produce one message from the laptop through sbx forward and wait for the ack 125 ms median, 20 messages
Redpanda Console on a private hosted URL 401 without the token, 200 with it
Kafka client pointed at a hosted URL Broker transport failure
sbx stop --wait, then sbx resume 2.2 s and 1.4 s, memory restored, messages intact
Memory in use after the tests Redpanda 584 MiB, Console 44 MiB

Which route reaches which port#

Kafka clients speak Kafka's binary protocol over TCP. A hosted URL is an HTTPS proxy that checks a token on each request, so it suits web UIs and HTTP APIs, not the broker.

What Port in the VM How you reach it from outside
Kafka API (producers, consumers, admin) 19092 on 127.0.0.1 sbx forward or ssh -L, both over your API key
Kafka API for other containers 9092 on the Docker network Only other containers on kafkanet
Redpanda Console (web UI) 8080 on 127.0.0.1 sbx host --localhost, private by default

We checked the second-to-last row of the results table directly: we hosted 19092, pointed a Kafka admin client at …-19092.box.sandboxer.com:443 over TLS, and it failed with Failed to get metadata: Local: Broker transport failure. A plain curl at that URL got 401 from the proxy. We then hid the route.

1. Start Redpanda with two listeners#

Docker is preinstalled in every sandbox. The command follows Redpanda's single-broker quickstart. Image pulls take longer than the default 30-second sbx exec timeout, so pass --timeout:

sbx new --name redpanda --type default --no-env
id=sb_pgzo691h33    # the id that sbx new printed

sbx exec $id --timeout 600 "docker network create kafkanet && \
  docker run -d --name redpanda --network kafkanet --restart unless-stopped \
    -p 127.0.0.1:19092:19092 -v redpanda-data:/var/lib/redpanda/data \
    docker.redpanda.com/redpandadata/redpanda:latest \
    redpanda start --mode dev-container --smp 1 \
      --kafka-addr internal://0.0.0.0:9092,external://0.0.0.0:19092 \
      --advertise-kafka-addr internal://redpanda:9092,external://localhost:19092 && \
  until docker exec redpanda rpk cluster health | grep -q 'Healthy:.*true'; do sleep 1; done && \
  docker exec redpanda rpk cluster health"
CLUSTER HEALTH OVERVIEW
=======================
Healthy:                          true
Unhealthy reasons:                []
Controller ID:                    0
All nodes:                        [0]
…

The listener flags are the part that trips people up. A Kafka client only uses the address you give it to fetch metadata. After that it connects to whatever address the broker advertises. So each listener advertises the name its clients can resolve:

  • internal://redpanda:9092 is for containers on the kafkanet network, such as Redpanda Console in step 4.
  • external://localhost:19092 is for clients that reach port 19092 as localhost. That covers anything in the VM and your laptop through a port forward that keeps the same port number.

Three other flags matter:

  • -p 127.0.0.1:19092:19092 keeps the broker inside the VM. Dev-container mode has no authentication, so nothing outside the VM should reach it except through your forward.
  • -v redpanda-data:/var/lib/redpanda/data keeps the log segments in a named volume on the sandbox disk.
  • --restart unless-stopped makes Docker start Redpanda again if the sandbox boots from disk instead of restoring memory.

--mode dev-container and --smp 1 are Redpanda's settings for a small development broker. They are not a production layout. See Redpanda's production deployment docs for that.

2. Produce and consume with rpk#

The image ships with rpk, Redpanda's CLI. Create a topic with three partitions, then send and read one message:

sbx exec $id "docker exec redpanda rpk topic create orders -p 3 && \
  echo hello-from-rpk | docker exec -i redpanda rpk topic produce orders -k first && \
  docker exec redpanda rpk topic consume orders -n 1"
TOPIC   STATUS
orders  OK
Produced to partition 1 at offset 0 with timestamp 1793095742303.
{
  "topic": "orders",
  "key": "first",
  "value": "hello-from-rpk",
  "timestamp": 1793095742303,
  "partition": 1,
  "offset": 0
}

rpk topic produce sends one message per input line, so a file of 100,000 lines is a quick load test:

sbx exec $id --timeout 300 "seq 1 100000 | sed 's/^/order-/' > msgs.txt && \
  s=\$(date +%s%N); docker exec -i redpanda rpk topic produce orders < msgs.txt > /dev/null; \
  echo produce ms=\$(( (\$(date +%s%N)-s)/1000000 )); \
  s=\$(date +%s%N); docker exec redpanda rpk topic consume orders -n 100001 -f '%v\n' | tail -1; \
  echo consume ms=\$(( (\$(date +%s%N)-s)/1000000 )); \
  docker exec redpanda rpk topic describe orders -p"
produce ms=222
order-93172
consume ms=174
PARTITION  LEADER  EPOCH  REPLICAS  LOG-START-OFFSET  HIGH-WATERMARK
0          0       1      [0]       0                 38790
1          0       1      [0]       0                 38861
2          0       1      [0]       0                 22350

Three more produce runs into a fresh topic took 187, 210 and 210 ms. The messages are a few bytes each, so this measures per-message overhead, not disk bandwidth. The last message consumed is order-93172, not order-100000: Kafka orders messages within a partition, not across a topic.

3. Produce and consume from your laptop#

Open a forward in one terminal. It runs in the foreground until you press Ctrl-C:

sbx forward $id --remote 19092
{"event":"forwarding","id":"sb_pgzo691h33","reverse":false,"local":19092,"remote":19092,"bind":"127.0.0.1"}

Keep the local port at 19092. The broker advertises localhost:19092, so after the first metadata request your client connects to that address. If you forward to another local port, the client still dials 19092 after the first request (we kept 19092 and did not test other ports).

In a second terminal, any Kafka client works with bootstrap.servers=127.0.0.1:19092. We used confluent-kafka for Python (pip install confluent-kafka). This script, laptop.py, sends 20 messages one at a time, waits for each ack, then reads the whole topic back:

import time, uuid
from confluent_kafka import Producer, Consumer

conf = {"bootstrap.servers": "127.0.0.1:19092"}
p = Producer(conf)
lat = []
for i in range(20):
    t0 = time.perf_counter()
    p.produce("orders", key=f"k{i}", value=f"from-laptop-{i}")
    p.flush()
    lat.append((time.perf_counter() - t0) * 1000)
lat.sort()
print(f"produce+ack median {lat[10]:.0f} ms, min {lat[0]:.0f} ms over {len(lat)} messages")

c = Consumer({**conf, "group.id": f"laptop-{uuid.uuid4().hex[:6]}", "auto.offset.reset": "earliest"})
c.subscribe(["orders"])
n, last = 0, None
deadline = time.time() + 20
while time.time() < deadline:
    m = c.poll(1.0)
    if m is None:
        if n: break
        continue
    if m.error(): print(m.error()); continue
    n += 1; last = m
print(f"consumed {n} messages, last: partition={last.partition()} offset={last.offset()} value={last.value().decode()}")
c.close()
produce+ack median 125 ms, min 125 ms over 20 messages
consumed 100021 messages, last: partition=2 offset=22356 value=from-laptop-17

The 125 ms is the round trip between our client and the sandbox, plus the broker's ack. It depends on where you are. A producer that batches, which is the default when you don't flush() after every message, pays it once per batch. For load tests, run the producer in the sandbox as in step 2.

ssh -L 19092:127.0.0.1:19092 $id.sbx is the same tunnel with plain OpenSSH; we tested sbx forward only for Kafka. CLI builds from 2026-10-27 (7e848c3) on free local port 19092 when you stop sbx forward with kill from a script, as well as with Ctrl-C. With an older CLI, check the port afterwards (lsof -i :19092), because its ssh child could keep listening.

4. Redpanda Console on a private URL#

Redpanda Console is a web UI for topics, messages and consumer groups. It reaches the broker on the internal listener and serves HTTP on 8080, so it fits a hosted URL:

sbx exec $id --timeout 600 "docker run -d --name console --network kafkanet --restart unless-stopped \
  -p 127.0.0.1:8080:8080 -e KAFKA_BROKERS=redpanda:9092 docker.redpanda.com/redpandadata/console:latest"
sbx host $id 8080 --localhost --title "Redpanda Console"

-p 127.0.0.1:8080:8080 keeps Console off the VM's public IPv6 address whatever the firewall does. (In our October 27, 2026 check, ufw did block a Docker-published port there, but Docker can bypass ufw on other setups.) --localhost tells the hosted URL to relay to that loopback port. (Our test run published -p 8080:8080 and hosted it without --localhost; the results below come from that run.)

sbx host prints a private URL like https://zesty-spry-nectar-8080.box.sandboxer.com?_token=…. Console has no login in this setup, so the token is the only lock. Treat the URL like a password. We checked it with curl:

url='https://zesty-spry-nectar-8080.box.sandboxer.com?_token=…'   # the full URL sbx host printed
base=${url%%\?*}
curl -s -o /dev/null -w '%{http_code}\n' "$base/"                  # no token
curl -s -c jar -b jar -L -o /dev/null -w '%{http_code}\n' "$url"   # token
curl -s -b jar "$base/api/topics" | python3 -c 'import sys, json
for t in json.load(sys.stdin)["topics"]: print(t["topicName"], t["partitionCount"], t["isInternal"])'
401
200
__consumer_offsets 3 True
orders 3 False

The 401 comes from Sandboxer: without the token, the request never reaches the sandbox. With it, the page title is Redpanda Console and the topic list includes orders and the consumer group offsets from step 3. Don't use sbx host --public for Console unless you add Console's own authentication; anyone with the link could browse your messages.

5. What survives a stop and resume#

We wrote a marker message, stopped the sandbox, resumed it and read the marker back:

sbx exec $id "echo written-before-stop | docker exec -i redpanda rpk topic produce orders -k marker"
sbx stop $id --wait
sbx resume $id
sbx exec $id "docker ps --format '{{.Names}} {{.Status}}'; \
  docker exec redpanda rpk topic consume orders -n 100022 -f '%k %v\n' | grep marker"
Produced to partition 1 at offset 38865 with timestamp 1793095839469.
console Up About a minute
redpanda Up About a minute
marker written-before-stop

Our first stop and resume took 2.2 and 1.4 seconds. The resume reported memoryRestored: true, so neither container restarted. The Console URL kept working with the same token. A new sbx forward after the resume ran laptop.py again with the same 125 ms median; the docs say a running forward reconnects by itself when the sandbox moves.

A resume boots from disk instead when no machine with the same CPU type is free, or when you move to a bigger size. Then processes are gone, and --restart unless-stopped starts both containers again from the redpanda-data volume. We did not force that case here; the Docker Compose guide shows the pattern. Moving to a smaller size isn't supported yet; create a new sandbox and copy the topics over with a Kafka mirroring tool.

6. Clean up#

sbx delete $id --yes

A default sandbox costs $0.036/hour while it runs and nothing while it is stopped.

FAQ#

Can a Kafka client connect to a hosted HTTPS URL?#

No. A hosted URL is an HTTPS proxy that checks a token on each request, and Kafka clients speak their own binary protocol. We tried it and got Broker transport failure. Use sbx forward or an SSH tunnel for the broker, and a hosted URL for HTTP tools like Redpanda Console.

Why does my client connect, then fail with "connection refused" or "unknown host redpanda"?#

Usually the client reached the bootstrap address, then dialed the advertised address from the metadata. We didn't reproduce this error; it follows from how Kafka clients use advertised listeners. Make sure the listener you reach advertises an address your client can resolve: localhost:19092 for the forward, redpanda:9092 for containers on the Docker network. Forward to the same local port the broker advertises.

Is this real Kafka?#

It is Redpanda, which implements the Kafka API, so Kafka clients and rpk work against it. We tested rpk and Python confluent-kafka. If you need Apache Kafka itself, run the official apache/kafka image with the same listener and port-forward pattern; we did not test it here.

How much memory does it need?#

After the tests, Redpanda used 584 MiB and Console 44 MiB in a default sandbox with 8 GB. Dev-container mode with --smp 1 uses one core. If a container runs out of memory, it exits with code 137 and the sandbox stays up.

Can other sandboxes reach my broker?#

No. Sandboxes can't reach each other's private addresses, and the broker port is bound to 127.0.0.1 in the VM. See what can reach a sandbox.

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