ElasticMQ on Ubuntu 24.04 on Azure User Guide
Overview
ElasticMQ is an open source, in-memory message queue that speaks the Amazon SQS API. Point the AWS CLI, boto3 or any AWS SDK at its endpoint with --endpoint-url and you get a fully local, SQS-compatible queue with no AWS account, no network egress and no per-message cost. It is the go-to way to run SQS-backed applications on a laptop, in CI pipelines and in air-gapped environments, and to write fast, deterministic integration tests against real queue semantics rather than mocks.
The cloudimg image installs the pinned official ElasticMQ 1.7.1 server jar (OpenJDK 17) running under systemd, then locks it down for a marketplace appliance. ElasticMQ ships upstream with no authentication of any kind, so this image binds it to loopback only and lets nginx do all customer-facing work: port 9324 (the well-known ElasticMQ port) is the drop-in SQS endpoint your AWS SDK clients point at — deliberately unauthenticated because SQS clients sign with SigV4 and cannot supply HTTP Basic Auth — and port 80 fronts the same SQS API behind a per-VM HTTP Basic Auth gate for authenticated access from a browser or curl. There is no known bootstrap credential in the image: the Basic Auth password (user admin) is generated uniquely on the first boot of each VM. Backed by 24/7 cloudimg support.
What is included:
- ElasticMQ 1.7.1 installed as the official fat jar, running as the
elasticmqsystemd service under OpenJDK 17 - A drop-in Amazon SQS-compatible endpoint on
:9324for aws-cli, boto3 and the AWS SDKs, with ElasticMQ bound to loopback only behind nginx - The same SQS API on
:80behind per-VM HTTP Basic Auth (useradmin), with a unique password generated on first boot - An unauthenticated
/healthzendpoint on:80for Azure Load Balancer health probes elasticmq.service+nginx.serviceas systemd units, enabled and active- In-memory storage (ElasticMQ's default) - clearly documented below, including how to enable durable queues
- 24/7 cloudimg support
Important - storage is in-memory by default
This appliance ships ElasticMQ's default in-memory storage. Queues and messages live in the JVM's memory only and are lost whenever the elasticmq service restarts or the VM reboots. This keeps the appliance simple and dependency-free out of the box, and is exactly what you want for development, CI, integration testing and demos - the queue starts empty and deterministic every time. If you need queues that survive restarts, ElasticMQ 1.7.1 supports optional persistence: enable queues-storage (queue definitions) and messages-storage (messages, backed by an embedded H2 database) in /etc/elasticmq/custom.conf per the upstream ElasticMQ documentation, then sudo systemctl restart elasticmq.
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 comfortable starting point for development and test queueing; size up the JVM heap (JAVA_OPTS in the systemd unit) and the VM for higher throughput. NSG inbound: allow 22/tcp from your management network, 9324/tcp from the application subnets whose code sends and receives messages, 80/tcp if you want browser/curl access to the authenticated management endpoint, and 443/tcp if you add TLS. ElasticMQ serves plain HTTP; the :9324 drop-in endpoint is unauthenticated by design (SQS clients cannot supply Basic Auth), so restrict it to trusted application subnets in your Network Security Group, and front it with TLS for production.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for ElasticMQ 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) and Custom (9324) for SQS clients, plus HTTP (80) if you want the authenticated management endpoint. Then Review + create -> Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name elasticmq \
--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
az vm open-port --resource-group <your-rg> --name elasticmq --port 9324 --priority 1010
az vm open-port --resource-group <your-rg> --name elasticmq --port 80 --priority 1020
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm the services are running
systemctl is-active elasticmq.service nginx.service
Both report active. ElasticMQ serves its SQS API on the loopback address 127.0.0.1:19324 only (an internal port); nginx fronts port 9324 with an unauthenticated drop-in SQS proxy for your AWS SDK clients, and port 80 with the per-VM HTTP Basic Auth gate for authenticated management access:
ss -tlnH | grep -E ':80|:9324|:19324'
You see nginx listening on 0.0.0.0:80 and 0.0.0.0:9324, and ElasticMQ listening on 127.0.0.1:19324 only - never on a public interface.

Step 5 - Retrieve your endpoint and management password
The SQS endpoint and the per-VM management password are written to a root-only file on first boot:
sudo cat /root/elasticmq-credentials.txt
This file contains ELASTICMQ_SQS_ENDPOINT (the drop-in :9324 URL you point AWS SDK clients at), ELASTICMQ_ADMIN_URL (the authenticated :80 endpoint), ELASTICMQ_USERNAME (admin) and ELASTICMQ_PASSWORD (unique to this VM). The password is stored on disk only as a bcrypt hash in /etc/nginx/.elasticmq.htpasswd, so no plaintext password ships in the image. Store it somewhere safe.

Step 6 - Confirm the health endpoint
nginx serves an unauthenticated health endpoint for load balancers and probes:
curl -s http://localhost/healthz
It returns ok. This endpoint never requires authentication, so it is safe for an Azure Load Balancer health probe.
Step 7 - Confirm authentication on the management endpoint
Because a password is set on first boot, an unauthenticated request to the :80 SQS management endpoint returns HTTP 401. The following reads the per-VM password from the credentials file and proves the round-trip - unauthenticated is rejected, a wrong password is rejected, and the correct password authenticates:
PW=$(sudo grep '^ELASTICMQ_PASSWORD=' /root/elasticmq-credentials.txt | cut -d= -f2-)
echo "unauth : $(curl -s -o /dev/null -w '%{http_code}' 'http://127.0.0.1/?Action=ListQueues&Version=2012-11-05')"
echo "wrongpw : $(curl -s -o /dev/null -w '%{http_code}' -u admin:wrong-pw 'http://127.0.0.1/?Action=ListQueues&Version=2012-11-05')"
echo "authed : $(curl -s -o /dev/null -w '%{http_code}' -u admin:$PW 'http://127.0.0.1/?Action=ListQueues&Version=2012-11-05')"
It prints unauth : 401, wrongpw : 401, authed : 200. Only the per-VM password reaches the SQS API on port 80, because ElasticMQ itself is bound to loopback and nginx port 80 is the only authenticated way in.

Step 8 - Create a queue and round-trip a message
The :9324 drop-in endpoint is exactly the Amazon SQS wire protocol, so any SQS client works against it. The commands below use plain curl against the SQS Query API to confirm the pipeline end to end on the VM itself - create a queue, send a message, and receive it back:
curl -s 'http://127.0.0.1:9324/?Action=CreateQueue&QueueName=orders&Version=2012-11-05' >/dev/null
curl -s 'http://127.0.0.1:9324/?Action=SendMessage&QueueUrl=http://127.0.0.1:9324/000000000000/orders&MessageBody=order%2342%20created&Version=2012-11-05' >/dev/null
curl -s 'http://127.0.0.1:9324/?Action=ReceiveMessage&QueueUrl=http://127.0.0.1:9324/000000000000/orders&Version=2012-11-05'
The final command returns an XML ReceiveMessageResponse containing <Body>order#42 created</Body> - the message you just sent, read back off the queue.
In real use you point the AWS CLI (or any SDK) at the endpoint from ELASTICMQ_SQS_ENDPOINT and any dummy AWS credentials:
export AWS_ACCESS_KEY_ID=x AWS_SECRET_ACCESS_KEY=x AWS_DEFAULT_REGION=elasticmq
EP=http://<vm-ip>:9324
aws --endpoint-url $EP sqs create-queue --queue-name orders
aws --endpoint-url $EP sqs send-message \
--queue-url $EP/000000000000/orders --message-body 'order#42 created'
aws --endpoint-url $EP sqs receive-message --queue-url $EP/000000000000/orders

Step 9 - Use ElasticMQ from your application
Any Amazon SQS client works - just override the endpoint. With boto3:
import boto3
sqs = boto3.client(
"sqs",
endpoint_url="http://<vm-ip>:9324",
region_name="elasticmq",
aws_access_key_id="x",
aws_secret_access_key="x",
)
q = sqs.create_queue(QueueName="orders")["QueueUrl"]
sqs.send_message(QueueUrl=q, MessageBody="order#42 created")
print(sqs.receive_message(QueueUrl=q)["Messages"][0]["Body"])
The same override works for the AWS SDKs for Java, JavaScript, Go and .NET, and for frameworks such as Spring Cloud AWS - set the SQS endpoint to http://<vm-ip>:9324 and supply any dummy credentials. Your application code is otherwise identical to talking to real Amazon SQS, which is what makes ElasticMQ ideal for local development and CI.
Maintenance
- Password: the management password is set on first boot and stored as a bcrypt entry in
/etc/nginx/.elasticmq.htpasswd. To change it, runsudo htpasswd -B /etc/nginx/.elasticmq.htpasswd adminand thensudo systemctl reload nginx. - Storage is in-memory by default: queues and messages are lost on
elasticmqservice restart or VM reboot. For durable queues, enablequeues-storageandmessages-storagein/etc/elasticmq/custom.confper the upstream persistence docs, thensudo systemctl restart elasticmq. - Drop-in SQS port:
:9324is intentionally unauthenticated (SQS SDK clients sign with SigV4 and cannot supply HTTP Basic Auth). Restrict it in your NSG to your application subnets in production. - Endpoint address: the queue URLs ElasticMQ returns are built from
node-addressin/etc/elasticmq/custom.conf, which first boot sets to this VM's address. If you place the VM behind a load balancer or a different DNS name, updatenode-address.hostthere andsudo systemctl restart elasticmq. - Loopback binding: ElasticMQ is bound to
127.0.0.1:19324in/etc/elasticmq/custom.conf, so nginx is the only path in for both the drop-in and the authenticated endpoint. Keep it that way - do not change the bind address to a public interface. - JVM sizing: the default heap is
-Xms256m -Xmx512m(JAVA_OPTSin/etc/systemd/system/elasticmq.service). Increase it and the VM size for higher message volume, thensudo systemctl daemon-reload && sudo systemctl restart elasticmq. - Restrict access and add TLS: ElasticMQ serves plain HTTP. For production, restrict access to trusted IP ranges in your Network Security Group, and front it with TLS (for example certbot with your own domain) terminating on
:443. - Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.
Support
cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.