OW2 JORAM JMS Message Broker on Ubuntu 24.04 on Azure User Guide
Overview
OW2 JORAM (Java Open Reliable Asynchronous Messaging) is a mature, standalone JMS 2.0 message broker from the OW2 consortium. It implements the full Java Message Service model - queues for point to point delivery and topics for publish and subscribe - with persistent, transactional message storage so messages survive a restart. Applications connect over JORAM's TCP protocol or through its built in JNDI naming service, exchanging messages without needing to be online at the same time.
The cloudimg image installs the stock upstream JORAM 5.24.0 release (LGPL-2.1) on Ubuntu 24.04 LTS, running as a single agent server under an Apache Felix OSGi container. It is secure by default: the broker binds only to the VM's loopback interface, a host firewall blocks the JMS and JNDI ports from outside the VM, the unauthenticated OSGi remote shell is removed, and there is no default login - each VM generates its own broker admin password and a dedicated messaging user on first boot. Backed by 24/7 cloudimg support.
What is included:
- OW2 JORAM 5.24.0 message broker (JMS 2.0), running as
joram.service - Ubuntu's security patched OpenJDK 11 runtime (
openjdk-11.0.31) - JMS TCP endpoint on
127.0.0.1:16010and JNDI naming on127.0.0.1:16400 - A ready to use messaging user
cloudimgand acloudimg.demoqueue, provisioned on first boot - Per-VM broker admin and messaging passwords generated on first boot into a root only credentials file
- A host firewall (nftables) that blocks the JMS/JNDI ports from outside the VM
- 24/7 cloudimg support
This is a messaging appliance: JORAM is reached locally on the VM or over an SSH tunnel. There is no web console. The message bus is never exposed as an open bus on the public internet - you open it to remote clients deliberately, as shown in the last step.
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a good starting point. NSG inbound: allow 22/tcp from your management network only. No inbound application ports are needed because JORAM is reached over the SSH tunnel unless you deliberately open it.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for JORAM by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) only. Then Review + create then Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name joram \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm JORAM is installed and running
systemctl is-active joram.service
systemctl is-enabled joram.service joram-firstboot.service nftables.service
ss -tln | grep -E ':(16010|16400) '
You should see the broker active, the units enabled, and the JMS (16010) and JNDI (16400) listeners bound on the VM:
active
enabled
enabled
enabled
LISTEN 0 50 *:16400 *:*
LISTEN 0 10 *:16010 *:*

Step 5 - Retrieve your per-VM credentials
On first boot each VM generates its own broker admin password and a dedicated messaging user password, and writes them to a root only file. There is no default login.
sudo cat /root/joram-credentials.txt
# OW2 JORAM - generated on first boot by joram-firstboot.service.
# These credentials are unique to this VM. Store them somewhere safe.
JORAM_ADMIN_USER=root
JORAM_ADMIN_PASSWORD=****************
JORAM_APP_USER=cloudimg
JORAM_APP_PASSWORD=****************
#
# The JORAM broker listens on LOCALHOST ONLY (secure by default):
# JMS core (TCP proxy): tcp://127.0.0.1:16010
# JNDI naming service: 127.0.0.1:16400
# A ready-to-use messaging user 'cloudimg' and queue 'cloudimg.demo' are provisioned.

Step 6 - Prove messaging works: a real send and receive
The image ships a self test that connects as the per-VM cloudimg user and performs a real JMS round trip - it sends a uniquely tagged message to the cloudimg.demo queue and receives it back, asserting the exact payload. It also proves that anonymous, default (root/root) and wrong password connections are all refused.
sudo /usr/local/sbin/joram-smoke.sh verify
ROUNDTRIP_OK payload=cloudimg-joram-smoke-1786175951075
NEGATIVE_OK anonymous+default-root+wrong-pw all refused
ALL_OK
ROUNDTRIP_OK confirms end to end messaging with the per-VM credentials; NEGATIVE_OK confirms there is no unauthenticated or default access.

Step 7 - Secure by default
The broker's ports are blocked from outside the VM by a host firewall (input policy drop, only SSH and the Azure platform allowed), the unauthenticated OSGi remote shell is removed, and the credentials file is root only.
sudo nft list ruleset | sed -n '/chain input/,/}/p'
ss -tln | grep -E ':6666 ' || echo 'no unauthenticated management port (6666) listening'
sudo stat -c '%a %U:%G' /root/joram-credentials.txt
chain input {
type filter hook input priority filter; policy drop;
ct state established,related accept
ct state invalid drop
iif "lo" accept
ip protocol icmp accept
ip6 nexthdr ipv6-icmp accept
ip saddr 168.63.129.16 accept
udp sport 67 udp dport 68 accept
tcp dport 22 accept
}
no unauthenticated management port (6666) listening
600 root:root

Step 8 - Connect your own JMS application
The cloudimg messaging user and the cloudimg.demo queue are already provisioned. From code running on the VM, connect to tcp://127.0.0.1:16010 with the JORAM client library (the jars ship under /opt/joram/current/ship/bundle). A minimal Java producer/consumer looks like this:
import javax.jms.*;
import org.objectweb.joram.client.jms.tcp.TcpConnectionFactory;
import org.objectweb.joram.client.jms.Queue;
ConnectionFactory cf = TcpConnectionFactory.create("localhost", 16010);
Connection cnx = cf.createConnection("cloudimg", "<JORAM_APP_PASSWORD>");
Session s = cnx.createSession(false, Session.AUTO_ACKNOWLEDGE);
cnx.start();
Queue q = Queue.create(0, "cloudimg.demo");
s.createProducer(q).send(s.createTextMessage("hello"));
Message m = s.createConsumer(q).receive(5000);
System.out.println(((TextMessage) m).getText());
To reach the broker from another host, tunnel over SSH so the bus stays private:
ssh -L 16010:127.0.0.1:16010 azureuser@<vm-public-ip>
To expose JORAM to remote clients directly instead, edit /var/lib/joram/a3servers.xml to advertise the VM's address, allow the port in the host firewall (/etc/nftables.conf), and open the matching inbound rule in your Azure NSG - only after you have reviewed who should reach the message bus.
Support
Every cloudimg image is fully patched at build time and backed by 24/7 support. For help with this image, contact cloudimg support.