# ccm: a cluster manager that builds throwaway Cassandra rings on your own machine

> Cassandra Cluster Manager is a Python script and library that creates, launches, inspects and removes small Apache Cassandra clusters on localhost, so developers and CI can exercise real ring behaviour without provisioning servers. It works from a Cassandra source tree rather than from jars, which is both its strength and its constraint.

**apache/cassandra-ccm** — Apache Cassandra® Cluster Manager (CCM) – easily create and destroy clusters on localhost

- Repository: https://github.com/apache/cassandra-ccm
- Website: https://cassandra.apache.org/
- Stars: 1,235 · Forks: 308
- Language: Python
- License: Apache-2.0
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/apache-cassandra-ccm

## ccm is scoped to small clusters on one host

Cassandra Cluster Manager is described as a script and library to create, launch and remove an Apache Cassandra cluster on localhost, and the project states the goal plainly: make it easy to create, manage and destroy a small cluster on a local box, meant for testing a Cassandra cluster.

That scope statement matters more than it looks. A local cluster is a way to test behaviours that a single node cannot show, such as replication across failure, token assignment, node bootstrapping and gossip convergence. What it cannot show is anything about how a real deployment behaves across separate machines, real network partitions or real disk. Every node shares one host's CPU, memory and disk.

The interface is a command line tool called `ccm`, backed by a Python library named ccmlib, and the repository also holds an `ssl/` directory, a `misc/` directory, tests and the `ccm` script itself. Licensing is Apache 2.0, so the code carries the usual Apache Software Foundation headers, and the installation notes live in a separate INSTALL.md file rather than in the main documentation.

The command surface is large, with sub-commands for clusters and for individual nodes, and every command documents itself through the `-h` or `--help` flag.

## ccm works from a source tree, not from jars

The one design decision that shapes everything else is stated early: ccm works from a Cassandra source tree, not the jars. That single constraint determines how you point the tool at a Cassandra version.

There are two routes. If you have already downloaded and compiled Cassandra sources, you can name the directory directly when creating a cluster:

```bash
ccm create test --install-dir=<path/to/cassandra-sources>
```

Alternatively, run `ccm create` from inside the source tree, and ccm will use that tree.

The other route asks ccm to use a released version, for example Cassandra 2.0.5:

```bash
ccm create test -v 2.0.5
```

In that case ccm downloads the binary from the Apache archive and sets the new cluster to use it. The documentation warns that this command can take a few minutes the first time you create a cluster for a given version, because the compiled source is saved in `~/.ccm/repository/`, which makes the second run for the same version much faster. The flip side is stated too: creating a lot of clusters with different versions takes up disk space.

Version strings are also a way to change the mode of operation. Prefixing with `source:` selects a source distribution instead of the default binary, which the docs suggest for continuous integration. Prefixing with `git:` pulls a named branch from the canonical Apache Git repository, and `github:` points at a branch in a GitHub fork:

```bash
ccm create trunk -v git:trunk -n 5
ccm create patched -v github:jbellis/trunk -n 1
```

There is also a fallback rule. If neither `binary:` nor `source:` appears in the version string, ccm falls back to building the requested version from git if it cannot reach the Apache mirrors.

## A three node cluster from create to ring

The short version of a whole session is three commands. Create the cluster with a version and a node count, populate it with the nodes, and start it:

```bash
ccm create test -v 2.0.5 -n 3 -s
ccm populate -n 3
ccm start
```

The `-s` flag appears in the create example, and the documentation notes that `-i` is used to add an IP prefix during the create process so that nodes communicate using the proper IP address for their node.

Once the cluster is up, you check it with a node scoped command:

```bash
ccm node1 ring
```

The node naming convention is worth internalising early, because most commands take a node argument. `ccm node2 flush` flushes a single node, `ccm node1 showlog` prints that node's log file, and `ccm flush` flushes every node in the cluster. The docs describe populate as a shortcut for adding multiple nodes initially, and bootstrapping a further node into a running cluster is done with:

```bash
ccm add node4 -i 127.0.0.4 -j 7400 -b
```

Tearing everything down is one command, `ccm remove`, which stops the nodes and removes all the data. Nothing in the documentation suggests a confirmation prompt, so treat it as destructive and keep the cluster name straight.

The port map is fixed and worth recording. Nodes listen on port 9160 for thrift, port 7000 for internal cluster communication, and ports 7100, 7200 and 7300 for JMX.

## macOS needs loopback aliases for every node past the first

On Mac OSX, each node beyond the first needs its own loopback interface, and the documentation gives the exact commands. For a three node cluster you add 127.0.0.2 and 127.0.0.3:

```bash
sudo ifconfig lo0 alias 127.0.0.2
sudo ifconfig lo0 alias 127.0.0.3
```

This is the step where a first run on a Mac most often stalls, because the cluster starts but the extra nodes cannot bind or advertise addresses that do not exist on the machine. The aliases are temporary and the docs say so, pointing to NETWORK_ALIASES.md for permanent network aliases on Mac OSX. That is a real operational wrinkle: a reboot on a CI runner or a laptop drops the aliases, and a cluster that started cleanly yesterday may fail to come back today for a reason that has nothing to do with Cassandra.

On Linux the equivalent problem does not arise in the same form, since the loopback range is routable without explicit aliases.

## Remote execution over SSH with selective file transfer

ccm can drive a cluster on another machine, and the documentation says all the local examples work the same way once the remote options are supplied. The connection is established with a set of arguments before the CCM command runs. `--ssh-host` takes a hostname or IP address, `--ssh-port` is an integer that defaults to 22, `--ssh-username` is the user for password or public key authentication, `--ssh-password` covers a password or a private key passphrase, and `--ssh-private-key` takes a filename.

The mechanism behind this is Paramiko, and the section heading in the documentation names it, SSH/Paramiko. A complete remote create looks like this:

```bash
ccm --ssh-host=192.168.33.11 --ssh-username=vagrant --ssh-password=vagrant create test -v 2.0.5 -n 3 -i 192.168.33.1 -s
```

Some commands need files that exist only on the local machine, and the documentation describes a pre-processing step where those commands have their files transferred and their argument values rewritten for the remote run. Three parameters are listed: `--dse-credentials` copies a local DSE credentials file to the remote server, `--node-ssl` recursively copies a node SSL directory, and `--ssl` recursively copies the SSL directory.

One caution on the remote path. Passing a password on the command line is what the example shows, and that value will end up in shell history and in process listings on the machine where you type it. The private key option is the better default for anything beyond a throwaway virtual machine.

## Shell completion and the limits of a localhost cluster

A small quality of life feature is bash completion, delivered as a file you copy and source yourself. The instructions are to copy `misc/ccm-completion.bash` into your home directory, or into `/etc` to make it available to all users, and source it in your `.bash_profile`:

```bash
. ~/scripts/ccm-completion.bash
```

After that, `ccm sw<tab>` expands to `ccm switch `. The `switch` sub-command has extra completion logic for the cluster name, so `ccm switch cl<tab>` can complete to a specific cluster, and a second tab press lists the candidates when the prefix is ambiguous.

The limits are worth stating without hedging. A localhost cluster cannot reproduce a multi rack or multi datacenter topology, because there is one host. It cannot reproduce a real network partition without tooling the documentation does not describe. Disk and memory contention between nodes is shared, so a slow disk makes every node slow at once, which is the opposite of how a real cluster degrades. And a cluster that behaves correctly on a laptop says nothing about behaviour under production data volumes.

On maintenance, the repository is not archived, but it has no GitHub releases, and the last push was on 2026-04-21. The dependency list in requirements.txt is short: pyYaml, six, psutil, packaging, and importlib_metadata for Python versions below 3.8. setup.py uses pbr, and on Windows the `ccm` script is copied to `ccm.py` at install time, which is a small sign that the Windows path is supported but not the primary one.

## Conclusion

ccm belongs in the toolkit of anyone who needs a real Cassandra ring on a laptop or a CI runner, because it hands back a cluster you can start, break and delete with a handful of commands instead of provisioning machines. It is the wrong choice for production shaped questions, since every node is on one host and the documentation scopes it to testing. Before you build a pipeline on it, confirm the Cassandra version you want to test can be fetched or built in your environment, decide whether the `binary:` or `source:` prefix fits your CI speed needs, and remember that the repository's last push was on 2026-04-21, so pin the Cassandra versions your suite exercises rather than tracking trunk.

## FAQ

### How do I create a local Cassandra cluster with ccm?

Run `ccm create test -v 2.0.5 -n 3 -s` to create a cluster named test with three nodes from a released version, then `ccm populate -n 3` and `ccm start`. ccm downloads the binary from the Apache archive and caches it in `~/.ccm/repository/`.

### What do I need to install ccm?

The installation instructions are in INSTALL.md. The Python dependencies in requirements.txt are pyYaml, six, psutil and packaging, and setup.py uses pbr.

### Why does ccm need network aliases on macOS?

Every node past the first needs its own loopback address, so on Mac OSX you add aliases such as `sudo ifconfig lo0 alias 127.0.0.2`. The documentation notes these aliases disappear on reboot.

### Can ccm manage a cluster on a remote host?

Yes. Remote options such as `--ssh-host`, `--ssh-port`, `--ssh-username`, `--ssh-password` and `--ssh-private-key` establish the connection before the CCM command runs, and some parameters such as `--ssl` and `--node-ssl` have their files copied to the remote server first.

### How do I test a Cassandra branch rather than a release?

Prefix the version with `git:` for a branch in the canonical Apache Git repository, for example `ccm create trunk -v git:trunk -n 5`, or with `github:` for a branch in a GitHub fork, for example `ccm create patched -v github:jbellis/trunk -n 1`.

## Sources

- [apache/cassandra-ccm on GitHub](https://github.com/apache/cassandra-ccm)
- [Issues](https://github.com/apache/cassandra-ccm/issues)
- [License: Apache-2.0](https://github.com/apache/cassandra-ccm/blob/trunk/LICENSE)
- [Project website](https://cassandra.apache.org/)
- [README](https://github.com/apache/cassandra-ccm/blob/trunk/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/apache-cassandra-ccm
