Apache Cassandra 5.0 on Rocky Linux 9 on Azure User Guide
Overview
This guide covers the deployment and configuration of Apache Cassandra 5.0 on Rocky Linux 9 on Azure using cloudimg Azure Marketplace images. Cassandra 5.0 is the latest major release of the wide-column NoSQL database originally developed at Facebook and now an Apache top-level project — the engine behind Netflix, Apple, Instagram, and Discord.
The image installs Cassandra 5.0 from the official redhat.cassandra.apache.org/50x DNF repo. The Cassandra daemon runs under OpenJDK 17 with PasswordAuthenticator enabled by default — no anonymous access. At first boot, cassandra-firstboot.service rotates the default cassandra/cassandra superuser password to a per-VM 32-char random password, creates a cloudimg superuser, creates the cloudimg keyspace (SimpleStrategy, RF=1), sets broadcast_rpc_address to the VM's primary private IPv4, and writes both passwords to /stage/scripts/cassandra-credentials.log (mode 0600 root only).
What is included:
-
Apache Cassandra 5.0 (latest 5.0.x at build time, 5.0.8) from the official Apache DNF repo
-
OpenJDK 17 JRE headless (Cassandra 5.0 requires Java 11+; 17 recommended)
-
cassandra.servicesystemd unit auto-starting on boot -
cassandra-firstboot.servicesystemd oneshot that rotates thecassandrasuperuser password, creates thecloudimgsuperuser, creates thecloudimgkeyspace, sets the VM's primary IP asbroadcast_rpc_address, and writes credentials to/stage/scripts/cassandra-credentials.log(mode 0600 root only) -
Python 3.9 + cassandra-driver venv at
/opt/cqlsh-venvfor cqlsh-equivalent CLI work -
PasswordAuthenticatorandCassandraAuthorizerenabled;CassandraRoleManagerfor password-backed access control -
Default cluster name
cloudimg-cassandra(single-node by default; customers reconfigure for multi-node) -
CQL native protocol listener on TCP 9042 (firewalld opens 9042 externally; 7000/7001/7199 stay private)
-
JMX bound to localhost:7199 only
-
Data directory
/var/lib/cassandra(NOT/mnt— Azure mounts the ephemeral resource disk at/mntand that location does not survive SIG capture) -
Rocky Linux 9 base with latest security patches applied at build time
-
Azure Linux Agent for seamless cloud integration and SSH key injection
-
24/7 cloudimg support with guaranteed 24 hour response SLA
Prerequisites
-
An active Azure subscription
-
A subscription to the Apache Cassandra 5.0 on Rocky Linux 9 listing on Azure Marketplace
-
An SSH public key for VM authentication
-
A virtual network and subnet in the target region
Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) for development and small single-node deployments. Production multi-node clusters should use Standard_D4s_v5 or larger with attached Premium SSD data disks for the /var/lib/cassandra/data directory. Cassandra is heap-sensitive — give the JVM at least 4 GB and leave a similar amount for the page cache.
Step 1: Deploy from the Azure Portal
Navigate to Marketplace in the Azure Portal, search for Cassandra 5.0, select the cloudimg publisher entry, and click Create.
On the Networking tab attach a network security group that allows inbound TCP 22 from your management IP range and TCP 9042 from your application VMs. Do not expose port 9042 to the public internet — even with PasswordAuthenticator enabled, brute-force attacks against weak passwords are a real risk. For multi-node clusters, also open TCP 7000 (intra-node, unencrypted) or 7001 (TLS) and 7199 (JMX) between cluster member private IPs.
Click Review + create, wait for validation, then Create. Deployment takes around three minutes (Cassandra's first-boot service generates the keypair and waits for native transport on 9042).
Step 2: Deploy from the Azure CLI
RG="cassandra-prod"
LOCATION="eastus"
VM_NAME="cassandra-01"
ADMIN_USER="azureuser"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/cassandra-5-0-rocky-9/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az network vnet create --resource-group "$RG" --name cass-vnet \
--address-prefix 10.97.0.0/16 --subnet-name cass-subnet --subnet-prefix 10.97.1.0/24
az network nsg create --resource-group "$RG" --name cass-nsg
az network nsg rule create --resource-group "$RG" --nsg-name cass-nsg \
--name allow-ssh --priority 100 \
--source-address-prefixes "<your-mgmt-cidr>" \
--destination-port-ranges 22 --access Allow --protocol Tcp
az network nsg rule create --resource-group "$RG" --nsg-name cass-nsg \
--name allow-cql --priority 110 \
--source-address-prefixes 10.97.0.0/16 \
--destination-port-ranges 9042 --access Allow --protocol Tcp
az vm create \
--resource-group "$RG" --name "$VM_NAME" \
--image "$GALLERY_IMAGE_ID" \
--size Standard_B2s --storage-sku StandardSSD_LRS \
--admin-username "$ADMIN_USER" --ssh-key-values "$SSH_KEY" \
--vnet-name cass-vnet --subnet cass-subnet --nsg cass-nsg \
--public-ip-sku Standard
Step 3: Connect via SSH
ssh azureuser@<vm-ip>
cassandra.service will already be running and cassandra-firstboot.service will already have rotated the superuser password and created the cloudimg keyspace.
Step 4: Verify the Cassandra Service
sudo systemctl status cassandra.service --no-pager
Expected: active (running). Confirm the firstboot sentinel:
sudo test -f /var/lib/cloudimg/cassandra-firstboot.done && echo FIRSTBOOT_DONE
Confirm the CQL listener is bound on port 9042:
sudo ss -tln | grep 9042

Step 5: Retrieve the cassandra and cloudimg Passwords
The cassandra superuser password and the cloudimg user password have been rotated on this specific virtual machine and written to a root-only file. Read them with:
sudo cat /stage/scripts/cassandra-credentials.log
You will see lines similar to:
CASSANDRA_USER=cassandra
CASSANDRA_PASSWORD=<CASSANDRA_PASSWORD>
CLOUDIMG_USER=cloudimg
CLOUDIMG_PASSWORD=<CLOUDIMG_PASSWORD>
CLOUDIMG_KEYSPACE=cloudimg
LISTEN_PORT=9042
CLUSTER_NAME=cloudimg-cassandra
BROADCAST_RPC_ADDRESS=<primary-ipv4>
Store these in your secret store and rotate as needed via ALTER USER cassandra WITH PASSWORD '<new-password>';.

Step 6: Connect from the VM with cassandra-driver
cqlsh ships with Cassandra and works with Rocky 9's stock Python 3.9. The image also provides an isolated cassandra-driver venv at /opt/cqlsh-venv for Python work — use that for any production cassandra-driver scripts to avoid system-package conflicts.
CASS_PASS=$(sudo grep '^CASSANDRA_PASSWORD=' /stage/scripts/cassandra-credentials.log | cut -d= -f2-)
/opt/cqlsh-venv/bin/python << PY
from cassandra.cluster import Cluster
from cassandra.auth import PlainTextAuthProvider
ap = PlainTextAuthProvider(username='cassandra', password='${CASS_PASS}')
session = Cluster(['127.0.0.1'], port=9042, auth_provider=ap).connect()
for row in session.execute('SELECT release_version, cluster_name FROM system.local'):
print(row.release_version, row.cluster_name)
PY
Expected output: 5.0.8 cloudimg-cassandra.
Step 7: Round-trip Test as cloudimg
The cloudimg superuser and the cloudimg keyspace are ready to use from first boot. Run a CREATE / INSERT / SELECT round-trip as the cloudimg user:
CLOUD_PASS=$(sudo grep '^CLOUDIMG_PASSWORD=' /stage/scripts/cassandra-credentials.log | cut -d= -f2-)
/opt/cqlsh-venv/bin/python << PY
from cassandra.cluster import Cluster
from cassandra.auth import PlainTextAuthProvider
ap = PlainTextAuthProvider(username='cloudimg', password='${CLOUD_PASS}')
session = Cluster(['127.0.0.1'], port=9042, auth_provider=ap).connect()
session.execute("CREATE TABLE IF NOT EXISTS cloudimg.users (id int PRIMARY KEY, name text)")
session.execute("INSERT INTO cloudimg.users (id, name) VALUES (1, 'alice')")
row = session.execute("SELECT name FROM cloudimg.users WHERE id = 1").one()
print(row.name)
session.execute("DROP TABLE cloudimg.users")
PY
Expected output: alice. The cloudimg user has full access to the cloudimg keyspace and can create / drop tables, insert / select / update / delete rows.

Step 8: Connect from a Remote Application
From any host inside the same virtual network with cassandra-driver installed (Python, Java, Go, Node.js, .NET, Rust, etc.), connect on port 9042 with the cassandra or cloudimg credentials. Example Python:
pip install cassandra-driver
from cassandra.cluster import Cluster
from cassandra.auth import PlainTextAuthProvider
ap = PlainTextAuthProvider(username='cloudimg', password='<CLOUDIMG_PASSWORD>')
cluster = Cluster(['<vm-private-ip>'], port=9042, auth_provider=ap)
session = cluster.connect('cloudimg')
session.execute("INSERT INTO orders (id, sku, qty) VALUES (uuid(), 'WIDGET-1', 4)")
Do not open port 9042 to the public internet without TLS termination at a reverse proxy or VPN. Cassandra's native protocol does not encrypt by default; for production, configure client-to-node SSL via client_encryption_options in cassandra.yaml.
Step 9: Server Components
| Component | Path |
|---|---|
| Cassandra binary | /usr/sbin/cassandra |
cqlsh |
/usr/bin/cqlsh |
| cassandra-driver Python venv | /opt/cqlsh-venv/bin/python |
| Configuration | /etc/cassandra/conf/cassandra.yaml |
| JVM options | /etc/cassandra/conf/jvm-server.options, /etc/cassandra/conf/jvm17-server.options |
| Logback config | /etc/cassandra/conf/logback.xml |
| Data directory | /var/lib/cassandra/data |
| Commit log | /var/lib/cassandra/commitlog |
| Saved caches | /var/lib/cassandra/saved_caches |
| Hints | /var/lib/cassandra/hints |
| Logs | /var/log/cassandra/system.log |
| Systemd unit | /etc/systemd/system/cassandra.service |
| Firstboot script | /usr/local/sbin/cassandra-firstboot.sh |
| Firstboot service | /etc/systemd/system/cassandra-firstboot.service |
| Credentials file | /stage/scripts/cassandra-credentials.log (mode 0600) |
| Firstboot sentinel | /var/lib/cloudimg/cassandra-firstboot.done |
| Firewall | firewalld (9042/tcp open, others private) |
Inspect the running version:
/usr/sbin/cassandra -v
Step 10: Managing the Cassandra Service
Status:
sudo systemctl status cassandra.service --no-pager
Stop / Start / Restart:
sudo systemctl stop cassandra.service
sudo systemctl start cassandra.service
sudo systemctl restart cassandra.service
View the system log:
sudo tail -f /var/log/cassandra/system.log
Inspect the node status (single-node by default, multi-node if you've added peers):
sudo nodetool status
Run a flush + compaction:
sudo nodetool flush
sudo nodetool compact
Repair (essential for multi-node clusters; safe single-node no-op):
sudo nodetool repair

Step 11: Scale to a Multi-Node Cluster
The image ships configured as a single-node cluster (cluster_name: 'cloudimg-cassandra', listening on 0.0.0.0:9042 with broadcast_rpc_address set to the VM's primary private IP). To convert into a real cluster:
- Stop Cassandra on every prospective node:
sudo systemctl stop cassandra - On each node, edit
/etc/cassandra/conf/cassandra.yaml: listen_address: <node-private-ip>(or comment out and uselisten_interface)seed_provider.parameters.seeds: "<node1-private-ip>,<node2-private-ip>"endpoint_snitch: GossipingPropertyFileSnitch(default isSimpleSnitch— fine for single AZ)- Open firewalld for gossip + JMX between cluster members on private IPs:
sudo firewall-cmd --permanent --add-rich-rule="rule family=ipv4 source address=<peer-private-ip>/32 port port=7000 protocol=tcp accept" sudo firewall-cmd --reload - Wipe
/var/lib/cassandra/data/system/peers*so nodes don't carry stale peer info:sudo rm -rf /var/lib/cassandra/data/system/peers* - Start the seed node first, then the rest one-by-one with 30s gap between starts
- Verify cluster:
sudo nodetool status— all nodes should appearUN(Up Normal) - Update the
cloudimgkeyspace replication:ALTER KEYSPACE cloudimg WITH replication = {'class': 'NetworkTopologyStrategy', 'datacenter1': 3}; - Run a full repair:
sudo nodetool repair --full
For multi-DC or large-scale clusters, refer to the official Cassandra operations guide at cassandra.apache.org/doc/.
Step 12: Troubleshooting
cassandra.service won't start
- Check the system log:
sudo tail -100 /var/log/cassandra/system.log - Confirm the JVM has enough memory:
free -hshould show >= 2 GB free - Check for port conflicts:
sudo ss -tln | grep -E ':(9042|7000|7199)'
SELinux denials
- Rocky 9 runs SELinux Enforcing by default. The Cassandra RPM ships with port labels for 9042, but custom data-dir paths may need labelling:
sudo semanage fcontext -a -t cassandra_var_lib_t '/data/cassandra(/.*)?' sudo restorecon -Rv /data/cassandra
Authentication fails with rotated password
- Confirm the credentials file is current:
sudo cat /stage/scripts/cassandra-credentials.log - Check the auth keyspace exists:
sudo nodetool describering system_auth - If you've forgotten the password, you can disable auth temporarily in
cassandra.yaml(authenticator: AllowAllAuthenticator), restart, set a new password, and re-enable
Cassandra never bound port 9042 during firstboot
- The default Cassandra startup takes 30–90s on a Standard_B2s. If firstboot times out, check
sudo journalctl -u cassandra-firstboot.serviceandsudo tail -100 /var/log/cassandra/system.log - Larger heap (
MAX_HEAP_SIZEin/etc/cassandra/conf/jvm-server.options) requires more startup time
Data dir filling up /var/lib/cassandra
- Cassandra's commit log + SSTables grow without bound on heavy write workloads
- Attach a Premium SSD data disk and bind-mount it to
/var/lib/cassandra/data(or changedata_file_directoriesincassandra.yaml) - Run
sudo nodetool compactto merge SSTables, but be aware compactions are I/O-heavy
Step 13: Security Recommendations
-
Rotate the cassandra superuser password immediately after deployment — see Step 5 for the auto-rotated one and use
ALTER USERto change -
Restrict NSG so port 9042 is only reachable from your application VMs and admin networks; never expose to the public internet
-
Enable client-to-node SSL in
cassandra.yaml(client_encryption_options.enabled: true) for any cross-VM connection -
Enable internode encryption (
server_encryption_options.internode_encryption: all) for multi-node clusters -
Restrict the cassandra superuser to admin operations only — application code should connect as
cloudimg(a regular superuser scoped to the cloudimg keyspace) or a least-privilege role you create -
Enable audit logging (
audit_logging_options.enabled: true) for compliance-sensitive workloads -
Back up
/var/lib/cassandra/dataregularly vianodetool snapshot+ Azure Files or Blob storage upload -
Patch the OS monthly with
sudo dnf upgrade -y && sudo rebooton each cluster node (rolling, one node at a time)
Step 14: Support and Licensing
Apache Cassandra is licensed under the Apache License 2.0. There is no per-node, per-CPU, or per-GB fee for any Cassandra component or for the upstream protocol.
cloudimg provides commercial support for this image separately from the upstream project.
-
Email: support@cloudimg.co.uk
-
Website: www.cloudimg.co.uk
-
Support hours: 24/7 with guaranteed 24 hour response SLA
Deploy on Azure
Launch Apache Cassandra 5.0 on Rocky Linux 9 with 24/7 support from cloudimg.
View on Marketplace
Need Help?
Our support team is available 24/7.
support@cloudimg.co.uk