DEV Community

Cover image for Deploying Apache Cassandra on Kubernetes
Sanskriti Harmukh for Vultr

Posted on with Aashish Chaurasiya Originally published at docs.vultr.com

Deploying Apache Cassandra on Kubernetes

Apache Cassandra is a highly scalable, distributed NoSQL database built for write-heavy workloads, horizontal scaling, and tunable consistency, with no single point of failure. K8ssandra is an open-source operator that automates Cassandra provisioning, scaling, backups, and repairs on Kubernetes. This guide deploys a multi-node Cassandra cluster via the K8ssandra Operator, with cert-manager, persistent block storage, and a LoadBalancer service for external access.

Prerequisites: a Kubernetes cluster with at least 4 nodes / 4GB RAM each, a workstation with kubectl and helm configured against the cluster.


Install cqlsh

cqlsh is the Python-based CLI for querying Cassandra.

$ sudo apt update
$ sudo apt install -y python3 python3-pip python3.12-venv
$ python3 -m venv cassandra
$ source cassandra/bin/activate
$ pip install -U cqlsh
$ cqlsh --version
Enter fullscreen mode Exit fullscreen mode

Install cert-manager

K8ssandra uses cert-manager to generate the Java keystores/truststores Cassandra needs for internal TLS.

$ helm repo add jetstack https://charts.jetstack.io
$ helm repo update
$ helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --set crds.enabled=true
$ kubectl get all -n cert-manager
Enter fullscreen mode Exit fullscreen mode

Install the K8ssandra Operator

$ helm repo add k8ssandra https://helm.k8ssandra.io/stable
$ helm install k8ssandra-operator k8ssandra/k8ssandra-operator \
  --namespace k8ssandra-operator \
  --create-namespace
$ kubectl -n k8ssandra-operator get deployment
$ kubectl get pods -n k8ssandra-operator
Enter fullscreen mode Exit fullscreen mode

You should see both k8ssandra-operator and k8ssandra-operator-cass-operator deployments ready.


Deploy a Multi-Node Cassandra Cluster

1. Check available StorageClasses in your cluster:

$ kubectl get storageclass
Enter fullscreen mode Exit fullscreen mode

Note the name of a CSI-backed block storage class — you'll reference it below.

2. Create the cluster manifest:

$ nano cluster.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: k8ssandra.io/v1alpha1
kind: K8ssandraCluster
metadata:
  name: demo
spec:
  cassandra:
    serverVersion: "4.0.1"
    datacenters:
      - metadata:
          name: dc1
        size: 3
        storageConfig:
          cassandraDataVolumeClaimSpec:
            storageClassName: standard
            accessModes:
              - ReadWriteOnce
            resources:
              requests:
                storage: 10Gi
        config:
          jvmOptions:
            heapSize: 512M
        stargate:
          size: 1
          heapSize: 256M
Enter fullscreen mode Exit fullscreen mode

Replace standard with your cluster's actual StorageClass name. This provisions 3 Cassandra nodes with a 10GB volume each, 512MB JVM heap per Cassandra node, and a single Stargate node (REST/GraphQL/Document API gateway in front of Cassandra) with a 256MB heap. Confirm your cloud provider's CSI driver is installed and configured before applying — it's required for dynamic volume provisioning.

3. Apply and wait (cluster bring-up takes ~15 minutes):

$ kubectl apply -n k8ssandra-operator -f cluster.yaml
$ kubectl get pods -n k8ssandra-operator --watch
Enter fullscreen mode Exit fullscreen mode

You're looking for 3 demo-dc1-default-sts-N pods and a demo-dc1-default-stargate-deployment-* pod, all Running.


Verify Persistent Storage

Cassandra nodes run as a StatefulSet, each backed by a PVC for durable storage.

$ kubectl get statefulset -n k8ssandra-operator
$ kubectl get sc standard
$ kubectl get pvc --all-namespaces
Enter fullscreen mode Exit fullscreen mode

All three PVCs should show Bound.


Expose the Cluster

Create a LoadBalancer service to reach Cassandra over the native CQL protocol (port 9042):

$ nano service.yaml
Enter fullscreen mode Exit fullscreen mode
apiVersion: v1
kind: Service
metadata:
  name: cassandra
  labels:
    app: cassandra
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  ports:
    - port: 9042
      targetPort: 9042
  selector:
    app.kubernetes.io/name: cassandra
Enter fullscreen mode Exit fullscreen mode
$ kubectl apply -n k8ssandra-operator -f service.yaml
$ kubectl get svc/cassandra -n k8ssandra-operator
Enter fullscreen mode Exit fullscreen mode

Wait for EXTERNAL-IP to populate (a few minutes) — that's your connection address.


Test the Cluster

$ CASS_IP=$(kubectl get svc cassandra -n k8ssandra-operator -o jsonpath="{.status.loadBalancer.ingress[*].ip}")
$ CASS_USERNAME=$(kubectl get secret demo-superuser -n k8ssandra-operator -o=jsonpath='{.data.username}' | base64 --decode)
$ CASS_PASSWORD=$(kubectl get secret demo-superuser -n k8ssandra-operator -o=jsonpath='{.data.password}' | base64 --decode)
$ cqlsh -u $CASS_USERNAME -p $CASS_PASSWORD $CASS_IP 9042
Enter fullscreen mode Exit fullscreen mode
demo-superuser@cqlsh> CREATE KEYSPACE demo WITH replication = {'class': 'SimpleStrategy', 'replication_factor': 3};
demo-superuser@cqlsh> CREATE TABLE demo.users (id text primary key, name text, country text);
demo-superuser@cqlsh> BEGIN BATCH
                         INSERT INTO demo.users (id, name, country) VALUES ('42', 'John Doe', 'UK');
                         INSERT INTO demo.users (id, name, country) VALUES ('43', 'Joe Smith', 'US');
                      APPLY BATCH;
demo-superuser@cqlsh> SELECT * FROM demo.users;
Enter fullscreen mode Exit fullscreen mode
 id | country | name
----+---------+-----------
 43 |      US | Joe Smith
 42 |      UK |  John Doe

(2 rows)
Enter fullscreen mode Exit fullscreen mode

Next Steps

Cassandra is running as a 3-node cluster with persistent storage and a Stargate data gateway. From here:

  • Scale size in the datacenter spec and reapply to add nodes
  • Enable Stargate's REST/GraphQL APIs for app access without a native CQL driver
  • Configure K8ssandra's Medusa integration for scheduled backups to object storage

For the full guide, visit the original article on Vultr Docs.

Top comments (0)