Skip to content

Commit d2f7e2c

Browse files
committed
fix(helm): deliver bundled REDIS_URL via ConfigMap so an operator value always wins
Injecting REDIS_URL as an inline container env made it beat every envFrom source, so a REDIS_URL held in a pre-created Secret or synced by External Secrets was silently shadowed and traffic moved to a fresh in-cluster Redis. Kubernetes resolves duplicate envFrom keys by letting the last source win, so the bundled URL now ships as a ConfigMap listed before the app Secret. Any operator-supplied value overrides it without the chart needing to read it, which also removes the redis.provideUrl flag the previous attempt required.
1 parent 58bd1ba commit d2f7e2c

10 files changed

Lines changed: 119 additions & 184 deletions

File tree

apps/docs/content/docs/en/platform/self-hosting/redis.mdx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,9 @@ app:
5858
REDIS_URL: "rediss://:<password>@my-cache.internal:6380"
5959
```
6060
61-
`app.env.REDIS_URL` takes over whenever it is set, and the chart skips the bundled instance so you do not get a stray pod.
61+
`app.env.REDIS_URL` takes over whenever it is set, and the chart skips the bundled Deployment so you do not get a stray pod.
6262

63-
If the URL lives in a secret store rather than in values:
64-
65-
- **External Secrets** — map `externalSecrets.remoteRefs.app.REDIS_URL`. The chart detects the mapping and steps aside automatically.
66-
- **Pre-created Secret** (`app.secrets.existingSecret`) — set `redis.provideUrl: false`. The chart cannot read your Secret, so without this it would inject a computed `REDIS_URL` that takes precedence over `envFrom` and shadows your value.
63+
If the URL lives in a secret store instead — a pre-created Secret or one synced by External Secrets — it also wins, and there is nothing extra to configure. The bundled URL is delivered as a ConfigMap listed before the app Secret in `envFrom`, and Kubernetes lets the last source win for duplicate keys, so your value overrides it without the chart ever reading it.
6764

6865
The bundled Redis is deliberately non-persistent (`--save ""`, `--appendonly no`) with a 512 MB cap: Sim stores coordination state and short-lived keys in it, so a restart costs in-flight live updates rather than committed data.
6966

helm/sim/README.md

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -473,17 +473,11 @@ Two changes alter behavior on an existing release. Neither requires action, but
473473

474474
* **Free-tier plan limits are no longer preset.** `app.envDefaults` previously shipped `RATE_LIMIT_FREE_SYNC`, `RATE_LIMIT_FREE_ASYNC`, `EXECUTION_TIMEOUT_FREE`, `EXECUTION_TIMEOUT_ASYNC_FREE`, `FREE_TABLES_LIMIT: 3`, and `FREE_TABLE_ROWS_LIMIT: 1000`. With billing disabled the application treats these as **opt-in** — unset means unlimited — so presetting them imposed hosted-plan caps on self-hosted deployments and diverged from Docker Compose, which presets nothing. They are now commented out. **On upgrade, these limits stop being enforced.** To keep them, set the keys explicitly under `app.env`. An explicitly set value has always taken precedence and is unaffected.
475475

476-
* **Redis is now bundled** (`redis.enabled: true`), matching the Docker Compose stack. Redis backs pub/sub and the Socket.IO adapter, and multi-replica deployments silently drop cross-pod events without it. The chart steps aside whenever it can tell you are supplying `REDIS_URL` yourself, so no upgrade reroutes an existing instance:
476+
* **Redis is now bundled** (`redis.enabled: true`), matching the Docker Compose stack. Redis backs pub/sub and the Socket.IO adapter, and multi-replica deployments silently drop cross-pod events without it.
477477

478-
| Your configuration | Bundled Redis | `REDIS_URL` |
479-
|---|---|---|
480-
| Default install | Deployed | Points at the bundled instance |
481-
| `app.env.REDIS_URL` set | Not deployed | Your value |
482-
| `externalSecrets.remoteRefs.app.REDIS_URL` mapped | Not deployed | Synced by ESO — detected automatically |
483-
| `redis.provideUrl: false` | Not deployed | Whatever your pre-created Secret contains |
484-
| `redis.enabled: false` | Not deployed | Unset unless you provide it |
478+
**An existing `REDIS_URL` always wins, wherever it comes from — no action needed on upgrade.** The bundled URL ships as a ConfigMap listed *before* the app Secret in `envFrom`. Kubernetes resolves duplicate keys by letting the last source win, so a `REDIS_URL` in your chart-managed Secret, a pre-created `existingSecret`, or one synced by External Secrets overrides the bundled value — the chart never has to read it. The bundled Redis simply fills the gap when nothing else provides a URL.
485479

486-
**If you use `app.secrets.existingSecret` and that Secret already contains `REDIS_URL`, set `redis.provideUrl: false`.** The chart cannot read a pre-created Secret, so it would otherwise inject a computed `REDIS_URL` as a container `env` entry, which takes precedence over `envFrom` and would shadow your value. External Secrets needs no such flag — mapping `remoteRefs.app.REDIS_URL` is detected on its own.
480+
Set `app.env.REDIS_URL` to skip the bundled Deployment entirely (no unused pod), or `redis.enabled: false` to opt out.
487481

488482
## Upgrading to 1.2.0
489483

helm/sim/examples/values-existing-secret.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,5 @@ postgresql:
7070
# --namespace sim \
7171
# --from-literal=POSTGRES_PASSWORD="$(openssl rand -base64 16 | tr -d '/+=')"
7272

73-
# Redis: the chart bundles one by default and wires REDIS_URL to it. If the
74-
# Secret referenced above already contains REDIS_URL, set redis.provideUrl=false
75-
# so the chart does not shadow it.
73+
# Redis: bundled by default. If the Secret above already contains REDIS_URL,
74+
# it overrides the bundled value automatically — nothing to configure.

helm/sim/examples/values-external-secrets.yaml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,5 @@ postgresql:
125125
# mountPath: "kubernetes"
126126
# role: "external-secrets"
127127

128-
# Redis: the chart bundles one by default and wires REDIS_URL to it. To use a
129-
# managed Redis instead, map externalSecrets.remoteRefs.app.REDIS_URL — the
130-
# chart detects that and steps aside.
128+
# Redis: bundled by default. Map externalSecrets.remoteRefs.app.REDIS_URL to
129+
# use a managed instance instead; the synced value overrides the bundled one.

helm/sim/templates/_helpers.tpl

Lines changed: 8 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -465,54 +465,21 @@ PII (Presidio) service URL
465465
{{- end }}
466466

467467
{{/*
468-
Whether the operator is supplying REDIS_URL themselves.
469-
470-
Three detectable signals:
471-
- app.env.REDIS_URL set explicitly
472-
- externalSecrets.remoteRefs.app.REDIS_URL mapped (ESO syncs it into the Secret)
473-
- redis.provideUrl=false, the opt-out for a pre-created Secret that already
474-
contains REDIS_URL, which the chart cannot read at render time
475-
476-
When any holds, the chart neither deploys Redis nor emits a computed REDIS_URL —
477-
a container `env` entry beats `envFrom`, so emitting one would silently shadow
478-
the operator's value and reroute a managed Redis to an empty in-cluster instance.
479-
*/}}
480-
{{- define "sim.redisUrlSuppliedByOperator" -}}
481-
{{- $esoRef := "" -}}
482-
{{- if .Values.externalSecrets.enabled -}}
483-
{{- $esoRef = dig "remoteRefs" "app" "REDIS_URL" "" .Values.externalSecrets -}}
484-
{{- end -}}
485-
{{- if or (.Values.app.env.REDIS_URL | default "") $esoRef (not .Values.redis.provideUrl) -}}
486-
true
487-
{{- end -}}
488-
{{- end }}
468+
Whether the chart owns Redis for this release.
489469
490-
{{/*
491-
Whether the chart owns Redis for this release: enabled, and the operator is not
492-
supplying a URL themselves. Secret-manager modes alone do NOT suppress it —
493-
doing so left those deployments with no Redis at all, since REDIS_URL is optional
494-
and the shipped examples omit it.
470+
False only when the operator points app.env.REDIS_URL at their own instance —
471+
then deploying a bundled one would leave an unused pod. Secret-manager modes do
472+
NOT suppress it: the bundled URL ships as a ConfigMap listed before the app
473+
Secret in envFrom, so any operator-supplied REDIS_URL (chart Secret, pre-created
474+
Secret, or ESO-synced) overrides it without the chart needing to see the value.
475+
See templates/configmap-redis.yaml.
495476
*/}}
496477
{{- define "sim.chartManagesRedis" -}}
497-
{{- if and .Values.redis.enabled (not (include "sim.redisUrlSuppliedByOperator" .)) -}}
478+
{{- if and .Values.redis.enabled (not (.Values.app.env.REDIS_URL | default "")) -}}
498479
true
499480
{{- end -}}
500481
{{- end }}
501482

502-
{{/*
503-
Redis URL emitted as a chart-computed container env. Only set when the chart owns
504-
Redis, or when app.env.REDIS_URL is given explicitly. Empty otherwise, so a
505-
Secret- or ESO-supplied value flows through envFrom untouched.
506-
*/}}
507-
{{- define "sim.redisUrl" -}}
508-
{{- $external := .Values.app.env.REDIS_URL | default "" -}}
509-
{{- if $external -}}
510-
{{- $external -}}
511-
{{- else if (include "sim.chartManagesRedis" .) -}}
512-
{{- printf "redis://%s-redis:6379" (include "sim.fullname" .) -}}
513-
{{- end -}}
514-
{{- end }}
515-
516483
{{/*
517484
Socket Server URL (internal)
518485
*/}}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{{- if include "sim.chartManagesRedis" . }}
2+
{{- /*
3+
REDIS_URL for the bundled Redis, delivered as a ConfigMap rather than an
4+
inline container `env` entry.
5+
6+
This is deliberate. Kubernetes resolves duplicate keys by letting the LAST
7+
envFrom source win, while an inline `env` entry beats every envFrom source.
8+
The app and realtime Deployments list this ConfigMap FIRST, before the app
9+
Secret, so:
10+
11+
- an operator-supplied REDIS_URL (chart Secret, pre-created Secret, or one
12+
synced by External Secrets) always wins, because it is listed later
13+
- the bundled URL applies only when nothing else provides one
14+
15+
An inline `env` entry would invert that and silently shadow a managed Redis
16+
the chart cannot see, which is why REDIS_URL is not chart-computed like
17+
DATABASE_URL and SOCKET_SERVER_URL are.
18+
19+
The value is a plain in-cluster service address with no credentials, so a
20+
ConfigMap is the right resource here.
21+
*/}}
22+
apiVersion: v1
23+
kind: ConfigMap
24+
metadata:
25+
name: {{ include "sim.fullname" . }}-redis
26+
namespace: {{ .Release.Namespace }}
27+
labels:
28+
{{- include "sim.labels" . | nindent 4 }}
29+
app.kubernetes.io/component: redis
30+
data:
31+
REDIS_URL: {{ printf "redis://%s-redis:6379" (include "sim.fullname" .) | quote }}
32+
{{- end }}

helm/sim/templates/deployment-app.yaml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,6 @@ spec:
9191
value: {{ include "sim.ollamaUrl" . | quote }}
9292
- name: PII_URL
9393
value: {{ include "sim.piiUrl" . | quote }}
94-
{{- with (include "sim.redisUrl" .) }}
95-
- name: REDIS_URL
96-
value: {{ . | quote }}
97-
{{- end }}
9894
{{- /*
9995
Skip envDefaults keys that the user has explicitly overridden in app.env
10096
with a non-empty value. K8s `env` takes precedence over `envFrom`, so an
@@ -123,7 +119,7 @@ spec:
123119
and in inline mode (values flow through the chart-managed Secret).
124120
*/}}
125121
{{- if and .Values.app.secrets.existingSecret.enabled (not .Values.externalSecrets.enabled) }}
126-
{{- $chartComputed := list "DATABASE_URL" "SOCKET_SERVER_URL" "OLLAMA_URL" "PII_URL" "REDIS_URL" }}
122+
{{- $chartComputed := list "DATABASE_URL" "SOCKET_SERVER_URL" "OLLAMA_URL" "PII_URL" }}
127123
{{- range $key, $value := $appEnv }}
128124
{{- if and (ne (toString $value) "") (ne (toString $value) "<nil>") (not (has $key $chartComputed)) }}
129125
- name: {{ $key }}
@@ -147,6 +143,12 @@ spec:
147143
{{- toYaml . | nindent 12 }}
148144
{{- end }}
149145
envFrom:
146+
# Bundled Redis URL. Listed FIRST so any operator-supplied REDIS_URL
147+
# in a later source overrides it — see templates/configmap-redis.yaml.
148+
{{- if include "sim.chartManagesRedis" . }}
149+
- configMapRef:
150+
name: {{ include "sim.fullname" . }}-redis
151+
{{- end }}
150152
# App secrets (authentication, encryption keys)
151153
- secretRef:
152154
name: {{ include "sim.appSecretName" . }}

helm/sim/templates/deployment-realtime.yaml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,6 @@ spec:
6262
env:
6363
- name: DATABASE_URL
6464
value: {{ include "sim.databaseUrl" . | quote }}
65-
{{- with (include "sim.redisUrl" .) }}
66-
- name: REDIS_URL
67-
value: {{ . | quote }}
68-
{{- end }}
6965
{{- if .Values.telemetry.enabled }}
7066
{{- $nodeEnv := default (default "production" (index (.Values.realtime.envDefaults | default dict) "NODE_ENV")) (index (.Values.realtime.env | default dict) "NODE_ENV") }}
7167
# OpenTelemetry configuration
@@ -116,7 +112,7 @@ spec:
116112
deployment.
117113
*/}}
118114
{{- if and .Values.app.secrets.existingSecret.enabled (not .Values.externalSecrets.enabled) }}
119-
{{- $chartComputed := list "DATABASE_URL" "SOCKET_SERVER_URL" "OLLAMA_URL" "PII_URL" "REDIS_URL" }}
115+
{{- $chartComputed := list "DATABASE_URL" "SOCKET_SERVER_URL" "OLLAMA_URL" "PII_URL" }}
120116
{{- /*
121117
Build the effective realtime env from app.env as the base, then
122118
overlay non-empty realtime.env values. Sprig's `merge` keeps the
@@ -141,6 +137,12 @@ spec:
141137
{{- toYaml . | nindent 12 }}
142138
{{- end }}
143139
envFrom:
140+
# Bundled Redis URL. Listed FIRST so any operator-supplied REDIS_URL
141+
# in a later source overrides it — see templates/configmap-redis.yaml.
142+
{{- if include "sim.chartManagesRedis" . }}
143+
- configMapRef:
144+
name: {{ include "sim.fullname" . }}-redis
145+
{{- end }}
144146
# App secrets (authentication keys shared with main app)
145147
- secretRef:
146148
name: {{ include "sim.appSecretName" . }}

0 commit comments

Comments
 (0)