Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
0167337
fix: source filtering stream error
ThibautChoppy Jul 6, 2026
9143e70
chore: add TEI class for reranker
ThibautChoppy Jul 6, 2026
2fd93ba
fix: unit test for router
ThibautChoppy Jul 6, 2026
70e45e1
fix: image repo in upper & dockerhub push
ThibautChoppy Jul 6, 2026
521a2aa
chore: add refacto for helm chart
ThibautChoppy Jul 6, 2026
213d863
fix: general improvement & lot of bug fixed
ThibautChoppy Jul 6, 2026
8ea54b6
fix: template error & add ray dash port to np
ThibautChoppy Jul 7, 2026
f56dac7
fix: ingress simplify & use explicit port name
ThibautChoppy Jul 7, 2026
78864cf
fix: revert past error
ThibautChoppy Jul 7, 2026
4215de7
fix: file permission in admin-ui image
ThibautChoppy Jul 7, 2026
0f1496d
Merge remote-tracking branch 'upstream/refactor/hexagonal' into refac…
ThibautChoppy Jul 7, 2026
cef331d
fix: file permission in admin-ui image
ThibautChoppy Jul 7, 2026
20be538
fix: template error for sa
ThibautChoppy Jul 7, 2026
9561b30
fix: value file
ThibautChoppy Jul 7, 2026
aa616f3
fix: port name referencement
ThibautChoppy Jul 7, 2026
a191dea
Merge branch 'refactor/hexagonal' into feat/helm-improvement
ThibautChoppy Jul 7, 2026
6be6cb5
Merge remote-tracking branch 'upstream/refactor/hexagonal' into refac…
ThibautChoppy Jul 7, 2026
982c22d
Merge branch 'refactor/hexagonal' into feat/helm-improvement
ThibautChoppy Jul 7, 2026
54f8f0f
fix: revert build_rc
ThibautChoppy Jul 7, 2026
ea9afd3
chore: fix posix compatible
ThibautChoppy Jul 7, 2026
80aa44d
fix: all github PR comments
ThibautChoppy Jul 8, 2026
b71c78b
Merge branch 'develop' into feat/helm-improvement
andyne13 Jul 16, 2026
fe915cf
Merge remote-tracking branch 'upstream/develop' into feat/helm-improv…
ThibautChoppy Jul 20, 2026
1fa9785
chore: last uniformization modifications for pr merge
ThibautChoppy Jul 20, 2026
16549fc
chore: fix global value overide
ThibautChoppy Jul 20, 2026
262ae14
chore: values uniformization
ThibautChoppy Jul 20, 2026
16ac948
Merge remote-tracking branch 'upstream/develop' into feat/helm-improv…
ThibautChoppy Jul 21, 2026
a44256e
Merge remote-tracking branch 'upstream/develop' into feat/helm-improv…
ThibautChoppy Jul 22, 2026
cb54874
Merge remote-tracking branch 'upstream/develop' into feat/helm-improv…
ThibautChoppy Jul 23, 2026
98aa2e2
feat(helm): surface MODEL_ENDPOINT_SYNC_ON_BOOT in values.yaml
ThibautChoppy Jul 23, 2026
f5bbd7f
Merge remote-tracking branch 'upstream/develop' into feat/helm-improv…
ThibautChoppy Jul 23, 2026
3d43c60
chore: fix doublon
ThibautChoppy Jul 23, 2026
3918e04
fix(helm): propagate storageClass to sub-charts, correct NOTES.txt --…
ThibautChoppy Jul 23, 2026
9746ba2
Merge remote-tracking branch 'upstream/develop' into feat/helm-improv…
ThibautChoppy Jul 29, 2026
15db9a1
chore: remove graphify of .gitignore
ThibautChoppy Jul 29, 2026
cb93280
fix(helm): correct stale adminUi.ingress routing comment
Ahmath-Gadji Jul 30, 2026
7996d1d
fix(admin-ui): run as 10001:0 so the image works under an arbitrary UID
ThibautChoppy Jul 30, 2026
8c68543
Merge remote-tracking branch 'upstream/develop' into feat/helm-improv…
ThibautChoppy Jul 30, 2026
70f3ab8
fix(helm): give the migration Job the same ServiceAccount as the app
ThibautChoppy Jul 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 28 additions & 3 deletions docs/content/docs/documentation/kubernetes.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ This guide explains how to deploy the **OpenRAG** stack on a Kubernetes cluster

- Copy or create a new `values.yaml` at the root of your repo.
- You can see the full example file inside the chart:
[values.yaml](https://github.com/linagora/openrag/blob/dev/charts/openrag-stack/values.yaml)
[values.yaml](https://github.com/linagora/openrag/blob/dev/infra/charts/openrag-stack/values.yaml)
- Customize the values you need (e.g., image tags, resources, ingress host, storage class, environment variables, secrets).

2. **Set environment and secrets**:
Expand All @@ -36,16 +36,41 @@ This guide explains how to deploy the **OpenRAG** stack on a Kubernetes cluster
helm upgrade\
--install openrag oci://ghcr.io/linagora/openrag-stack\
-f ./values.yaml\
--version 0.1.0
--version 0.6.0
```

- `openrag` is the Helm release name.
- `oci://ghcr.io/linagora/openrag-stack` is the remote chart location.
- `-f ./values.yaml` specifies your custom configuration.
- `--version 0.1.0` ensures you deploy a specific chart version.
- `--version 0.6.0` ensures you deploy a specific chart version — check `Chart.yaml` for the current version before installing.

---

## Upgrading to chart 0.6.0

Chart 0.6.0 renames the PVCs, ConfigMap and Secret from fixed `rag-*` names to
`{{ fullname }}-*`, so they follow the release instead of colliding between two
installs in one namespace. With the default `fullnameOverride: "openrag"`:

| Before | After |
|---|---|
| `rag-model-weights`, `rag-data`, `rag-logs`, `rag-venv` | `openrag-model-weights`, `openrag-data`, `openrag-logs`, `openrag-venv` |
| `rag-env` | `openrag-env` |
| `rag-env-secrets` | `openrag-env-secrets` |

The old PVCs carry `helm.sh/resource-policy: keep`, so **the upgrade does not
delete them — but it does not mount them either**. It provisions new, empty ones
under the new names, and the release comes up as if it had no indexed data. Pick
one before upgrading:

- **Keep the existing volumes.** Set `fullnameOverride: "rag"`, which reproduces
the old names exactly. Also set `postgresql.fullnameOverride`,
`milvus.fullnameOverride` and `vllm.hfTokenSecretName` to match (they are kept
in sync by hand — `values.yaml` explains why, and `NOTES.txt` warns on an
HF_TOKEN secret-name mismatch).
- **Migrate to the new names.** Copy the data across (e.g. a Job mounting both
PVCs), then delete the old ones once the release is healthy.

## Notes

- If using a public IP instead of a hostname, you can leave `ingress.host` empty in your `values.yaml`.
Expand Down
8 changes: 4 additions & 4 deletions infra/charts/openrag-stack/Chart.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ dependencies:
version: 1.4.0
- name: postgresql
repository: https://charts.bitnami.com/bitnami
version: 18.1.13
version: 18.7.3
- name: milvus
repository: https://zilliztech.github.io/milvus-helm/
version: 5.0.0
- name: vllm-stack
repository: https://vllm-project.github.io/production-stack
version: 0.1.8
digest: sha256:838fb1143c5471aaf919c128ece414799898727d083103ac2b669083f50b82c2
generated: "2025-12-02T11:53:42.972907563Z"
version: 0.1.11
digest: sha256:1ea11f53796e3196848d5d46cdff5a6bdeeb012427c1951ea646a22b176adec6
generated: "2026-06-10T14:51:21.005020805+02:00"
37 changes: 17 additions & 20 deletions infra/charts/openrag-stack/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,39 +1,36 @@
apiVersion: v2
name: openrag-stack
description: A Helm chart for Kubernetes

# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application

# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.5.2

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
version: 0.6.0
appVersion: "2.0.1"

maintainers:
- name: linagora
email: openrag@linagora.com
url: https://github.com/linagora/openrag

home: https://github.com/linagora/openrag
sources:
- https://github.com/linagora/openrag

dependencies:
- name: kuberay-operator
version: "1.4.0"
repository: "https://ray-project.github.io/kuberay-helm/"
alias: kuberay
condition: kuberay.enabled
- name: postgresql
version: ">=17.6.0"
version: ">=17.6.0 <19.0.0"
repository: "https://charts.bitnami.com/bitnami"
condition: postgresql.enabled
- name: milvus
version: "5.0.0"
repository: "https://zilliztech.github.io/milvus-helm/"
condition: milvus.enabled
- name: vllm-stack
alias: vllm
version: ">=0.1.7"
version: ">=0.1.7 <1.0.0"
repository: "https://vllm-project.github.io/production-stack"
condition: vllm.enabled
50 changes: 50 additions & 0 deletions infra/charts/openrag-stack/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
OpenRAG stack deployed as release "{{ .Release.Name }}" in namespace "{{ .Release.Namespace }}".

{{- $secretName := include "openrag-stack.secretName" . }}
{{- $configuredVllmSecret := .Values.vllm.hfTokenSecretName }}
{{- if ne $secretName $configuredVllmSecret }}

⚠ vLLM HF_TOKEN secret mismatch — action required
---------------------------------------------------
This release creates the env secret as: {{ $secretName }}
The vLLM sub-chart values still reference: {{ $configuredVllmSecret }}

vLLM serving pods will fail to mount HF_TOKEN unless you fix this.

Careful how you override this: Helm never merges *whole lists* across values
sources, it replaces them — so supplying your own
vllm.servingEngineSpec.modelSpec array (a values file, or a --set that
redefines the entire array) silently drops any of the 4 entries you don't
repeat, breaking those deployments.

An indexed override like
"--set vllm.servingEngineSpec.modelSpec[0].hf_token.secretName=..." is fine —
it only touches that one field of that one entry and leaves the other 3
entries (and the other fields of that entry) untouched. The wholesale-replace
risk above only applies when the *entire* modelSpec array is redefined.

To fix this mismatch, either edit hfTokenSecretName's anchor directly (its
value already flows to all 4 entries), or supply the *complete* vllm.servingEngineSpec.modelSpec
array (copy the 4 entries from values.yaml, keep every field, just change
hf_token.secretName — an anchor keeps all 4 in sync from one line), either:
- directly in values.yaml (vllm.hfTokenSecretName's anchor already does this), or
- in your own values file / ArgoCD Application values block, reusing an
anchor the same way, e.g.:

vllm:
hfTokenSecretName: &hfTokenSecret "{{ $secretName }}"
servingEngineSpec:
modelSpec:
- name: "embedder"
# ...copy every other field from values.yaml unchanged...
hf_token:
secretName: *hfTokenSecret
secretKey: HF_TOKEN
# ...repeat for "whisper", "llm", "vlm"...

{{- end }}

Secrets provider: {{ .Values.env.secretsProvider.type | default "values" }}
{{- if .Values.env.existingSecret }}
Using existing secret: {{ .Values.env.existingSecret }}
{{- end }}
77 changes: 71 additions & 6 deletions infra/charts/openrag-stack/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,81 @@ Selector labels
*/}}
{{- define "openrag-stack.selectorLabels" -}}
app.kubernetes.io/name: {{ include "openrag-stack.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/instance: {{ include "openrag-stack.fullname" . }}
{{- end }}

{{/*
Create the name of the service account to use
Component-scoped selector labels. app.kubernetes.io/name identifies the
specific workload ("openrag", "admin-ui", "reranker", ...) instead of the
umbrella chart name, so e.g. `kubectl get pods -l app.kubernetes.io/name=admin-ui`
targets one component — every workload template should use this (and
"componentLabels" below) instead of hand-rolling its own label block.
Usage: {{ include "openrag-stack.componentSelectorLabels" (dict "component" "openrag" "context" $) }}
*/}}
{{- define "openrag-stack.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "openrag-stack.fullname" .) .Values.serviceAccount.name }}
{{- define "openrag-stack.componentSelectorLabels" -}}
app.kubernetes.io/name: {{ .component }}
app.kubernetes.io/instance: {{ include "openrag-stack.fullname" .context }}
{{- end }}

{{/*
Component-scoped common labels (componentSelectorLabels plus chart/version/managed-by).
Usage: {{ include "openrag-stack.componentLabels" (dict "component" "openrag" "context" $) }}
*/}}
{{- define "openrag-stack.componentLabels" -}}
helm.sh/chart: {{ include "openrag-stack.chart" .context }}
{{ include "openrag-stack.componentSelectorLabels" . }}
{{- if .context.Chart.AppVersion }}
app.kubernetes.io/version: {{ .context.Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .context.Release.Service }}
{{- end }}

{{/*
Environment secret name used by all consumers.
When env.existingSecret is set, that name is returned directly.
*/}}
{{- define "openrag-stack.secretName" -}}
{{- if .Values.env.existingSecret }}
{{- .Values.env.existingSecret }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- printf "%s-env-secrets" (include "openrag-stack.fullname" .) | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}

{{/*
Merge a component's security context override (e.g. just runAsUser/runAsGroup/
fsGroup, tuned to that component's own Dockerfile) on top of a shared default
from values.yaml's top-level `security` block — component keys win on
conflicts, everything else is inherited from the default.
Deliberately NOT Sprig's `merge` (mergo): mergo treats a zero value (false, 0,
"") as "unset" and overwrites it with the default, so an explicit
`allowPrivilegeEscalation: false` override would be silently discarded the
day the shared default becomes `true`. This does a presence-based (hasKey)
shallow merge instead, so an explicitly-set false/0/"" always wins.
Usage: {{ include "openrag-stack.mergeSecurityContext" (dict "component" .Values.ray.podSecurityContext "default" .Values.security.podSecurityContext) }}
*/}}
{{- define "openrag-stack.mergeSecurityContext" -}}
{{- $result := deepCopy .component -}}
{{- range $key, $value := .default -}}
{{- if not (hasKey $result $key) -}}
{{- $_ := set $result $key $value -}}
{{- end -}}
{{- end -}}
{{- $result | toYaml -}}
{{- end }}

{{/*
Component override for a single boolean/scalar security field (e.g.
automountServiceAccountToken): uses the component's own value only if the key
is explicitly present, otherwise falls back to the shared default. A plain
`| default` would treat an explicit `false` override as empty and silently
fall back anyway — this checks presence (hasKey) instead of truthiness.
Usage: {{ include "openrag-stack.securityFieldOverride" (dict "component" .Values.ray "key" "automountServiceAccountToken" "default" .Values.security.automountServiceAccountToken) }}
*/}}
{{- define "openrag-stack.securityFieldOverride" -}}
{{- if hasKey .component .key -}}
{{- get .component .key -}}
{{- else -}}
{{- .default -}}
{{- end -}}
{{- end }}
61 changes: 31 additions & 30 deletions infra/charts/openrag-stack/templates/admin-ui.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,65 +2,66 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ .Release.Name }}-admin-ui
name: {{ include "openrag-stack.fullname" . }}-admin-ui
labels:
app.kubernetes.io/name: admin-ui
app.kubernetes.io/instance: {{ .Release.Name }}
{{- include "openrag-stack.componentLabels" (dict "component" "admin-ui" "context" $) | nindent 4 }}
spec:
replicas: {{ .Values.adminUi.replicaCount }}
selector:
matchLabels:
app.kubernetes.io/name: admin-ui
app.kubernetes.io/instance: {{ .Release.Name }}
{{- include "openrag-stack.componentSelectorLabels" (dict "component" "admin-ui" "context" $) | nindent 6 }}
template:
metadata:
labels:
app.kubernetes.io/name: admin-ui
app.kubernetes.io/instance: {{ .Release.Name }}
{{- include "openrag-stack.componentSelectorLabels" (dict "component" "admin-ui" "context" $) | nindent 8 }}
spec:
automountServiceAccountToken: {{ .Values.security.automountServiceAccountToken }}
securityContext:
{{- toYaml .Values.security.podSecurityContext | nindent 8 }}
{{- include "openrag-stack.mergeSecurityContext" (dict "component" .Values.adminUi.podSecurityContext "default" .Values.security.podSecurityContext) | nindent 8 }}
{{- with .Values.adminUi.serviceAccountName }}
serviceAccountName: {{ tpl . $ }}
{{- end }}
automountServiceAccountToken: {{ include "openrag-stack.securityFieldOverride" (dict "component" .Values.adminUi "key" "automountServiceAccountToken" "default" .Values.security.automountServiceAccountToken) }}
containers:
- name: admin-ui
image: "{{ .Values.adminUi.image.repository }}:{{ .Values.adminUi.image.tag }}"
imagePullPolicy: {{ .Values.adminUi.imagePullPolicy }}
# nginx-unprivileged listens on :8080 as a non-root user, so it runs
# under the same hardened context (runAsNonRoot, drop ALL) as the API.
image: "{{ .Values.adminUi.image.registry | default .Values.global.image.registry }}/{{ .Values.adminUi.image.repository }}:{{ .Values.adminUi.image.tag }}"
imagePullPolicy: {{ .Values.adminUi.image.pullPolicy }}
securityContext:
{{- toYaml .Values.security.containerSecurityContext | nindent 12 }}
{{- include "openrag-stack.mergeSecurityContext" (dict "component" (.Values.adminUi.containerSecurityContext | default dict) "default" .Values.security.containerSecurityContext) | nindent 12 }}
ports:
- containerPort: {{ .Values.adminUi.service.targetPort }}
name: admin-ui
# nginx serves the SPA at /app/; probe it so a crash-looping or stuck
# pod is restarted and gated out of rollout traffic.
{{- if .Values.adminUi.probes.startup }}
Comment thread
ThibautChoppy marked this conversation as resolved.
startupProbe:
{{- toYaml .Values.adminUi.probes.startup | nindent 12 }}
{{- end }}
{{- if .Values.adminUi.probes.readiness }}
readinessProbe:
httpGet:
path: /app/
port: {{ .Values.adminUi.service.targetPort }}
initialDelaySeconds: 5
periodSeconds: 10
{{- toYaml .Values.adminUi.probes.readiness | nindent 12 }}
{{- end }}
{{- if .Values.adminUi.probes.liveness }}
livenessProbe:
httpGet:
path: /app/
port: {{ .Values.adminUi.service.targetPort }}
initialDelaySeconds: 10
periodSeconds: 20
{{- toYaml .Values.adminUi.probes.liveness | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.adminUi.resources | nindent 12 }}
---
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-admin-ui
name: {{ include "openrag-stack.fullname" . }}-admin-ui
labels:
app.kubernetes.io/name: admin-ui
app.kubernetes.io/instance: {{ .Release.Name }}
{{- include "openrag-stack.componentLabels" (dict "component" "admin-ui" "context" $) | nindent 4 }}
spec:
type: {{ .Values.adminUi.service.type }}
selector:
app.kubernetes.io/name: admin-ui
app.kubernetes.io/instance: {{ .Release.Name }}
{{- include "openrag-stack.componentSelectorLabels" (dict "component" "admin-ui" "context" $) | nindent 4 }}
ports:
- port: {{ .Values.adminUi.service.port }}
- name: admin-ui
port: {{ .Values.adminUi.service.port }}
targetPort: {{ .Values.adminUi.service.targetPort }}
{{- if eq .Values.adminUi.service.type "NodePort" }}
nodePort: {{ .Values.adminUi.service.nodePort }}
{{- end }}
{{- end }}
19 changes: 18 additions & 1 deletion infra/charts/openrag-stack/templates/configmap-env.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: rag-env
name: {{ include "openrag-stack.fullname" . }}-env
data:
{{- range $key, $value := .Values.env.config }}
{{ $key }}: "{{ tpl (printf "%v" $value) $ }}"
{{- end }}
{{- if .Values.proxy.enabled }}
{{- if or (empty .Values.proxy.url) (eq .Values.proxy.url "http://192.168.100.100:80") }}
{{- fail "proxy.enabled is true but proxy.url is not set or still uses the placeholder default. Set a valid proxy URL in proxy.url." }}
{{- end }}
{{- $proxyKeys := list "HTTP_PROXY" "HTTPS_PROXY" "http_proxy" "https_proxy" "NO_PROXY" "no_proxy" }}
{{- range $proxyKeys }}
{{- if hasKey $.Values.env.config . }}
{{- fail (printf "proxy.enabled is true but env.config already defines %q — remove it from env.config to avoid a duplicate ConfigMap key." .) }}
{{- end }}
{{- end }}
HTTP_PROXY: {{ .Values.proxy.url | quote }}
HTTPS_PROXY: {{ .Values.proxy.url | quote }}
http_proxy: {{ .Values.proxy.url | quote }}
https_proxy: {{ .Values.proxy.url | quote }}
NO_PROXY: {{ .Values.proxy.noProxy | quote }}
no_proxy: {{ .Values.proxy.noProxy | quote }}
{{- end }}
4 changes: 4 additions & 0 deletions infra/charts/openrag-stack/templates/extra-objects.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{{- range .Values.extraObjects }}
---
{{ tpl (toYaml .) $ }}
{{- end }}
Loading
Loading