Skip to main content
Version 3.0.0 no longer creates a GVC — it deploys into one you already have, and there is no in-place upgrade path from 2.x. A helm upgrade across that boundary deletes the GVC the old release created and every workload, volume set and identity inside it. See Migrating From Version 2.

Overview

Redis Multi-Location deploys a single Redis Sentinel cluster spanning multiple Control Plane locations within one GVC. All replicas across all locations belong to the same cluster — replica 0 in the first configured location starts as the master, and Sentinel automatically elects a new master from any location on failure. Since template version 2.2.0 the same cluster can run Valkey instead of Redis. It is one value, chosen at install, and it changes nothing else about the deployment — see Redis or Valkey.
This template deploys into a GVC you already have, and that GVC must have at least 2 locations. It does not create, provision or manage a GVC. For a cluster inside a single location, use Redis instead — that template is a full single-location Sentinel cluster.

How Many Locations You Need

A failover needs a majority of Sentinels to agree, and Sentinel runs exactly one instance per location. That arithmetic decides what the cluster survives. With N locations you survive floor((N-1)/2) losses, so an even count buys nothing over the odd count below it.

What Gets Created

  • Stateful Redis Workload{release}-redis, running redis.replicasPerLocation instances in every configured location. One instance is the master; the rest replicate from it, across locations. Each instance gets its own persistent volume and is individually addressable.
  • Stateful Sentinel Workload{release}-sentinel, exactly 1 instance per location (not configurable). Monitors the master, holds the failover vote, and persists the elected master and its peer list to its own volume.
  • Volume Sets{release}-redis-vs for Redis data (configurable capacity and autoscaling) and {release}-sentinel-vs (10 GiB) for Sentinel’s rewritten configuration.
  • Secrets — Opaque secrets holding the Redis and Sentinel base configurations, mounted into each container. Passwords are not created by this template — see Prerequisites.
  • Identities & Policies — A separate identity and policy per tier, each with reveal on exactly the secrets that tier reads, plus a bucket-scoped cloud binding when backups are enabled. The Redis tier gets one more policy granting view on exactly the one GVC this release installs into, which is what lets each instance check that GVC’s location list at boot. It is scoped to that single GVC, never to the whole org.
  • Domain (optional) — One domain per workload when public access is enabled, exposing Redis and Sentinel at the configured addresses.
  • Backup Cron Workload (optional){release}-redis-backup, a scheduled job that writes a compressed snapshot to AWS S3 or GCS. Runs in the first configured location only.
No GVC resource is created. Every resource above lands in the GVC you install into, and nothing runs in that GVC’s other locations.

Prerequisites

An existing GVC with at least 2 locations, and global.locations listing exactly the locations you want this release to run in. Every name you list must already be one of that GVC’s locations — read them back with cpln gvc get GVC_NAME -o yaml and compare spec.staticPlacement.locationLinks. Extra locations in the GVC are fine; nothing runs in them. See Matching the Location List to the GVC. A default install needs nothing else. Passwords, backups and public access each add a step:
1

Create the password secrets (only if you want authentication)

Passwords are never Helm values, so they never land in the release. Each one is an opaque secret with encoding: plain whose payload is the password itself — one value, no keys:
Set redis.passwordSecretName and sentinel.passwordSecretName to the names you used, and read a password back later with cpln secret reveal my-redis-password. Leave either value empty to run that tier without a password.
2

Create a bucket and cloud account (only for backups)

See Backup for the per-provider steps.
3

Add DNS records, and enable a dedicated load balancer on the GVC (only for public access)

Both must be in place before the deploy that turns public access on. See Public Access.
Create the password secrets before installing. Without them helm install still reports success, but the workload waits on a secret that does not exist and cpln logs returns nothing at all — the reason appears only in cpln workload get-deployments WORKLOAD --gvc GVC_NAME -o yaml, under status.versions[].message. Creating the secret afterwards clears the wedge on its own within several minutes.

Installation

To install, follow the instructions for your preferred method:

UI

Browse, install, and manage templates visually

CLI

Manage templates from your terminal

Terraform

Declare templates in your Terraform configurations

Pulumi

Declare templates in your Pulumi programs

Configuration

The default values.yaml for this template:

Locations

The location list lives under global so that a parent chart can set it once when this template is used as a subchart — that is the only channel that reaches an aliased subchart, and it keeps the two lists from ever being edited apart.
  • global.locations[].name — A Control Plane location (e.g. aws-us-east-1) that must already be one of the locations of the GVC you install into. At least 2 are required; see How Many Locations You Need.
  • The first location listed is where the initial master is seeded, and where the backup cron runs.
global.locations[].replicas is deliberately not read by this template — that field belongs to parent charts that share the same location list, where it already means the parent’s own members per location. Set redis.replicasPerLocation instead, which applies to every location. A standalone install that sets replicas fails at render with a message saying so.

Matching the Location List to the GVC

The platform validates the pairing in neither direction. A location the GVC lacks is accepted, stored and simply inert, and both its Redis instances and its Sentinel never start — which quietly removes part of the Sentinel quorum that automatic failover depends on, while every status surface reads healthy. Extra locations in the GVC are harmless: they get minScale/maxScale of 0 and their deployments read This workload location is deactivated because maxScale is set to 0. The Redis instances therefore read their own GVC at boot, using the scoped view grant described in What Gets Created, and refuse to bootstrap a cluster that could never work. On a fresh data directory each of these is fatal — the container exits with code 1 and logs a [redis] FATAL: line naming the problem: On an already-initialized cluster all three are warnings only and the instances keep serving, so that losing a location cannot also take the cluster down. Read the [redis] lines with cpln logs '{gvc="GVC_NAME", workload="RELEASE_NAME-redis"}'.
Before version 3.0.0 this check never actually ran. Its request to the platform API was rejected before it could read anything, so every instance took the fail-open branch and skipped the whole thing silently. Following the earlier guidance could therefore leave you with a cluster that had no master and refused every write while both workloads reported healthy. The check works from 3.0.0 onward and has been verified on all three arms.

Redis or Valkey

engine picks the server both tiers run. It is the only difference between the two shapes — topology, replica counts, configuration, Sentinel behavior, secrets, firewall and backups are identical, and setting engine: valkey changes exactly the two image references in the whole deployment. Valkey is the fork of Redis 7.2 that the Linux Foundation stewards. Its image ships redis-server, redis-cli and redis-sentinel compatibility symlinks, so every command, configuration directive and connection string on this page is unchanged — including masterauth, sentinel auth-pass and the hostname-based replica discovery this template relies on across locations. redis.serverCommand stays redis-server on both engines.
  • engine binds both tiers at once. You cannot end up with a Valkey server behind a Redis Sentinel by accident: setting engine: valkey makes redis.image and sentinel.image inert.
  • Set valkeyImage to a Debian-based tag. Both tiers assemble their configuration with echo "\n...", which busybox does not expand, so an -alpine tag fails at start with Bad directive or wrong number of arguments. The redis -alpine tags fail the same way.
  • Valkey 9.x tags are reachable through valkeyImage but have not been tested with this template. Valkey 9 writes RDB format 80, which no Redis release can read, so it is a one-way move rather than a supported upgrade.
  • INFO reports redis_version:7.2.4 on Valkey for client compatibility. Read server_name and valkey_version to see what is really running — anything that version-gates on redis_version believes it is talking to Redis 7.2.
  • The marketplace card and the app.cpln.io/version tag still show the Redis version (7.4) on a Valkey install. A chart’s appVersion is a constant and cannot follow a values setting.
engine is an install-time choice and cannot be changed on an existing install. This template ships appendonly yes, and redis:7.4 writes its append-only base file in RDB format 12, which Valkey 8 refuses. helm upgrade reports success, then every Redis replica crash-loops with Can't handle RDB format version 12 and Error reading the RDB base file appendonly.aof.N.base.rdb, AOF loading aborted, exiting with code 1. Setting engine back to redis and upgrading again recovers the data untouched — measured at 130 seconds with all 100 test keys intact. Move between engines with a dump and restore, or by replicating into a fresh install; never by changing the value.

Authentication

Authentication is disabled by default. Redis and Sentinel take independent passwords, each supplied as the prerequisite secret named by its own value — never as a Helm value.
  • redis.passwordSecretName — Names the opaque secret whose payload is applied as both requirepass and masterauth on every Redis instance. Empty means no Redis password at all.
  • sentinel.passwordSecretName — Names the opaque secret whose payload is applied as requirepass on the Sentinel process. Independent of the Redis password; empty means Sentinel itself is unauthenticated.
Both tiers read their secret at container start, and Sentinel rewrites its own configuration from it on every start.
Rotating a password requires a forced redeployment — it does not apply on its own. Updating the secret in place restarts nothing: measured five minutes after a rotation, the workload version was unchanged and the old password was still accepted, with a healthy status throughout. There is no error anywhere, so a rotation looks like it worked while the old credential keeps working indefinitely. Apply it explicitly, Sentinel first and Redis second:
Adding, changing or removing redis.passwordSecretName through a helm upgrade restarts every Redis instance in every location at once, which takes the master out of quorum and starts a failover. Treat it as a planned restart of the whole tier. Changing only the Sentinel password is safe — a Sentinel-only restart triggers no failover vote.

Redis

  • redis.image — Redis image to use. Ignored when engine is valkey.
  • redis.replicasPerLocation — Number of Redis instances to run in each location (default 2). The total instance count is this value multiplied by the number of locations.
  • redis.resources.cpu / redis.resources.memory — CPU and memory allocated per Redis instance.
  • redis.serverCommand — The server binary to execute. redis-server is correct for both engines.
  • redis.extraArgs — Additional server arguments appended to the startup command, for example --maxmemory 200mb --maxmemory-policy allkeys-lru. They override the equivalent settings in the base configuration.

Sentinel

Sentinel monitors the Redis master across all locations and promotes a replica on failure. One Sentinel instance is deployed per location — this is fixed and not configurable. Quorum is calculated automatically from the number of locations. The master name is mymaster.
  • sentinel.image — Image to use for Sentinel. Ignored when engine is valkey.
  • sentinel.resources.cpu / sentinel.resources.memory — CPU and memory allocated per Sentinel instance.
  • sentinel.extraArgs — Additional Sentinel arguments, for example --sentinel down-after-milliseconds mymaster 5000.

Storage

  • redis.volumeset.initialCapacity — Initial volume size in GiB for Redis data (default 20). Every Redis instance gets its own volume.
  • redis.volumeset.autoscaling.enabled — Automatically expand the volume as it fills. 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.
Sentinel’s own 10 GiB volume, which holds its rewritten configuration, is not configurable.

Public Access

Redis and Sentinel can be exposed over the internet as raw TCP via Control Plane’s domain resource, with per-replica port routing.
A dedicated load balancer must already be enabled on your GVC, and this template can no longer enable it for you. TCP port routing needs one, and it is a paid Control Plane feature. Earlier versions turned it on as part of creating the GVC; 3.0.0 creates no GVC, so enable it on the GVC’s load balancer settings before the deploy that turns public access on. A domain pointed at a GVC without one will not serve traffic.
  • redis.publicAccess.enabled — Expose Redis publicly (default: false).
  • redis.publicAccess.address — A domain you control to expose Redis (e.g. redis.your-domain.com).
  • sentinel.publicAccess.enabled — Expose Sentinel publicly (default: false).
  • sentinel.publicAccess.address — A domain you control to expose Sentinel (e.g. redis-sentinel.your-domain.com).
DNS records must be added before deploying. If ownership has not been proven, the first deploy will fail and the error message will list the exact TXT and CNAME records required. Add those records in your DNS provider, then upgrade the release to complete the deployment. Disable DNS proxying (e.g. Cloudflare’s orange cloud) — TCP traffic must pass through directly. When enabled, a Control Plane domain resource is created for each address. Port mapping is one port per replica: firewall.externalInboundAllowCIDR defaults to 0.0.0.0/0 when public access is enabled; set it to restrict which addresses can reach the workloads. Connecting externally:
Add --no-auth-warning -a "$PASSWORD" to either command if you set the matching password.

Firewall

A single firewall configuration applies to both the Redis and Sentinel workloads.
  • firewall.internalAllowType — Controls which workloads can connect internally:
  • firewall.workloads — List of specific workload links, used when internalAllowType is workload-list.
  • firewall.externalInboundAllowCIDR — Comma-separated list of CIDRs allowed to reach the workloads externally. Defaults to 0.0.0.0/0 when public access is enabled.
  • firewall.externalOutboundAllowCIDR — Comma-separated list of CIDRs the workloads are allowed to connect to externally.
With workload-list, list only your clients — never this release’s own workloads. The same list governs Redis-to-Redis replication and Sentinel’s monitoring of both tiers, so a list naming only clients used to cut the cluster off from itself while every replica still reported ready. The Redis, Sentinel and backup-cron workloads are appended for you from version 3.0.0, and a client that is not listed is still refused.
Do not pin externalInboundAllowCIDR to a single /32 unless that address is genuinely static. An allow list is only as stable as the egress address in it: when your office, VPN or NAT gateway rotates its IP, the entry stops matching and you lose access to your own cluster from outside. Allow the range your egress can actually come from, and keep an in-GVC path you can still reach.
A firewall change is not instant — 132 seconds was measured on this template, and longer elsewhere. Allow several minutes and re-test before concluding a setting did not apply.

Connecting to Redis

All replicas across all locations belong to a single cluster and are reachable from anywhere in the GVC. Replica 0 in the first configured location starts as the master; after any failover, Sentinel is the authority on where the master is. Option 1 — Load-balanced endpoint (any replica; only the master accepts writes):
Option 2 — Direct to a specific replica:
Option 3 — Via Sentinel, to always write to the current master:
Add --no-auth-warning -a "$SENTINEL_PASSWORD" to the Sentinel call and --no-auth-warning -a "$REDIS_PASSWORD" to the Redis call when passwords are configured. For Sentinel-aware clients, connect through:
A specific Sentinel is reachable at replica-0.RELEASE_NAME-sentinel.LOCATION.GVC_NAME.cpln.local:26379. The Sentinel master name is mymaster.

Ports

Migrating From Version 2

Every release before 3.0.0 created its own GVC. Do not helm upgrade a 2.x release onto 3.0.0. Once the chart stops declaring a GVC, Helm prunes the one the old release created — and deleting a GVC deletes every workload, volume set and identity inside it, in seconds, while the command prints upgraded successfully.
The chart refuses to render when your values still carry the 2.x global.gvc key, so a values-carrying upgrade fails safely before any resource is touched. That guard cannot fire on an upgrade run with no values file at all, because it then sees only 3.0.0’s own defaults. The procedure below is the safety; the render guard is only a backstop.
Install 3.0.0 as a new release against an existing GVC, copy the keyspace across, then remove the old release.
1

Pick the target GVC

It needs at least two locations, and every entry you put in global.locations must be one of them.
2

Install 3.0.0 as a new release, keeping the old one running

Delete global.gvc.name from your values and rename global.gvc.locations to a top-level global.locations.
3

Move the data

Point a client at the old cluster’s current master and the new one’s, and copy the keyspace across — redis-cli --scan plus DUMP/RESTORE, or replication from the old master, whichever suits your dataset. Confirm the new cluster serves reads and writes before going on.
4

Cut clients over

Every internal hostname contains the GVC name, so every connection string changes.
5

Remove the old release

Uninstalling it deletes the GVC it created and the old volume sets with it, so make sure the previous step is genuinely complete and nothing else depends on that GVC.
Values that changed:

Availability and Failover

Measured on a two-location cluster (aws-us-east-1 and aws-us-west-2) running Valkey 8.1.9 with replicasPerLocation: 2 — one master and three replicas — and a one-second write probe connecting through Sentinel.
Treat every helm upgrade as a planned write outage and a change of master. Instances do not restart one at a time — the field that would serialize the rollout is not retained by the platform on a stateful workload, so all of them go down together and Sentinel elects a new master when they return.
A three-location run of 3.0.0 (aws-us-east-1, aws-us-east-2, aws-us-west-2, replicasPerLocation: 2) measured the case the template exists for: with the master taken out, a replica in a different location was promoted in 8–9 seconds across two runs, all three Sentinels converged on the same new master with no split brain, pre-failover data survived the promotion, and the old master rejoined as a replica of the new one and received the writes it had missed.
The two-location measurements above are the loss of the master instance, not of a whole location — a two-location cluster cannot fail over on location loss, because its Sentinel quorum is 2 of 2. Use three or more locations to survive losing one.
Replication is asynchronous, so a failover can lose writes the promoted replica had not yet received, bounded by the replication lag at the moment of failure.

Backup

Backup is disabled by default. When enabled, a cron workload runs in the first configured location only and uploads a single compressed redis-<timestamp>.rdb.gz snapshot to AWS S3 or GCS. It runs in one location because a cron workload otherwise fires in every location of its GVC and writes one copy of the same dump per location into the same bucket.
  • backup.enabled — Enable scheduled backups.
  • backup.image — The backup container image.
  • backup.schedule — Cron expression for backup frequency (default: daily at 02:00 UTC).
  • backup.provideraws or gcp.
  • backup.resources.cpu / backup.resources.memory — Resources for the backup cron container.
The job connects to the load-balanced Redis endpoint, so the snapshot is taken from whichever instance answers — usually a replica rather than the master. Replicas apply writes asynchronously, so a snapshot can be marginally behind the master at the instant it was taken.

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. Set backup.aws.cloudAccountName to its name.
  3. Create an IAM policy with the following JSON, replacing YOUR_BUCKET_NAME:
  1. Set backup.aws.policyName to the name of the policy created in step 3. This bucket-scoped policy is all the workload identity needs — no broad managed policy is required.
  2. 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. Set backup.gcp.cloudAccountName to its name.
  3. Add the Storage Admin role to the GCP service account associated with the Cloud Account. The template additionally binds the identity to roles/storage.objectAdmin on exactly the bucket named in backup.gcp.bucket.
  4. Set backup.gcp.prefix to the folder path where backups will be stored.

Restoring a Backup

The backup produces a single file (redis-<timestamp>.rdb.gz).
Restoring is not a one-liner here, and the obvious approach silently does nothing. This template runs with appendonly yes, so Redis loads appendonlydir/ at start and ignores dump.rdb entirely — copying a downloaded RDB into the data directory changes nothing, and reports no error. The Redis image also ships neither aws nor gsutil, so the download happens outside the container.
Fetch the object first:
Loading it then requires starting the instance with AOF disabled so the RDB is read, and re-enabling AOF afterwards so it is rewritten from memory. That sequence has not been verified against this template. Rehearse it against a scratch install before you need it, rather than first attempting a restore during an incident.

Important Notes

  • There is no upgrade path from 2.x. Every 2.x release created its own GVC; 3.0.0 deploys into an existing one, and a helm upgrade across that boundary deletes the old GVC and everything in it. The chart refuses to render on the 2.x global.gvc key, but that guard cannot see an upgrade run with no values at all. Follow Migrating From Version 2.
  • Every location in global.locations must already exist in the GVC you install into. The platform accepts one that does not, stores it and runs nothing there — which silently removes part of the Sentinel quorum. A fresh install now refuses to bootstrap and names the problem; an already-initialized cluster warns and keeps serving. See Matching the Location List to the GVC.
  • Choose engine before the first install. It cannot be changed afterwards: a Valkey server refuses the on-disk format Redis 7.4 wrote, helm upgrade reports success anyway, and every replica crash-loops until the value is set back. See Redis or Valkey.
  • Create the password secrets before installing. redis.passwordSecretName and sentinel.passwordSecretName name secrets this template does not create; pointing either at a secret that does not exist wedges the deployment waiting on it.
  • Rotating a password needs a forced redeployment. Updating the secret in place changes nothing, and the old password keeps working with every status surface reporting healthy.
  • Every helm upgrade restarts every replica in every location at once, costing a short write outage and a change of master.
  • Two locations give no automatic failover, because the Sentinel vote cannot reach a majority. Use three or more to survive losing a location.
  • With firewall.internalAllowType: workload-list, list only your clients — this release’s own workloads are added for you. Listing only clients used to cut the cluster off from itself while every replica still reported ready.
  • Public access needs a dedicated load balancer you enable on the GVC yourself. This template no longer creates the GVC and so can no longer turn it on; without one, the domain is created and never serves traffic.
  • Never suspend a location. Suspending and resuming one permanently withdraws its endpoints from the other locations’ service discovery while every status surface still reports healthy. To remove a location, take it out of global.locations.
  • Extra locations in the GVC are harmless. A location this release does not declare starts nothing; its deployment reads This workload location is deactivated because maxScale is set to 0.
  • Allow about two minutes after a cold install before believing a replica is unreachable — cross-region service discovery takes that long to converge, and a firewall change can take a further few minutes.
  • Data survives an upgrade but not an uninstallhelm uninstall deletes both volume sets.

External References

Redis Documentation

Official Redis documentation

Redis Sentinel Documentation

Redis Sentinel setup and client configuration

Valkey Documentation

Official Valkey documentation and configuration reference

Redis Persistence

How RDB and AOF files are written and loaded

Backup Image Source

Source code for the Redis backup container image

Redis Multi-Location Template

View the source files, default values, and chart definition