Databases Azure

Apache Cassandra 5.0 on Rocky Linux 9 on Azure User Guide

| Product: Apache Cassandra 5.0 on Rocky Linux 9 on Azure

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.service systemd unit auto-starting on boot

  • cassandra-firstboot.service systemd oneshot that rotates the cassandra superuser password, creates the cloudimg superuser, creates the cloudimg keyspace, sets the VM's primary IP as broadcast_rpc_address, and writes credentials to /stage/scripts/cassandra-credentials.log (mode 0600 root only)

  • Python 3.9 + cassandra-driver venv at /opt/cqlsh-venv for cqlsh-equivalent CLI work

  • PasswordAuthenticator and CassandraAuthorizer enabled; CassandraRoleManager for 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 /mnt and 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

cassandra.service active (running) with TCP 9042 native CQL listener on Rocky 9

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>';.

/stage/scripts/cassandra-credentials.log (mode 0600 root) shows per-VM cassandra + cloudimg passwords + broadcast_rpc_address

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.

Cassandra release_version 5.0.8 + cloudimg round-trip via cassandra-driver venv (CREATE / INSERT / SELECT / DROP) on the cloudimg keyspace

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

Cassandra components inventory: cassandra binary, cassandra.yaml at /etc/cassandra/conf, /var/lib/cassandra/data, firewalld 9042/tcp open

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:

  1. Stop Cassandra on every prospective node: sudo systemctl stop cassandra
  2. On each node, edit /etc/cassandra/conf/cassandra.yaml:
  3. listen_address: <node-private-ip> (or comment out and use listen_interface)
  4. seed_provider.parameters.seeds: "<node1-private-ip>,<node2-private-ip>"
  5. endpoint_snitch: GossipingPropertyFileSnitch (default is SimpleSnitch — fine for single AZ)
  6. 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
  7. Wipe /var/lib/cassandra/data/system/peers* so nodes don't carry stale peer info: sudo rm -rf /var/lib/cassandra/data/system/peers*
  8. Start the seed node first, then the rest one-by-one with 30s gap between starts
  9. Verify cluster: sudo nodetool status — all nodes should appear UN (Up Normal)
  10. Update the cloudimg keyspace replication: ALTER KEYSPACE cloudimg WITH replication = {'class': 'NetworkTopologyStrategy', 'datacenter1': 3};
  11. 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 -h should 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.service and sudo tail -100 /var/log/cassandra/system.log
  • Larger heap (MAX_HEAP_SIZE in /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 change data_file_directories in cassandra.yaml)
  • Run sudo nodetool compact to 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 USER to 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/data regularly via nodetool snapshot + Azure Files or Blob storage upload

  • Patch the OS monthly with sudo dnf upgrade -y && sudo reboot on 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