> ## Documentation Index
> Fetch the complete documentation index at: https://controlplanecorporation-majid-docs-content-expansion.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Redis Cluster

> Deploy Redis Cluster on Control Plane using the Template Catalog. Runs Redis or Valkey with native data sharding across multiple primary nodes, replication, persistent volumes, and scheduled backups.

## Overview

Redis Cluster is a distributed Redis deployment with automatic data sharding across multiple primary nodes and built-in replication. This template deploys a native Redis Cluster with 3 primary shards and 3 replicas, providing both horizontal scalability and high availability without an external Sentinel process. Nodes run Redis by default, and the `engine` knob added in version 1.6.0 runs the same cluster on Valkey instead — see [Engine Selection](#engine-selection).

<Note>
  This template does not create a GVC. You must deploy it into an existing GVC.
</Note>

### What Gets Created

* **Stateful Redis Cluster Workload** — (`RELEASE_NAME-redis-cluster`): all six nodes managed together, each running Redis or Valkey according to `engine`. Replica 0 initializes the cluster once all nodes are healthy.
* **Volume Set** — Persistent storage for each Redis node's data directory.
* **Secret** — An opaque secret containing the Redis cluster configuration (`redis.conf`), mounted into each container.
* **Secret** — An opaque secret containing the cluster initialization script, mounted and executed at startup.
* **Secret** *(optional)* — A dictionary secret holding the Redis password, created when `redis.password` is set.
* **Identity & Policy** — An identity bound to the workload with `reveal` access to the config, startup script, and auth secrets, and cloud storage access when backup is enabled.
* **Backup Cron Workload** *(optional)* — A scheduled backup job that writes one snapshot per primary shard to AWS S3 or GCS.

## Installation

This template has no external prerequisites unless backup is enabled. To install, follow the instructions for your preferred method:

<CardGroup cols={2}>
  <Card title="UI" href="/template-catalog/install-manage/ui" icon="laptop">
    Browse, install, and manage templates visually
  </Card>

  <Card title="CLI" href="/template-catalog/install-manage/cli" icon="terminal">
    Manage templates from your terminal
  </Card>

  <Card title="Terraform" href="/template-catalog/install-manage/terraform" icon={<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128"><g fill-rule="evenodd"><path d="M77.941 44.5v36.836L46.324 62.918V26.082zm0 0" fill="#5c4ee5"/><path d="M81.41 81.336l31.633-18.418V26.082L81.41 44.5zm0 0" fill="#4040b2"/><path d="M11.242 42.36L42.86 60.776V23.941L11.242 5.523zm0 0M77.941 85.375L46.324 66.957v36.82l31.617 18.418zm0 0" fill="#5c4ee5"/></g></svg>}>
    Declare templates in your Terraform configurations
  </Card>

  <Card
    title="Pulumi"
    href="/template-catalog/install-manage/pulumi"
    icon={<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" id="Pulumi-Icon--Streamline-Svg-Logos" height="24" width="24">
    <desc>
        Pulumi Icon Streamline Icon: https://streamlinehq.com
    </desc>
    <path fill="#f26e7e" d="M4.683025 13.3318c0.869125 -0.5018 0.870575 -2.1264 0.003225 -3.62865s-2.27504 -2.313275 -3.1441725 -1.811475C0.672945 8.3935 0.6715 10.0181 1.53885 11.52035c0.86735 1.502275 2.27505 2.313275 3.144175 1.81145Zm0.0052 3.2167c0.86735 1.502275 0.865925 3.126875 -0.003225 3.628675 -0.86915 0.5018 -2.2768275 -0.309225 -3.144175 -1.81145 -0.8673525 -1.50225 -0.8659075 -3.126875 0.003225 -3.628675 0.8691325 -0.5018 2.276825 0.309225 3.144175 1.81145Zm5.922875 3.4243c0.86735 1.50225 0.8659 3.126775 -0.003225 3.62875 -0.869125 0.501775 -2.27685 -0.309325 -3.1442 -1.81155 -0.867325 -1.50225 -0.865875 -3.12685 0.00325 -3.628675 0.869125 -0.5018 2.276825 0.309225 3.144175 1.811475Zm-0.001925 -6.845275c0.86735 1.50225 0.8659 3.12685 -0.003225 3.628675 -0.869125 0.5018 -2.276825 -0.309225 -3.144175 -1.811475 -0.86735 -1.50225 -0.8659 -3.12685 0.003225 -3.62865 0.869125 -0.501825 2.276825 0.3092 3.144175 1.81145Z" stroke-width="0.25"></path>
    <path fill="#8a3391" d="M22.45775 11.524125c0.86725 -1.502225 0.865925 -3.12685 -0.003225 -3.62865 -0.869125 -0.501825 -2.276825 0.3092 -3.144175 1.811475 -0.86735 1.50225 -0.8659 3.126825 0.003225 3.62865 0.869125 0.501825 2.276825 -0.3092 3.144175 -1.811475Zm0.000175 3.2151c0.869075 0.5018 0.870625 2.1264 0.003225 3.62865 -0.86735 1.50225 -2.27505 2.313275 -3.144175 1.81145 -0.869125 -0.5018 -0.870575 -2.126425 -0.003225 -3.62865 0.86735 -1.50225 2.27505 -2.313275 3.144175 -1.81145ZM16.536225 18.157875c0.86915 0.501825 0.8706 2.126425 0.00325 3.628675 -0.86735 1.502125 -2.275075 2.313225 -3.1442 1.81145 -0.869125 -0.50175 -0.870575 -2.126425 -0.003225 -3.62865 0.867375 -1.502275 2.27505 -2.3133 3.144175 -1.811475Zm-0.003325 -6.843775c0.869125 0.5018 0.870575 2.126425 0.003225 3.628675s-2.27505 2.313275 -3.1442 1.811475c-0.869125 -0.501825 -0.870575 -2.126425 -0.003225 -3.628675 0.86735 -1.502275 2.27505 -2.313275 3.1442 -1.811475Z" stroke-width="0.25"></path>
    <path fill="#f7bf2a" d="M15.138225 2.06721c0 1.003615 -1.40625 1.817215 -3.14095 1.817215 -1.7347 0 -3.14095 -0.8136 -3.14095 -1.817215C8.856325 1.06359 10.262575 0.25 11.997275 0.25c1.7347 0 3.14095 0.81359 3.14095 1.81721ZM9.2166 5.482375c0 1.003625 -1.40625 1.8172 -3.14095 1.8172 -1.7347 0 -3.14095 -0.813575 -3.14095 -1.8172s1.40625 -1.817225 3.14095 -1.817225c1.7347 0 3.14095 0.8136 3.14095 1.817225Zm8.71005 1.8172c1.7347 0 3.14095 -0.813575 3.14095 -1.8172s-1.40625 -1.817225 -3.14095 -1.817225c-1.7347 0 -3.14095 0.8136 -3.14095 1.817225s1.40625 1.8172 3.14095 1.8172Zm-2.788425 1.605625c0 1.003625 -1.40625 1.8172 -3.14095 1.8172 -1.7347 0 -3.14095 -0.813575 -3.14095 -1.8172 0 -1.0036 1.40625 -1.8172 3.14095 -1.8172 1.7347 0 3.14095 0.8136 3.14095 1.8172Z" stroke-width="0.25"></path>
    </svg>}
  >
    Declare templates in your Pulumi programs
  </Card>
</CardGroup>

## Configuration

The default `values.yaml` for this template:

```yaml theme={null}
# ─── Engine ───────────────────────────────────────────────────────────────────
# Which server the cluster nodes run. `redis` is the default and changes nothing.
# `valkey` runs every node on the image below — Valkey is the BSD-licensed fork
# of Redis 7.2 and ships redis-server / redis-cli compatibility symlinks, so the
# cluster bootstrap, config directives and probes are identical.
# Chosen at INSTALL time: switching an existing release between engines is not
# supported — see Engine Selection below.
engine: redis                     # redis | valkey
# Used for every node when engine is valkey; `image` below is then ignored.
# Only the Debian-based tags are supported (the -alpine tags have no bash and the
# start script requires it).
valkeyImage: valkey/valkey:8.1.9

# Redis image for the cluster nodes. Pinned so installs are reproducible.
image: docker.io/redis:7.2

replicas: 6 # minimum value is 6
port: 6379
memory: 250Mi
cpu: 200m

internalAccess: # Sets the internal firewall scope - if set to none, replicas will not be able to reach each other
  type: same-gvc # options: none, same-gvc, same-org, workload-list
  workloads:  # Note: can only be used if type is same-gvc or workload-list
    #- //gvc/GVC_NAME/workload/WORKLOAD_NAME
    #- //gvc/GVC_NAME/workload/WORKLOAD_NAME

volumeset:
  capacity: 10 # initial capacity in GiB (minimum is 10)
  autoscaling:
    enabled: false # Set to true to enable autoscaling
    maxCapacity: 100 # Maximum capacity in GiB when autoscaling is enabled
    minFreePercentage: 10 # Minimum free percentage to trigger scaling when autoscaling is enabled
    scalingFactor: 1.2 # Scaling factor to determine how much to scale up when autoscaling is triggered

# Configure Redis authentication by uncommenting and setting the password field
redis: {}
  # password: "your-secure-password-here"

backup:
  enabled: false
  image: ghcr.io/controlplane-com/backup-images/redis-backup:1.0.0
  schedule: "0 2 * * *"  # daily at 2am UTC

  resources:
    cpu: 100m
    memory: 512Mi # cluster mode dumps every primary sequentially; 128Mi OOMs during GCS uploads

  provider: aws # Options: aws or gcp

  aws:
    bucket: my-backup-bucket
    region: us-east-1
    cloudAccountName: my-backup-cloudaccount
    policyName: my-backup-policy
    prefix: "redis-cluster/backups"

  gcp:
    bucket: ""
    cloudAccountName: ""
    prefix: "redis-cluster/backups"
```

### Engine Selection

`engine` chooses which server every cluster node runs. `redis` is the default and changes nothing — an install that does not set it deploys exactly as it did before version 1.6.0.

Set `engine: valkey` to run the cluster on [Valkey](https://valkey.io/) instead. Valkey is the BSD-3-Clause fork of Redis 7.2, stewarded by the Linux Foundation. It has no paid edition, so nothing in it is feature-gated.

```yaml theme={null}
engine: valkey
valkeyImage: valkey/valkey:8.1.9
```

Nothing else in the template changes. The Valkey image ships `redis-server`, `redis-cli`, `redis-sentinel`, `redis-benchmark`, `redis-check-rdb` and `redis-check-aof` compatibility symlinks, so the cluster bootstrap script, the config directives, the readiness probe and the backup job all run unmodified. Sharding, `MOVED` redirects, hostname announcement, authentication, replica counts, backups and firewall behavior are identical on both engines.

|                       | `engine: redis` (default) | `engine: valkey`                                                                                 |
| --------------------- | ------------------------- | ------------------------------------------------------------------------------------------------ |
| Image knob read       | `image`                   | `valkeyImage`                                                                                    |
| Default image         | `docker.io/redis:7.2`     | `valkey/valkey:8.1.9`                                                                            |
| License               | Redis RSALv2 / SSPLv1     | BSD-3-Clause                                                                                     |
| `INFO server` reports | `redis_version:7.2.x`     | `server_name:valkey`, `valkey_version:8.1.9`, and `redis_version:7.2.4` for client compatibility |

* `valkeyImage` is used for every node whenever `engine` is `valkey`, and `image` is then ignored entirely — a Redis tag left in `image` has no effect.
* Use the Debian-based Valkey tags. The `-alpine` tags ship no `bash`, which both the cluster start script and the readiness probe require.
* Valkey reports `redis_version:7.2.4` in `INFO` for client compatibility. Read `server_name` and `valkey_version` to tell what is actually running — anything that version-gates on `redis_version` will believe it is talking to Redis 7.2.
* The marketplace card still shows the Redis version. A chart's `appVersion` is a constant and cannot follow a values knob.
* Do not enable `dual-channel-replication-enabled` on Valkey; a known upstream defect confuses replica accounting.

<Warning>
  **Choose the engine at install time — switching an existing release is not supported.**

  On the pinned default image (`docker.io/redis:7.2`) the switch does carry the data across: it was tested, all 48 seeded keys survived, and the cluster re-formed from its persisted `nodes.conf`. That only holds while `image` is untouched. Move `image` to a newer Redis and the same switch destroys the node instead — newer Redis releases write an on-disk format Valkey rejects, and on `redis:8` the node fails with `Can't handle RDB format version 15` and exits rather than starting.

  The failure is also close to invisible. The start script discards server output, so `cpln logs` returns zero lines for the failing node and the deployment message is empty; the only symptom is a node that never becomes ready. Migrate with dump/restore or replication instead of relying on the switch.
</Warning>

### Authentication

Authentication is disabled by default. To enable it, set a password:

```yaml theme={null}
redis:
  password: "your-secure-password-here"
```

When set, the password is stored in a dictionary secret and injected into both `requirepass` and `masterauth` in `redis.conf`, ensuring all nodes authenticate with each other.

From version 1.6.0 the readiness probe requires an actual `PONG` from the node. Earlier versions ran `redis-cli ping`, which exits 0 even when the server answers `NOAUTH Authentication required` — so a password-protected cluster reported ready even with the wrong password. One consequence is worth knowing: because the workload rolls with `OrderedReady`, a bad credential now stalls the rollout at the first replica instead of rolling all six nodes into a broken state while every status surface reports healthy.

### Cluster Size

* `replicas` — Total number of Redis nodes. **6 is the only supported value** (3 primaries + 3 replicas). The cluster is always created with `--cluster-replicas 1`, meaning each primary has exactly one replica. Replica 0 waits for all nodes to be healthy before running `redis-cli --cluster create`.

<Note>
  `replicas` is effectively pinned at 6, not merely floored. Below 6 the cluster cannot initialize — it requires 3 primary nodes for quorum and this template pairs each with a replica. Above 6 the install is rejected at apply: the workload uses replica-direct addressing, and a built-in platform quota caps replica-direct workloads at 6, so `replicas: 8` fails with `exceed the autoscaling.maxScale of 6 (quota: replicas-per-replica-direct-workload)`.
</Note>

### Resources

* `cpu` — CPU allocated to each Redis node.
* `memory` — Memory allocated to each Redis node.

### Storage

A Volume Set is always created to persist cluster data. The file system is `ext4` and the performance class is `general-purpose-ssd`.

* `volumeset.capacity` — Initial volume size in GiB per node (minimum 10).
* `volumeset.autoscaling.enabled` — Automatically expand volumes as they fill. When enabled:
  * `maxCapacity` — Maximum volume size in GiB.
  * `minFreePercentage` — Trigger a scale-up when free space drops below this percentage.
  * `scalingFactor` — Multiply current capacity by this factor when scaling up.

### Internal Access

Redis Cluster nodes must be able to communicate with each other on both the data port and the cluster bus port. Setting `internalAccess.type` to `none` will prevent inter-node communication and break the cluster.

* `internalAccess.type` — Controls which workloads can connect to the cluster:

| Value           | Description                                                     |
| --------------- | --------------------------------------------------------------- |
| `same-gvc`      | Allow access from all workloads in the same GVC (recommended)   |
| `same-org`      | Allow access from all workloads in the same organization        |
| `workload-list` | Allow access only from specific workloads listed in `workloads` |

* `internalAccess.workloads` — List of specific workload links, used when `type` is `workload-list`.

### Connecting to Redis Cluster

Redis Cluster requires a cluster-aware client. Connect to any node as a seed address — the client will discover the rest of the cluster automatically:

```text theme={null}
RELEASE_NAME-redis-cluster.GVC_NAME.cpln.local:6379
```

Each individual node is also accessible directly:

```text theme={null}
RELEASE_NAME-redis-cluster-N.RELEASE_NAME-redis-cluster.GVC_NAME.cpln.local:6379
```

Both forms use the configured `port`; substitute your own value if you changed it. Add `-a PASSWORD` to your client when `redis.password` is set.

### Ports

| Port                             | Protocol | Description                                              |
| -------------------------------- | -------- | -------------------------------------------------------- |
| `port` — default `6379`          | TCP      | Redis data port                                          |
| `port + 10000` — default `16379` | TCP      | Cluster bus (node-to-node gossip and failover elections) |

* `port` — The port every node listens on. Redis derives the cluster bus as `port + 10000`, and the template declares both ports, so a custom `port` needs no further configuration — but any client or firewall rule expecting `16379` must move with it.

<Warning>
  **`port` only works from version 1.6.0.** In earlier versions any non-default value crash-looped every node permanently. Three defects combined: the readiness probe and six calls in the cluster start script ran `redis-cli` with no `-p` (so they always targeted `127.0.0.1:6379`), and the cluster bus was hardcoded to `16379` instead of being derived from `port`, leaving gossip undeclared so the cluster could never form. All three are fixed in 1.6.0 and verified on live installs at `6379`, `7000`, `7100` and `7300`, with the derived bus proven by observing a failover election carried over it. At the default port nothing about the deployment changes, so existing installs are unaffected.
</Warning>

## Cluster Formation and Readiness

A fresh install takes roughly five minutes to become usable. The workload is stateful and brings its six replicas up in order; replica 0 then runs `redis-cli --cluster create` once every node answers.

<Note>
  **`ready` means the node answers, not that the cluster exists.** The readiness probe asks the local server for a `PONG`. It cannot also require `cluster_state:ok`, because the cluster is not created until all six nodes are up and the workload rolls with `OrderedReady` — a probe that waited for cluster state would deadlock the rollout it gates. During first boot a node can therefore report ready with no cluster for up to about a minute while creation retries.
</Note>

Check the cluster itself rather than the workload's ready flag:

```bash theme={null}
cpln workload exec RELEASE_NAME-redis-cluster \
  --gvc GVC_NAME \
  --replica RELEASE_NAME-redis-cluster-0 \
  --container redis-cluster \
  -- redis-cli -p 6379 cluster info
```

`cluster_state:ok` with `cluster_slots_assigned:16384` and `cluster_known_nodes:6` means the cluster is formed. Add `-a PASSWORD` to the `redis-cli` call when `redis.password` is set, and use your own value if you changed `port`.

Version 1.6.0 also fixes a bootstrap DNS race on default installs. Cluster creation re-resolves every peer hostname, and a name that answered moments earlier can briefly stop resolving; that used to kill the container, measured at up to 7 restarts and around 20 minutes to converge. Creation now retries in place, and still fails loudly if it genuinely cannot succeed.

## Backup

Backup is disabled by default. When enabled, a cron workload runs on the configured schedule and produces one compressed `.rdb.gz` file per primary shard, uploaded to AWS S3 or GCS. The backup image is compatible with all Redis versions.

* `backup.enabled` — Enable scheduled backups.
* `backup.schedule` — Cron expression for backup frequency (default: daily at 2am UTC).
* `backup.provider` — `aws` or `gcp`.
* `backup.resources.cpu` / `backup.resources.memory` — Resources for the backup cron container.

### AWS S3

Before enabling backup with `provider: aws`, complete the following in your AWS account:

1. Create an S3 bucket. Set `backup.aws.bucket` to its name and `backup.aws.region` to its region.
2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.aws.cloudAccountName` to its name.
3. Create an IAM policy with the following JSON, replacing `YOUR_BUCKET_NAME`:

<Warning>
  **Version 1.4.4 narrows AWS backup permissions.** This version removes `aws::ReadOnlyAccess` from the backup identity. That AWS managed policy granted read access to **every bucket in your AWS account** and contains no write actions at all, so it was never carrying the backup itself — but it *was* silently supplying any read action your own bucket-scoped policy happened to omit.

  **Update your IAM policy to the full action list below before upgrading.** If it already matches, no action is needed. The identity now carries `cpln-connector` and your bucket-scoped policy only, which is strictly narrower than before. Nothing else changes.
</Warning>

```json theme={null}
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject",
                "s3:ListBucket",
                "s3:GetObjectVersion",
                "s3:DeleteObjectVersion",
                "s3:GetBucketLocation",
                "s3:AbortMultipartUpload",
                "s3:ListBucketMultipartUploads",
                "s3:ListMultipartUploadParts"
            ],
            "Resource": [
                "arn:aws:s3:::YOUR_BUCKET_NAME",
                "arn:aws:s3:::YOUR_BUCKET_NAME/*"
            ]
        }
    ]
}
```

4. Set `backup.aws.policyName` to the name of the policy created in step 3.
5. Set `backup.aws.prefix` to the folder path where backups will be stored.

### GCS

Before enabling backup with `provider: gcp`, complete the following in your GCP account:

1. Create a GCS bucket. Set `backup.gcp.bucket` to its name.
2. If you do not have a Cloud Account set up, refer to the docs to [Create a Cloud Account](/guides/create-cloud-account). Set `backup.gcp.cloudAccountName` to its name.
3. Add the **Storage Admin** role to the GCP service account associated with the Cloud Account.
4. Set `backup.gcp.prefix` to the folder path where backups will be stored.

## Restoring a Backup

Each primary shard produces its own backup file (e.g. `redis-<timestamp>-node-0.rdb.gz`). Download and decompress the file for the shard you want to restore, then copy it to `/data/dump.rdb` on the corresponding replica and restart that replica.

For GCS, replace `aws s3 cp s3://...` with `gsutil cp gs://...`.

```sh theme={null}
aws s3 cp s3://BUCKET_NAME/PREFIX/BACKUP_FILE.rdb.gz - \
  | gunzip > /tmp/dump.rdb
```

## External References

<CardGroup cols={2}>
  <Card title="Redis Cluster Documentation" icon="book" href="https://redis.io/docs/latest/operate/oss_and_stack/management/scaling/">
    Official Redis Cluster setup and client configuration guide
  </Card>

  <Card title="Backup Image Source" icon="github" href="https://github.com/controlplane-com/backup-images/tree/main/redis-backup">
    Source code for the Redis backup container image
  </Card>

  <Card title="Redis Cluster Template" icon="github" href="https://github.com/controlplane-com/templates/tree/main/redis-cluster">
    View the source files, default values, and chart definition
  </Card>

  <Card title="Valkey Documentation" icon="book" href="https://valkey.io/topics/">
    Valkey topic guides, including the cluster tutorial
  </Card>
</CardGroup>
