Streaming & Messaging AWS

Apache RocketMQ 5.5.1 on AWS User Guide

| Product: Apache RocketMQ 5.5.1 on Ubuntu 24.04 on AWS

Overview

This guide covers launching and using the Apache RocketMQ 5.5.1 on Ubuntu 24.04 AMI from cloudimg on Amazon EC2. It is one version of the cloudimg streaming and messaging listing on AWS Marketplace; you pick it as the software version when you subscribe and launch.

Apache RocketMQ is a distributed messaging and streaming platform: topics with normal, ordered (FIFO), delayed and transactional messages, consumer groups with clustering and broadcast consumption, message filtering by tag or SQL92, retries and dead-letter queues, message tracing, and a commit-log store built for high throughput and low latency. RocketMQ 5 adds a stateless gRPC proxy that the RocketMQ 5 client SDKs (Java, Python, Go, C++, C#, Node.js, Rust) connect to. The image also includes the RocketMQ Dashboard, a web console for clusters, topics, consumer groups and messages.

What the cloudimg image adds:

  • Authentication and authorization on. RocketMQ's ACL 2.0 is enabled on the broker and the proxy: every client signs its requests with an access key and secret, and every request is checked against that user's policies. Out of the box RocketMQ accepts anyone who can reach it; this image accepts no anonymous client and ships no sample user (the distribution's sample rocketmq2 / 12345678 tools credentials are removed).
  • Users generated on your instance. At first boot each instance creates its own users with random 32-character secrets: admin (a super user for administration on the instance), app (a least-privilege user that may publish to and subscribe from topics and may not administer anything) and the internal identities of the proxy and the Dashboard. The Dashboard has its own, separate login. None of these secrets is in the image, and the first-start admin secret is removed from the broker's configuration as soon as the broker has created the user.
  • Clients over TLS only. The only RocketMQ port reachable from other machines is 8081: TLS 1.2 or 1.3 in front of the RocketMQ 5 gRPC proxy. The NameServer (9876), the broker (10911, 10909, 10912) and the proxy's own ports (18080, 18081) are reachable from the instance only: every RocketMQ service runs with a kernel filter that drops any packet to or from an address that is not the loopback address, so even a security group that allowed those ports would not expose them. The NameServer in RocketMQ 5.5.1 has no authentication at all, which is why it is never reachable off the instance.
  • HTTPS from the first boot. nginx serves the Dashboard on port 443, port 80 redirects to HTTPS, and requests for host names the instance does not serve are dropped. The same certificate secures port 8081. It is an end-entity certificate: it can only vouch for this instance's own names and can never sign other certificates. Install your own CA-issued certificate with one command (Step 11); the boot-time renewal never replaces it.
  • A guarded Dashboard. Its own login is required for every page and API call (including the proxy pages, which the upstream Dashboard leaves open), logins are rate limited, a request carrying another site's Origin is refused, so is every WebSocket upgrade, and the Dashboard's read and refresh calls that fetch from a broker or proxy address only go through when the browser proves the request comes from the Dashboard itself. The session cookie is Secure, HttpOnly and SameSite=Strict. The Spring Boot actuator and the Dashboard's demo test endpoint are not served.
  • Fail closed. Every RocketMQ service starts only after first boot has written its secrets, and a configuration check refuses to start a service with authentication or authorization switched off, a public address, topic auto-creation, an authentication whitelist, a missing secret or a Dashboard without its login.
  • Hardened defaults. Every service is blocked from the instance metadata service (the one small service that reads the instance's own host names is allowed to reach nothing else), the AMI requires IMDSv2, nginx hands RocketMQ the real client address and the configured host name (never values a client sends), the services run under systemd sandboxing as unprivileged users, topics are created by an administrator (never by a client), and no JVM writes a heap dump.
  • Fully patched Ubuntu 24.04 LTS at build time, with unattended security upgrades switched on.

What is on the image: Apache RocketMQ 5.5.1 (the official Apache binary release, verified against its published SHA-512 digest and its release signature), RocketMQ Dashboard 2.1.0 (built by cloudimg from the signed Apache source release), OpenJDK 17 (from Ubuntu 24.04), nginx 1.24 with its stream module (from Ubuntu 24.04), cloud-init and the AWS Systems Manager agent.

Default user: ubuntu · Architecture: x86_64 · Recommended instance type: m5.large

The RocketMQ Dashboard cluster page showing broker-a at 127.0.0.1:10911 running version V5_5_1 with today's produce and consume counts

Prerequisites

  • An AWS account subscribed to the listing, an EC2 key pair and a VPC subnet with a route to the internet (or a private route you use for management).
  • A security group that allows TCP 22 (SSH) from your admin network, TCP 8081 (RocketMQ clients over TLS) from the networks your applications connect from and TCP 443 (the Dashboard) from your admin network, plus TCP 80 if you want plain HTTP requests redirected to HTTPS. There is nothing to gain from opening 9876, 10909, 10911, 10912, 18080, 18081 or 8082: the instance drops those connections.
  • The message store lives on the root volume. The AMI's root volume is 8 GiB; for production, launch with a larger root volume (for example 50 GiB or more, sized for your retention). The root partition grows to the volume size at first boot.

Step 1: Launch the instance

In the AWS Marketplace listing choose Continue to Subscribe, then Continue to Configuration, pick Apache RocketMQ 5.5.1 on Ubuntu 24.04 as the software version and your Region, then Launch through EC2. Choose m5.large, your key pair, a security group as above and the root volume size you need.

With the AWS CLI (replace the values in angle brackets):

aws ec2 run-instances --region <region> --image-id <ami-id-from-the-listing> \
  --instance-type m5.large --key-name <your-key-pair> \
  --security-group-ids <your-security-group> --subnet-id <your-subnet> \
  --block-device-mappings 'DeviceName=/dev/sda1,Ebs={VolumeSize=50,VolumeType=gp3}' \
  --metadata-options HttpTokens=required,HttpEndpoint=enabled \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=rocketmq}]'

First boot takes about four minutes after the instance is running (the first boot of a new volume also loads its blocks from the snapshot): the boot service generates the secrets and the certificate, the NameServer and the broker start, the broker creates the admin user, a one-off bootstrap creates the other users and the self-test topics, then the proxy, the Dashboard and nginx start.

Step 2: Connect to your instance

Version SSH user
Apache RocketMQ 5.5.1 on Ubuntu 24.04 ubuntu
ssh -i <your-key.pem> ubuntu@<public-ip>

Step 3: Check the services

systemctl is-active rocketmq-metadata-names rocketmq-firstboot rocketmq-namesrv rocketmq-broker rocketmq-proxy rocketmq-dashboard nginx
sudo test -f /var/lib/cloudimg/rocketmq-firstboot.done && echo FIRSTBOOT_DONE

All seven units are active and the first boot marker is present:

active
active
active
active
active
active
active
FIRSTBOOT_DONE

Only SSH and nginx (80, 443 and 8081) accept connections from other machines. The NameServer, the broker and the Dashboard listen on the loopback address; RocketMQ cannot bind the broker's HA port or the proxy's ports to one address, so those three show *, and the kernel filter on their services drops every packet that does not come from the instance itself:

sudo ss -lntp | awk 'NR>1 {print $4, $6}' | grep -v '127.0.0.5' | sed -E 's/,pid=[0-9]+,fd=[0-9]+//g' | sort
*:10912 users:(("java"))
*:18080 users:(("java"))
*:18081 users:(("java"))
0.0.0.0:22 users:(("sshd"),("systemd"))
0.0.0.0:443 users:(("nginx"),("nginx"),("nginx"))
0.0.0.0:80 users:(("nginx"),("nginx"),("nginx"))
0.0.0.0:8081 users:(("nginx"),("nginx"),("nginx"))
[::]:22 users:(("sshd"),("systemd"))
[::]:443 users:(("nginx"),("nginx"),("nginx"))
[::]:80 users:(("nginx"),("nginx"),("nginx"))
[::]:8081 users:(("nginx"),("nginx"),("nginx"))
[::ffff:127.0.0.1]:10909 users:(("java"))
[::ffff:127.0.0.1]:10911 users:(("java"))
[::ffff:127.0.0.1]:8082 users:(("java"))
[::ffff:127.0.0.1]:9876 users:(("java"))

Step 4: Get your endpoint, users and certificate fingerprint

sudo cat /root/rocketmq-credentials.txt

The file is readable by root only:

# Apache RocketMQ 5.5.1 on Ubuntu 24.04 - generated on first boot by rocketmq-firstboot.service.
# Every secret below is unique to this instance; none was baked into the image. Keep this file secret.
# Clients connect over TLS ONLY, to ROCKETMQ_GRPC_ENDPOINT (the RocketMQ 5 gRPC proxy; the NameServer and broker
# ports are reachable from this instance only). Authentication and authorization (ACL 2.0) are on:
#   app   - a Normal user that may publish to and subscribe from topics (no administration): give it to applications
#   admin - a Super user for administration on this instance: sudo rocketmq-admin <mqadmin subcommand> ...
# The certificate is self signed (minted on this instance): check that the SHA-256 fingerprint your client sees
# matches ROCKETMQ_TLS_CERT_SHA256 below, then pin /etc/cloudimg/rocketmq-broker.pem in that client's OWN trust
# file - or install a CA-issued certificate with   sudo rocketmq-install-cert <chain.pem> <key.pem>
# The Dashboard (HTTPS) has its own login (ROCKETMQ_DASHBOARD_USER / _PASSWORD).
# Change the served name: sudo rocketmq-set-hostname <dns-name>      Re-verify: sudo rocketmq-selftest
ROCKETMQ_GRPC_ENDPOINT=<public-dns>:8081
ROCKETMQ_TLS_CERT_SHA256=<sha256-fingerprint>
ROCKETMQ_TLS_CERT_FILE=/etc/cloudimg/rocketmq-broker.pem
ROCKETMQ_APP_ACCESS_KEY=app
ROCKETMQ_APP_SECRET_KEY=<ROCKETMQ_APP_SECRET_KEY>
ROCKETMQ_ADMIN_ACCESS_KEY=admin
ROCKETMQ_ADMIN_SECRET_KEY=<ROCKETMQ_ADMIN_SECRET_KEY>
ROCKETMQ_DASHBOARD_URL=https://<public-dns>/
ROCKETMQ_DASHBOARD_USER=admin
ROCKETMQ_DASHBOARD_PASSWORD=<ROCKETMQ_DASHBOARD_PASSWORD>
# User guide: https://www.cloudimg.co.uk/guides/apache-rocketmq-5-5-1-on-ubuntu-24-04-aws/

The host is the instance's public DNS name from the EC2 instance metadata (the public IP address, then the private address, when it has none). The certificate also names the public and private IP addresses and the private DNS name, so clients may connect with any of them. If you stop and start the instance without an Elastic IP, the boot service picks up the new names and issues a new certificate for them (unless you installed your own, Step 11). To use your own DNS name, see Step 11.

Print the certificate's SHA-256 fingerprint and its names:

sudo openssl x509 -in /etc/cloudimg/rocketmq-broker.pem -noout -fingerprint -sha256
openssl x509 -in /etc/cloudimg/rocketmq-broker.pem -noout -ext subjectAltName,basicConstraints
sha256 Fingerprint=<sha256-fingerprint>
X509v3 Subject Alternative Name: 
    DNS:<public-dns>, IP Address:<public-ip>, IP Address:<private-ip>, DNS:ip-<private-ip-dashed>.ec2.internal, IP Address:127.0.0.1, DNS:localhost
X509v3 Basic Constraints: critical
    CA:FALSE

How to trust this certificate. The certificate is self signed and unique to this instance. Before a client trusts it, verify the SHA-256 fingerprint that client sees against ROCKETMQ_TLS_CERT_SHA256 in the credentials file (read over SSH, as above). Then pin it in that client's own trust file only (a certificate file that only this application uses, as in Step 8). Never add it to an operating-system, browser or JVM-wide trust store. For clients you cannot configure that way, install a certificate from a certificate authority instead (Step 11).

Step 5: The RocketMQ Dashboard

Open https://<public-dns>/ in your browser. It warns that the certificate is self signed: open the certificate details, check that its SHA-256 fingerprint matches the one in the credentials file, and only then continue to the site. Log in as admin with ROCKETMQ_DASHBOARD_PASSWORD. After 10 failed logins a minute the Dashboard answers 429 for a while.

The RocketMQ Dashboard login page

The Cluster page (shown at the top of this guide) lists the broker with its version, throughput and today's message counts. The Topic page lists the topics; Add / Update creates one, and each row has buttons for its status, routing, consumers, configuration, sending a test message, resetting consumer offsets and deleting it:

The Topic page listing the topics cloudimg-denied, cloudimg-selftest and orders with their operation buttons

Status shows each queue of the topic with its offsets:

The status of the orders topic: four message queues on broker-a with their minimum and maximum offsets

The Message page finds messages by topic and time range, by key or by message ID, and shows each message's properties and body:

The Message page, Message Key tab: the message with key order-1003 and tag created in the orders topic

The Consumer page lists the consumer groups with their clients, consumption mode and backlog:

The Consumer page listing two subscription groups with their quantity, version, type, mode and last update

Notes:

  • The Dashboard acts on the broker as a super user. Anyone with the Dashboard login can create and delete topics, groups, users and policies, reset offsets and read messages: share it only with RocketMQ administrators.
  • The Dashboard's ACL user list displays users' secrets, as does mqadmin listUser. Do not take screenshots of that page or paste its contents anywhere.
  • nginx refuses (400) a URL whose path contains ; or an encoded /, ., \ or NUL.

Step 6: Administer RocketMQ with rocketmq-admin

rocketmq-admin runs Apache RocketMQ's mqadmin against this instance as the admin user (or with --as app, the app user). It reads the access key and secret from the credentials file and hands them to mqadmin in a file that exists only while the command runs, so no secret ever appears on a command line or in the process list. Every mqadmin subcommand works; the NameServer address is set for you:

sudo rocketmq-admin clusterList | cut -c1-100
#Cluster Name           #Broker Name            #BID  #Addr                  #Version              #
DefaultCluster          broker-a                0     127.0.0.1:10911        V5_5_1                

Clients cannot create topics: an administrator creates each one. RocketMQ 5 clients need the topic's message type (NORMAL, FIFO, DELAY or TRANSACTION):

sudo rocketmq-admin updateTopic -b 127.0.0.1:10911 -t orders -r 4 -w 4 -a +message.type=NORMAL
create topic to 127.0.0.1:10911 success.
TopicConfig [topicName=orders, readQueueNums=4, writeQueueNums=4, perm=RW-, topicFilterType=SINGLE_TAG, topicSysFlag=0, order=false, attributes={+message.type=NORMAL}]

Give each application its own user with only the permissions it needs. Generate the secret on the instance into a file readable by root only; --secret-file puts it in place of the literal argument @SECRET@, so it never appears on a command line:

sudo bash -c 'umask 077; openssl rand -hex 16 > /root/orders-service.secret'
sudo rocketmq-admin --secret-file /root/orders-service.secret createUser -b 127.0.0.1:10911 -u orders-service -p @SECRET@ -t Normal
create user to 127.0.0.1:10911 success.

Allow it to publish to and subscribe from the orders topic only, with consumer groups whose names start with orders-:

sudo rocketmq-admin createAcl -b 127.0.0.1:10911 -s User:orders-service -r Topic:orders -a Pub,Sub -d Allow
sudo rocketmq-admin createAcl -b 127.0.0.1:10911 -s User:orders-service -r Group:orders-* -a Sub -d Allow
create acl to 127.0.0.1:10911 success.
create acl to 127.0.0.1:10911 success.

List the policies (this output contains no secrets):

sudo rocketmq-admin listAcl -b 127.0.0.1:10911
#Subject          #PolicyType  #Resource               #Actions              #SourceIp                 #Decision 
User:app          Custom      Topic:*                 [Pub, Sub]            null                      Allow     
User:app          Custom      Group:*                 [Sub]                 null                      Allow     
User:app          Custom      Topic:cloudimg-denied   [Pub, Sub]            null                      Deny      
User:orders-service  Custom      Topic:orders            [Pub, Sub]            null                      Allow     
User:orders-service  Custom      Group:orders-*          [Sub]                 null                      Allow     

The app user may publish and subscribe on every topic except cloudimg-denied, a topic the self-test uses to prove that a denial works. It cannot administer anything:

sudo rocketmq-admin --as app updateTopic -b 127.0.0.1:10911 -t not-allowed 2>&1 | grep -o 'DESC: .*'
DESC: User:app has no permission to access Topic:not-allowed from 127.0.0.1, no matched policies.

Notes:

  • mqadmin listUser and getUser print users' secrets in clear: never paste their output anywhere. To change a user's secret, write the new one to a root-only file and run sudo rocketmq-admin --secret-file <file> updateUser -b 127.0.0.1:10911 -u <user> -p @SECRET@. The proxy caches users for up to a minute.
  • deleteUser, deleteAcl and updateAcl manage users and policies; resources are Topic:<name>, Group:<name>, a prefix such as Topic:orders-*, or *, and actions are Pub, Sub, Create, Update, Delete, Get, List or All. A policy can also be limited to source addresses with -i <cidr>; the proxy sees each client's real address (Step 10).
  • Do not change the admin, app, rocketmq-proxy or rocketmq-dashboard users' type, and keep rocketmq-proxy and rocketmq-dashboard as super users: the proxy and the Dashboard log in with them.

Step 7: Send a test message

On the instance, /usr/local/lib/cloudimg/rocketmq-grpc-probe.py (the self-test's client, Python standard library only) sends one message over the same TLS port your clients use, as the app user, reading the secret from the credentials file:

H=$(sudo sed -n 's/^ROCKETMQ_GRPC_ENDPOINT=\(.*\):8081$/\1/p' /root/rocketmq-credentials.txt)
printf '{"order": 1001, "status": "CREATED"}' > /tmp/order.json
sudo python3 -I /usr/local/lib/cloudimg/rocketmq-grpc-probe.py send "$H" --addr 127.0.0.1 \
  --creds /root/rocketmq-credentials.txt --topic orders --tag created --key order-1001 --body-file /tmp/order.json
sudo python3 -I /usr/local/lib/cloudimg/rocketmq-grpc-probe.py route "$H" --addr 127.0.0.1 \
  --creds /root/rocketmq-credentials.txt --topic cloudimg-denied --expect-code 40100
rm -f /tmp/order.json
GRPC send status=20000 message=OK message_id=<message-id>
GRPC route status=40100 message=User:app has no permission to access Topic:cloudimg-denied from 127.0.0.1, the decision is deny. queues=0

The message is now on the Message page under the key order-1001.

Step 8: Connect your applications over TLS

Applications connect with a RocketMQ 5 client SDK to ROCKETMQ_GRPC_ENDPOINT (<public-dns>:8081) with TLS on, using the app access key and secret (or a user of their own, Step 6). The proxy answers with routes for the same address and port, so it works with any name in the certificate, including your own DNS name (Step 11). Clients that use the older RocketMQ remoting protocol (DefaultMQProducer with a NameServer address) are not served off the instance: use the RocketMQ 5 SDKs.

First, check the certificate. Copy the public certificate over SSH, then check its fingerprint against ROCKETMQ_TLS_CERT_SHA256 from the credentials file before you trust it:

scp -i <your-key.pem> ubuntu@<public-ip>:/etc/cloudimg/rocketmq-broker.pem ./rocketmq-broker.pem
openssl x509 -in rocketmq-broker.pem -noout -fingerprint -sha256
openssl s_client -connect <public-dns>:8081 -servername <public-dns> -alpn h2 </dev/null 2>/dev/null | openssl x509 -noout -fingerprint -sha256

Both fingerprints must equal ROCKETMQ_TLS_CERT_SHA256. Put the endpoint and the credentials in a file readable only by the application's user (never on a command line or in the source code):

install -m 0600 /dev/null ~/rocketmq-client.env
# copy these lines from /root/rocketmq-credentials.txt on the instance:
#   ROCKETMQ_GRPC_ENDPOINT=...   ROCKETMQ_APP_ACCESS_KEY=...   ROCKETMQ_APP_SECRET_KEY=...

Python (the official SDK, pip install rocketmq-python-client==5.1.2). The SDK asks gRPC for its default trust roots; the example gives it this instance's certificate instead, so only this program trusts it. It publishes a message and consumes it back:

import os, sys, time, uuid
creds = dict(l.strip().split("=", 1) for l in open(os.path.expanduser("~/rocketmq-client.env")) if "=" in l and not l.startswith("#"))
import grpc
pinned = open("rocketmq-broker.pem", "rb").read()      # this application's own trust file
_creds = grpc.ssl_channel_credentials
grpc.ssl_channel_credentials = lambda root_certificates=None, private_key=None, certificate_chain=None: \
    _creds(pinned, private_key, certificate_chain)
from rocketmq import ClientConfiguration, Credentials, FilterExpression, Message, Producer, SimpleConsumer

config = ClientConfiguration(creds["ROCKETMQ_GRPC_ENDPOINT"],
                             Credentials(creds["ROCKETMQ_APP_ACCESS_KEY"], creds["ROCKETMQ_APP_SECRET_KEY"]))
body = ("hello from %s" % uuid.uuid4().hex[:8]).encode()

producer = Producer(config, ("orders",), tls_enable=True)
producer.startup()
msg = Message(); msg.topic = "orders"; msg.tag = "example"; msg.body = body
print("sent", producer.send(msg).message_id)
producer.shutdown()

consumer = SimpleConsumer(config, "orders-example", {"orders": FilterExpression("example")}, await_duration=5, tls_enable=True)
consumer.startup()
time.sleep(5)
for m in consumer.receive(16, 15) or []:
    consumer.ack(m)
    print("received", m.body.decode())
consumer.shutdown()
sent 0112FFCF6E1DB707820AF47E4E00000000
received hello from 85e9adcb

Without the pinned certificate the same program refuses the server (failed to fetch topic route, with CERTIFICATE_VERIFY_FAILED in its log), and a wrong secret is refused with check signature failed.

Java and other SDKs. Enable TLS in the client configuration (Java: ClientConfiguration.newBuilder().setEndpoints("<public-dns>:8081").enableSsl(true).setCredentialProvider(new StaticSessionCredentialsProvider(accessKey, secretKey)), reading the key and secret from your secret store). Be aware that the RocketMQ 5 Java SDK does not verify the server certificate at all (it uses an insecure trust manager), so with it TLS encrypts the traffic but does not prove which server you reached. Restrict TCP 8081 to your application networks, and check the fingerprint with openssl s_client as above. Other SDKs that use gRPC's default trust roots behave like the Python SDK: give them the pinned certificate, or install a CA-issued certificate (Step 11).

Step 9: Security checks

rocketmq-selftest re-proves the whole appliance with this instance's credentials: the exact listening set and the loopback-only ports, a TLS round trip as the app user (the message is then found in the broker), refusal of a wrong secret, an anonymous client, the distribution's sample user and a denied topic, refusal of cleartext gRPC and of a client expecting another host name, the broker's refusal of anonymous and sample-user administration, the Dashboard's login wall, the redirect, refusal of other host names, foreign origins, WebSocket upgrades and ; paths, the cross-site guard, the instance metadata block and the certificate on 443 and 8081:

sudo rocketmq-selftest
Apache RocketMQ 5.5.1 appliance self-test (gRPC over TLS <public-dns>:8081, Dashboard https://<public-dns>/)
  [ok] listening set: 22, 80, 443, 8081 public; 9876, 10909, 10911, 8082 on 127.0.0.1; 10912/18080/18081 refuse <private-ip> (loopback control connects)
  [ok] TLS 8081: ALPN h2, the certificate pinned (SHA-256 = ROCKETMQ_TLS_CERT_SHA256), hostname <public-dns> checked
  [ok] app user QueryRoute over TLS: status 20000 (queues=4)
  [ok] wrong secret refused over TLS (40100, the signature check)
  [ok] anonymous request refused over TLS (40100, no username)
  [ok] the distribution's sample user rocketmq2 / 12345678 does not exist (40100)
  [ok] authorization: app denied Topic:cloudimg-denied (40100; the proxy saw the client as <private-ip>, from the socket via the PROXY header)
  [ok] round trip: app SendMessage over TLS -> message <message-id> stored in the broker (found by key cloudimg-st-<random> as admin)
  [ok] authorization: app SendMessage to Topic:cloudimg-denied refused (40100, decision deny)
  [ok] cleartext gRPC on 8081 refused ((0 bytes back, no HTTP/2 frame))
  [ok] the certificate is checked: a client expecting another host name refuses it
  [ok] remoting (127.0.0.1): admin clusterList OK; app cannot create a topic; anonymous and rocketmq2/12345678 refused by the broker
  [ok] Dashboard: anonymous API -> 302 to its login (/proxy/ too); wrong password refused; login OK (Secure session cookie); cluster listed; /actuator and /test/ 404
  [ok] front door: plain HTTP -> 301 https://<public-dns>/; unknown Host -> 444; foreign Origin 403; WebSocket 403; ';' 400; CONNECT refused
  [ok] cross-site GET guard: 13/13 (/rocketmq/nsaddr.query)
  [ok] cross-site GET guard: 13/13 (/ops/homePage.query)
  [ok] cross-site GET guard: 13/13 (/cluster/list.query)
  [ok] cross-site GET guard: 13/13 (/topic/list.query)
  [ok] instance metadata refused inside the namesrv, broker, proxy, Dashboard and nginx cgroups (loopback control 200); the RocketMQ units reach nothing off-host
  [ok] TLS certificate: end-entity (CA:FALSE, serverAuth, no certificate signing), names <public-dns>; served on 443 and 8081; SHA-256 <sha256-fingerprint>
SELFTEST OK - RocketMQ over TLS with ACL 2.0 (app round trip; wrong secret, anonymous, sample user and denied topic refused), loopback-only broker/NameServer/proxy, Dashboard login wall, origin and cross-site guards, exact listening set, metadata block verified.

Step 10: The instance metadata block and the loopback-only services

The NameServer, the broker, the proxy and the Dashboard may exchange packets with the loopback address only (IPAddressAllow=localhost, IPAddressDeny=any), so they can neither be reached from, nor reach, any other address, the instance metadata service (169.254.169.254) included. nginx and the boot service are denied the metadata service. The only service allowed to reach it is rocketmq-metadata-names, which runs once at boot, reads the instance's four host names and addresses (no credentials) for the certificate and the endpoint, and can reach nothing else:

for u in rocketmq-namesrv rocketmq-broker rocketmq-proxy rocketmq-dashboard nginx rocketmq-firstboot rocketmq-metadata-names; do printf '%s: deny=%s allow=%s\n' $u "$(systemctl show -p IPAddressDeny --value $u.service)" "$(systemctl show -p IPAddressAllow --value $u.service)"; done
rocketmq-namesrv: deny=::/0 0.0.0.0/0 allow=127.0.0.0/8 ::1/128
rocketmq-broker: deny=::/0 0.0.0.0/0 allow=127.0.0.0/8 ::1/128
rocketmq-proxy: deny=::/0 0.0.0.0/0 allow=127.0.0.0/8 ::1/128
rocketmq-dashboard: deny=::/0 0.0.0.0/0 allow=127.0.0.0/8 ::1/128
nginx: deny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 allow=
rocketmq-firstboot: deny=169.254.169.254/32 168.63.129.16/32 fd00:ec2::254/128 allow=
rocketmq-metadata-names: deny=::/0 0.0.0.0/0 allow=169.254.169.254/32 fd00:ec2::254/128

Clients reach the proxy through nginx, which terminates TLS on 8081 and passes the connection on with the client's real address in a PROXY protocol header. The proxy uses that address in its logs and in policies limited to source addresses (Step 6); a client cannot supply its own. Consequence of the block: RocketMQ cannot use the instance's IAM role, and nothing in this image needs it.

Step 11: Your own DNS name and a CA-issued certificate

Point a DNS record at the instance, then tell the appliance to serve that name. This updates nginx, issues a new self signed end-entity certificate naming it (for both 443 and 8081), accepts https://<name> as the Dashboard's origin and updates the endpoint, URL and fingerprint in the credentials file:

sudo rocketmq-set-hostname rocketmq.<your-domain>

The name is saved in /etc/cloudimg/rocketmq-hostname and survives reboots. For a certificate clients trust without pinning, install one from a certificate authority (your own CA, or Let's Encrypt). The chain file holds the server certificate first, then any intermediates; the key is PEM and unencrypted:

sudo rocketmq-install-cert /path/to/fullchain.pem /path/to/privkey.pem rocketmq.<your-domain>

rocketmq-install-cert checks that the key matches the certificate and that the certificate covers the name, installs it for nginx on 443 and 8081 and reloads nginx. The boot-time renewal never replaces a certificate installed this way, even if the instance's address changes. Running rocketmq-set-hostname <name> returns to a self signed certificate.

Step 12: Versions, configuration and file locations

ls /opt/rocketmq /opt/rocketmq-dashboard
sha256sum /opt/rocketmq/current/LICENSE /opt/rocketmq-dashboard/2.1.0/LICENSE
/usr/lib/jvm/java-17-openjdk-amd64/bin/java -version 2>&1 | head -1
/opt/rocketmq:
5.5.1
current

/opt/rocketmq-dashboard:
2.1.0
0bc6664f53f3cad2a55980d8b91871dd8ba4caaeea0f23225c076b417986a35b  /opt/rocketmq/current/LICENSE
3c45d82fe86d0e6982bde4a940b936cb3141e160f9a0b7e69dc4bf10953ff657  /opt/rocketmq-dashboard/2.1.0/LICENSE
openjdk version "17.0.20.1" 2026-08-18
Path What it is
/var/lib/rocketmq/store/ the broker's store: commit log, consume queues, topic and group configuration, and the users and policies (config/users, config/acls)
/etc/rocketmq/broker.conf, /etc/rocketmq/namesrv.conf broker and NameServer configuration (restart the service after a change; the configuration check refuses unsafe values)
/etc/rocketmq/rmq-proxy.json the proxy's configuration, including its own user's secret (root and the rocketmq group only)
/etc/rocketmq-dashboard/application.yml, /var/lib/rocketmq-dashboard/data/users.properties the Dashboard's configuration (its ACL identity) and its login
/root/rocketmq-credentials.txt the endpoint, the app and admin users, the Dashboard login and the certificate fingerprint (root only)
/etc/cloudimg/rocketmq-broker.pem the public certificate for clients to pin
/etc/nginx/rocketmq-stream.conf, /etc/nginx/sites-available/cloudimg-rocketmq the TLS front doors (8081 and 443); certificate and key in /etc/nginx/ssl/
/var/lib/rocketmq/logs/rocketmqlogs/, journalctl -u rocketmq-broker broker, proxy and NameServer logs (also rocketmq-proxy, rocketmq-namesrv, rocketmq-dashboard, nginx)

Step 13: Backup and maintenance

  • Back up /var/lib/rocketmq/store with the broker stopped (sudo systemctl stop rocketmq-proxy rocketmq-broker), together with /etc/rocketmq, /etc/rocketmq-dashboard, /var/lib/rocketmq-dashboard/data, /etc/cloudimg, /etc/nginx/ssl and the credentials file. The users and their secrets are inside the store.
  • The broker removes commit log files older than 48 hours at 04:00 by default (fileReservedTime and deleteWhen in /etc/rocketmq/broker.conf) and refuses new messages when the disk is nearly full: size the root volume for your retention, or grow it (the file system grows with the volume after a reboot).
  • The JVMs are sized for an m5.large: NameServer 512 MB, broker 1 GB heap plus 1 GB direct memory, proxy 512 MB, Dashboard 512 MB. On a larger instance raise -Xms/-Xmx in a drop-in (sudo systemctl edit rocketmq-broker, override ExecStart) and restart.
  • Ubuntu security updates install automatically (unattended-upgrades), including the OpenJDK runtime. RocketMQ itself lives in /opt/rocketmq; to upgrade, launch the newer version from the listing and move your producers and consumers across.

Step 14: Security recommendations

  • Limit TCP 8081 in the security group to the networks your applications connect from, and TCP 443 and 22 to your admin network.
  • Give each application its own user with policies for its own topics and groups only (Step 6), instead of sharing app. Keep admin on the instance.
  • Serve RocketMQ under your own DNS name with a certificate from a trusted CA (Step 11). Until then, check the self signed certificate's fingerprint (Step 4) and pin it per client (Step 8) rather than adding it to a trust store.
  • Change the app secret if you hand the credentials file around (updateUser, Step 6), and give other people their own Dashboard logins only if they administer RocketMQ.
  • Keep the NameServer and the broker on the loopback address: they have no TLS, and the NameServer has no authentication.

Support and licensing

cloudimg provides 24/7 technical support for this image by email and live chat: support@cloudimg.co.uk. Apache RocketMQ and the RocketMQ Dashboard are licensed under the Apache License 2.0 (their LICENSE and NOTICE files are on the instance in /opt/rocketmq/current and /opt/rocketmq-dashboard/2.1.0), OpenJDK under the GNU General Public License version 2 with the Classpath Exception and nginx under the 2-clause BSD licence. Apache RocketMQ, RocketMQ and Apache are trademarks of the Apache Software Foundation; cloudimg is not affiliated with or endorsed by the Apache Software Foundation.