Deployment guide

Cassandra 5.0 on Rocky 9

Overview

Apache Cassandra is a wide-column NoSQL database, originally developed at Facebook and now an Apache top-level project. Cassandra 5.0 is the latest major release. Netflix, Apple, Instagram and Discord all run it.

This SecureAnyCloud image installs Cassandra 5.0 on Rocky Linux 9 on Azure, from the official redhat.cassandra.apache.org/50x DNF repo. The daemon runs under OpenJDK 17 with PasswordAuthenticator enabled by default, so there is no anonymous access.

At first boot, cassandra-firstboot.service rotates the default cassandra/cassandra superuser password to a per-VM 32-character random password. It creates a SecureAnyCloud superuser and the SecureAnyCloud keyspace (SimpleStrategy, RF=1), and sets broadcast_rpc_address to the VM's primary private IPv4. It writes both passwords to /stage/scripts/cassandra-credentials.log (mode 0600, root only).

What you get:

  • 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 or later, and 17 is recommended)
  • cassandra.service systemd unit that starts on boot
  • cassandra-firstboot.service systemd oneshot that rotates the cassandra superuser password, creates the SecureAnyCloud superuser and 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 and a cassandra-driver venv at /opt/cqlsh-venv for cqlsh-equivalent command-line work
  • PasswordAuthenticator and CassandraAuthorizer enabled, with CassandraRoleManager for password-backed access control
  • Default cluster name sac-cassandra, single-node by default (reconfigure for multi-node)
  • CQL native protocol listener on TCP 9042 (firewalld opens 9042 externally, and 7000/7001/7199 stay private)
  • JMX bound to localhost:7199 only
  • Data directory /var/lib/cassandra, not /mnt, because Azure mounts the ephemeral resource disk at /mnt and that location does not survive SIG capture
  • Rocky Linux 9 base with the latest security patches applied at build time
  • Azure Linux Agent for cloud integration and SSH key injection
  • 24/7 SecureAnyCloud support with a 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 sensitive to heap size. Give the JVM at least 4 GB and leave a similar amount for the page cache.

1

Deploy from the Azure Portal

In the Azure Portal, open Marketplace and search for Cassandra 5.0. Select the SecureAnyCloud publisher entry and choose 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 the private IPs of the cluster members.

Choose Review + create, wait for validation, then choose Create. Deployment takes around three minutes, because Cassandra's first-boot service generates the keypair and waits for native transport on 9042.

2

Deploy from the Azure CLI

RG="cassandra-prod" LOCATION="eastus" VM_NAME="cassandra-01" ADMIN_USER="azureuser" IMAGE_URN="sac1647283583153:cassandra-5-0-rocky-9:default:latest" 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
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:cassandra-5-0-rocky-9:default:latest
az vm create \
  --resource-group "$RG" --name "$VM_NAME" \
  --image "$IMAGE_URN" \
  --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
3

Connect via SSH

ssh azureuser@<vm-ip>

cassandra.service is already running, and cassandra-firstboot.service has already rotated the superuser password and created the SecureAnyCloud keyspace.

4

Verify the Cassandra Service

sudo systemctl status cassandra.service --no-pager

The service should show active (running). Confirm the firstboot sentinel:

sudo test -f /var/lib/sac/data/cassandra-firstboot.done && echo FIRSTBOOT_DONE

Confirm the CQL listener is bound on port 9042:

sudo ss -tln | grep 9042
5

Retrieve the cassandra and SecureAnyCloud Passwords

First boot rotated the cassandra superuser password and the SecureAnyCloud user password on this virtual machine and wrote them to a root-only file. Read them with:

sudo cat /stage/scripts/cassandra-credentials.log

The output contains lines similar to:

CASSANDRA_USER=cassandra CASSANDRA_PASSWORD=<CASSANDRA_PASSWORD> SAC_USER=sac SAC_PASSWORD=<SAC_PASSWORD> SAC_KEYSPACE=sac LISTEN_PORT=9042 CLUSTER_NAME=sac-cassandra BROADCAST_RPC_ADDRESS=<primary-ipv4>

Store these passwords in your secret store. Rotate them when needed with ALTER USER cassandra WITH PASSWORD '<new-password>';.

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 it 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

The expected output is 5.0.8 sac-cassandra.

7

Round-trip Test as SecureAnyCloud

The SecureAnyCloud superuser and the SecureAnyCloud keyspace are ready to use from first boot. Run a CREATE, INSERT and SELECT round trip as the SecureAnyCloud user:

CLOUD_PASS=$(sudo grep '^SAC_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='sac', password='${CLOUD_PASS}') session = Cluster(['127.0.0.1'], port=9042, auth_provider=ap).connect() session.execute("CREATE TABLE IF NOT EXISTS sac.users (id int PRIMARY KEY, name text)") session.execute("INSERT INTO sac.users (id, name) VALUES (1, 'alice')") row = session.execute("SELECT name FROM sac.users WHERE id = 1").one() print(row.name) session.execute("DROP TABLE sac.users") PY

The expected output is alice. The SecureAnyCloud user has full access to the SecureAnyCloud keyspace. It can create and drop tables, and insert, select, update and delete rows.

8

Connect from a Remote Application

From any host inside the same virtual network that has a cassandra-driver installed (Python, Java, Go, Node.js, .NET, Rust and others), connect on port 9042 with the cassandra or SecureAnyCloud credentials. The following example uses Python:

pip install cassandra-driver
from cassandra.cluster import Cluster from cassandra.auth import PlainTextAuthProvider ap = PlainTextAuthProvider(username='sac', password='<SAC_PASSWORD>') cluster = Cluster(['<vm-private-ip>'], port=9042, auth_provider=ap) session = cluster.connect('sac') 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 with client_encryption_options in cassandra.yaml.

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/sac/data/cassandra-firstboot.done

Firewall firewalld (9042/tcp open, others private)

Inspect the running version:

/usr/sbin/cassandra -v
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 have added peers):

sudo nodetool status

Run a flush and compaction:

sudo nodetool flush
sudo nodetool compact

Repair (essential for multi-node clusters, and a safe no-op on a single node):

sudo nodetool repair
11

Scale to a Multi-Node Cluster

The image ships configured as a single-node cluster (cluster_name: 'sac-cassandra', listening on 0.0.0.0:9042 with broadcast_rpc_address set to the VM's primary private IP). To convert it into a real cluster, follow these steps:

  • Stop Cassandra on every prospective node: sudo systemctl stop cassandra
  • On each node, edit /etc/cassandra/conf/cassandra.yaml and set the following values:
    • listen_address: <node-private-ip> (or comment it out and use listen_interface)
    • seed_provider.parameters.seeds: "<node1-private-ip>,<node2-private-ip>"
    • endpoint_snitch: GossipingPropertyFileSnitch (the default is SimpleSnitch, which is fine for a single availability zone)
  • Open firewalld for gossip and 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 that nodes do not carry stale peer information: sudo rm -rf /var/lib/cassandra/data/system/peers*
  • Start the seed node first, then start the rest one by one, with a 30-second gap between starts
  • Verify the cluster with sudo nodetool status. All nodes should appear as UN (Up Normal)
  • Update the SecureAnyCloud keyspace replication: ALTER KEYSPACE SecureAnyCloud WITH replication = {'class': 'NetworkTopologyStrategy', 'datacenter1': 3};
  • Run a full repair: sudo nodetool repair --full

For multi-DC or large-scale clusters, see the official Cassandra operations guide at cassandra.apache.org/doc/.

12

Troubleshooting

cassandra.service won't start

  • Check the system log: sudo tail -100 /var/log/cassandra/system.log
  • Confirm that 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 in Enforcing mode by default. The Cassandra RPM ships with port labels for 9042, but custom data directory paths may need labelling: sudo semanage fcontext -a -t cassandra_var_lib_t '/data/cassandra(/.*)?' sudo restorecon -Rv /data/cassandra

Authentication fails with the rotated password

  • Confirm that the credentials file is current: sudo cat /stage/scripts/cassandra-credentials.log
  • Check that the auth keyspace exists: sudo nodetool describering system_auth
  • If you have forgotten the password, you can temporarily disable authentication in cassandra.yaml (authenticator: AllowAllAuthenticator). Restart, set a new password, and re-enable authentication

Cassandra never bound port 9042 during first boot

  • Cassandra startup takes 30-90s on a Standard_B2s by default. If first boot times out, check sudo journalctl -u cassandra-firstboot.service and sudo tail -100 /var/log/cassandra/system.log
  • A larger heap (MAX_HEAP_SIZE in /etc/cassandra/conf/jvm-server.options) needs more startup time

Data dir filling up /var/lib/cassandra

  • On heavy write workloads, Cassandra's commit log and SSTables grow without bound
  • 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. Compactions are I/O-heavy
13

Security Recommendations

  • Rotate the cassandra superuser password after deployment. Step 5 shows the automatically rotated password, and you change it with ALTER USER.
  • Restrict the NSG so that port 9042 is reachable only from your application VMs and admin networks. Never expose it 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 SecureAnyCloud (a regular superuser scoped to the SecureAnyCloud keyspace) or as a least-privilege role that you create.
  • Enable audit logging (audit_logging_options.enabled: true) for compliance-sensitive workloads.
  • Back up /var/lib/cassandra/data regularly with nodetool snapshot and an upload to Azure Files or Blob storage.
  • Patch the OS monthly with sudo dnf upgrade -y && sudo reboot on each cluster node, one node at a time (a rolling update).
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.

SecureAnyCloud provides commercial support for this image, separately from the upstream project.

Email: sales@secureanycloud.com

Website: secureanycloud.com

Support hours: 24/7 with a guaranteed 24-hour response SLA

Deploy on Azure

Launch Apache Cassandra 5.0 on Rocky Linux 9 with 24/7 support from SecureAnyCloud.

View on Marketplace

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure

Launch Apache Cassandra 5.0 on Rocky Linux 9 on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure