Documentation / valkey
TLS and authentication
This guide covers how TLS and ACL bootstrap work in the chart, how clients should connect, and how to integrate with external CAs or Secret stores.
TLS — what gets encrypted
When tls.enabled: true, the chart wires TLS into every connection ValKey makes:
| Connection | Encrypted when tls.enabled |
Config rendered |
|---|---|---|
| Client → ValKey | yes | tls-port, tls-cert-file, tls-key-file, tls-ca-cert-file |
| Replica → Primary (replication stream) | yes | tls-replication yes |
| Sentinel ↔ Sentinel / Sentinel → ValKey | yes | tls-port on the Sentinel sidecar |
| Cluster bus (gossip) | yes (cluster mode) | tls-cluster yes |
requireSecureTransport: true (default when TLS is on) sets port 0 and binds only the TLS port — plaintext is rejected. Drop to false if you have a migration window with mixed clients.
Certificate generation
The chart generates a self-signed CA + server certificate inline using Helm’s genCA and genSignedCert helpers. The certificate is stored in a kubernetes.io/tls Secret, and the CA is additionally published as a ConfigMap (<release>-tls-ca) for client Pods to mount.
The cert covers every DNS name ValKey might be addressed by:
<release>and<release>.<ns>(primary Service)<release>.<ns>.svc.<clusterDomain>(FQDN —clusterDomaindefaults tocluster.local)<release>-headlessand its FQDN*.<release>-headless.<ns>.svc.<clusterDomain>(every Pod)<release>-readonly(when sentinel/cluster)<release>-sentinel(sentinel mode)<release>-shard-<N>-headless(cluster mode, per shard)127.0.0.1andlocalhost
Validity defaults to 3650 days (10 years) for the CA. On helm upgrade, the chart looks up the existing Secret and reuses the same material — certificates are never silently rotated.
Using an external CA
Set tls.existingSecret to point at a Secret of type kubernetes.io/tls containing ca.crt, tls.crt, tls.key:
tls:
enabled: true
existingSecret: my-cert-manager-issued-cert
This is the integration path for cert-manager. Create a Certificate resource and reference its Secret name. The chart honours external certs verbatim — rotation is the cert-manager controller’s responsibility.
Mutual TLS (mTLS)
tls:
enabled: true
authClients: true
authClients: true enables tls-auth-clients yes on ValKey, which requires client certificates signed by the same CA. Clients must present a valid cert signed by the CA in the TLS Secret (or a CA chain compatible with it). Pair with AUTH off when the cert is sufficient identity, or with AUTH on for defence-in-depth.
Client recipes
Mount the CA on a client Pod
spec:
containers:
- name: my-app
volumeMounts:
- name: valkey-ca
mountPath: /etc/valkey-tls
readOnly: true
env:
- name: VALKEY_CA
value: /etc/valkey-tls/ca.crt
volumes:
- name: valkey-ca
configMap:
name: vk-valkey-tls-ca
valkey-cli
valkey-cli --tls \
--cacert /etc/valkey-tls/ca.crt \
-h vk-valkey.app.svc.cluster.local \
-p 6379 \
-a "$VALKEY_PASSWORD" \
PING
Python (valkey-py)
import valkey
r = valkey.Valkey(
host="vk-valkey.app.svc.cluster.local",
port=6379,
ssl=True,
ssl_ca_certs="/etc/valkey-tls/ca.crt",
username="default",
password=os.environ["VALKEY_PASSWORD"],
)
Node.js (ioredis)
import fs from "node:fs";
import Redis from "ioredis";
const r = new Redis({
host: "vk-valkey.app.svc.cluster.local",
port: 6379,
username: "default",
password: process.env.VALKEY_PASSWORD,
tls: { ca: fs.readFileSync("/etc/valkey-tls/ca.crt") },
});
PHP (predis/predis)
$client = new Predis\Client([
'scheme' => 'tls',
'host' => 'vk-valkey.app.svc.cluster.local',
'port' => 6379,
'username' => 'default',
'password' => getenv('VALKEY_PASSWORD'),
'ssl' => ['cafile' => '/etc/valkey-tls/ca.crt'],
]);
Sentinel-aware client (Node.js)
const r = new Redis({
sentinels: [
{ host: "vk-valkey-sentinel.app.svc.cluster.local", port: 26379 },
],
name: "mymaster",
username: "default",
password: process.env.VALKEY_PASSWORD,
tls: { ca: fs.readFileSync("/etc/valkey-tls/ca.crt") },
sentinelTLS: { ca: fs.readFileSync("/etc/valkey-tls/ca.crt") },
});
Cluster-aware client (Python)
from valkey.cluster import ValkeyCluster
r = ValkeyCluster(
startup_nodes=[
{"host": "vk-valkey.app.svc.cluster.local", "port": 6379},
],
ssl=True,
ssl_ca_certs="/etc/valkey-tls/ca.crt",
username="default",
password=os.environ["VALKEY_PASSWORD"],
)
Authentication
When auth.enabled: false, ValKey listens on the data port with protected-mode no and accepts any connection from the namespace. The chart relies on NetworkPolicy to limit who can reach the port.
When auth.enabled: true, the chart:
- Renders an ACL file from
auth.users(plus the default user, set toauth.password). - Stores the ACL file in the chart-managed Secret under
users.acl. - Stores each user’s password in the same Secret under
user-<name>-password. - Mounts the ACL file at
/etc/valkey/acl/users.acland instructs ValKey viaaclfile.
Passwords are looked up on helm upgrade and preserved — re-running the chart never rotates them.
ACL rules cheat-sheet
| Goal | Rule string |
|---|---|
| Full access | on ~* &* +@all |
| Read-only on all keys | on ~* +@read |
| Read-write on a key prefix | on ~app:* +@read +@write +@string |
| Pub/sub on one channel | on resetkeys ~nothing &events.* +@pubsub |
| Lock out a user | off |
See https://valkey.io/topics/acl for the full grammar.
Bootstrap recipe — multi-user
auth:
enabled: true
password: "" # auto-generated
users:
- name: app
password: ""
rules: "on ~app:* &app.* +@all -@dangerous"
- name: readonly
password: ""
rules: "on ~* +@read +@connection"
- name: metrics
password: ""
rules: "on ~* +CLIENT +PING +INFO +CLUSTER|INFO +CLUSTER|NODES"
After install:
kubectl get secret vk-valkey -n app -o json \
| jq -r '.data | with_entries(.value |= @base64d) | {password, "user-app-password", "user-readonly-password", "user-metrics-password"}'
Using an externally-managed Secret
auth:
enabled: true
existingSecret: my-existing-acl-secret
users:
- name: app
rules: "on ~* +@all"
The Secret must contain:
password— the default-user passworduser-<name>-passwordfor each entry inauth.usersusers.acl— the rendered ACL file content (the chart will not render its own ACL file whenexistingSecretis set; the file must already exist in the Secret)
This integration path is useful when ACL passwords come from an external secret store (Vault, AWS Secrets Manager, etc.) via External Secrets Operator.