-
Notifications
You must be signed in to change notification settings - Fork 213
feat: add Helm chart for Kubernetes deployment #1100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
69c3390
e1bd2e4
a83ca00
4cd177b
b04b481
34b47ee
bc4e4e7
23f2896
b2be4f7
7bdf0b0
d1074ea
3a710af
f410580
fe11323
6e66c61
6dc858b
75bf2ff
ab53f42
bd1c2dd
dbf9439
03c18bf
3c1b52c
c085455
8662e7d
7a559f4
cca7931
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| # Patterns to ignore when building packages. | ||
| .DS_Store | ||
| .git/ | ||
| .gitignore | ||
| .vscode/ | ||
| .idea/ | ||
| *.swp | ||
| *.bak | ||
| *.tmp |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| dependencies: | ||
| - name: postgresql | ||
| repository: https://charts.bitnami.com/bitnami | ||
| version: 16.7.14 | ||
| digest: sha256:3d12513cb378249e854df4b9edc49e63c5ab55842efdcc1b3ca6512b371ac811 | ||
| generated: "2026-04-28T16:34:47.673475258+02:00" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| apiVersion: v2 | ||
| name: nao | ||
| description: Helm chart for nao — an open-source analytics agent that transforms natural language into SQL queries. | ||
| type: application | ||
| version: 0.1.0 | ||
| appVersion: "latest" | ||
|
|
||
| keywords: | ||
| - analytics | ||
| - ai | ||
| - sql | ||
| - text-to-sql | ||
| - agent | ||
|
|
||
| home: https://github.com/mfournioux/nao | ||
| sources: | ||
| - https://github.com/mfournioux/nao | ||
|
|
||
| maintainers: | ||
| - name: Maxime FOURNIOUX | ||
| url: https://github.com/mfournioux/nao | ||
|
|
||
| kubeVersion: ">=1.24.0" | ||
|
|
||
| dependencies: | ||
| - name: postgresql | ||
| version: "16.7.14" | ||
| repository: "https://charts.bitnami.com/bitnami" | ||
| condition: postgresql.enabled | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,203 @@ | ||
| # nao Helm Chart | ||
|
|
||
| Helm chart for deploying nao — an open-source analytics agent that transforms natural language into SQL queries — on Kubernetes. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - (Optional) A running PostgreSQL instance, or enable the bundled bitnami/postgresql subchart | ||
|
|
||
| ## Chart structure | ||
|
|
||
| ``` | ||
| helm/ | ||
| ├── Chart.yaml # Chart metadata & bitnami/postgresql dependency | ||
| ├── values.yaml # Default configuration values | ||
| ├── .helmignore | ||
| └── templates/ | ||
| ├── NOTES.txt # Post-install instructions | ||
| ├── _helpers.tpl # Template helpers | ||
| ├── configmap.yaml # Non-sensitive environment variables | ||
| ├── secret.yaml # Sensitive values (API keys, auth secret, DB URI) | ||
| ├── serviceaccount.yaml | ||
| ├── deployment.yaml # Main nao workload | ||
| ├── service.yaml | ||
| ├── pvc.yaml # Conditional (local / api context modes) | ||
| ├── hpa.yaml # Conditional | ||
| ├── pdb.yaml # Conditional | ||
| └── tests/ | ||
| └── test-connection.yaml # helm test pod | ||
| ``` | ||
|
|
||
| ## Installation | ||
|
|
||
| ### 1. Add the bitnami repository (for the PostgreSQL dependency) | ||
|
|
||
| ```bash | ||
| helm repo add bitnami https://charts.bitnami.com/bitnami | ||
| helm repo update | ||
| ``` | ||
|
|
||
| ### 2. Install dependencies | ||
|
|
||
| ```bash | ||
| helm dependency update ./helm | ||
| ``` | ||
|
|
||
| ### 3. Install the chart | ||
|
|
||
| ```bash | ||
| helm install nao ./helm \ | ||
| --namespace nao --create-namespace \ | ||
| --set secrets.betterAuthSecret="$(openssl rand -base64 32)" \ | ||
| --set secrets.openaiApiKey="" | ||
| ``` | ||
|
|
||
| ## Context modes | ||
|
|
||
| nao supports three ways to load its project configuration, controlled by `config.contextSource`. | ||
|
|
||
| ### `local` (default) | ||
|
|
||
| The nao project directory is mounted as a Kubernetes PersistentVolume. | ||
|
|
||
| ```yaml | ||
| # values-local.yaml | ||
| config: | ||
| contextSource: local | ||
| contextPath: /app/project | ||
|
|
||
| persistence: | ||
| enabled: true | ||
| size: 1Gi | ||
| # existingClaim: my-nao-context-pvc # use an existing PVC | ||
| ``` | ||
|
|
||
| > **Note:** You must pre-populate the PVC with a valid nao project (a directory containing `nao_config.yaml`). | ||
|
|
||
| ### `git` | ||
|
|
||
| The project is cloned from a Git repository at pod startup. | ||
|
|
||
| ```yaml | ||
| # values-git.yaml | ||
| config: | ||
| contextSource: git | ||
| contextPath: /app/project | ||
| contextGitUrl: https://github.com/your-org/your-nao-project.git | ||
| contextGitBranch: main | ||
| refreshSchedule: "0 * * * *" # optional: pull every hour | ||
|
|
||
| secrets: | ||
| contextGitToken: ghp_... # required for private repositories | ||
| ``` | ||
|
|
||
| ### `api` | ||
|
|
||
| Projects are deployed dynamically via the `nao deploy` CLI. A writable volume is created at `/app/projects`. | ||
|
|
||
| ```yaml | ||
| # values-api.yaml | ||
| config: | ||
| contextSource: api | ||
|
|
||
| projectsPersistence: | ||
| enabled: true | ||
| size: 5Gi | ||
| ``` | ||
|
|
||
| ## Values reference | ||
|
|
||
| | Key | Default | Description | | ||
| |-----|---------|-------------| | ||
| | `replicaCount` | `1` | Number of pod replicas | | ||
| | `image.repository` | `ghcr.io/getnao/nao` | Container image repository | | ||
| | `image.tag` | `""` (→ appVersion) | Image tag | | ||
| | `image.pullPolicy` | `IfNotPresent` | Image pull policy | | ||
| | `config.serverPort` | `"5005"` | Fastify backend listening port | | ||
| | `config.betterAuthUrl` | `"http://localhost:5005"` | Public URL for auth callbacks | | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Inconsistent Prompt for AI agents |
||
| | `config.contextSource` | `"local"` | Context mode: `local` \| `git` \| `api` | | ||
| | `config.contextPath` | `"/app/project"` | Mount path for the nao project | | ||
| | `config.contextGitUrl` | `""` | Git repo URL (git mode) | | ||
| | `config.contextGitBranch` | `"main"` | Git branch to clone (git mode) | | ||
| | `config.refreshSchedule` | `""` | Cron for periodic git pull | | ||
| | `secrets.betterAuthSecret` | `""` | **Required.** Auth session secret | | ||
| | `secrets.openaiApiKey` | `""` | OpenAI API key | | ||
| | `secrets.anthropicApiKey` | `""` | Anthropic API key | | ||
| | `secrets.dbUri` | `""` | Database URI (overridden when `postgresql.enabled=true`) | | ||
| | `secrets.contextGitToken` | `""` | Git token for private repos | | ||
|
cubic-dev-ai[bot] marked this conversation as resolved.
|
||
| | `service.type` | `ClusterIP` | Kubernetes service type | | ||
| | `service.port` | `80` | Service port | | ||
| | `resources.requests.cpu` | `500m` | CPU request | | ||
| | `resources.requests.memory` | `512Mi` | Memory request | | ||
| | `resources.limits.cpu` | `2` | CPU limit | | ||
| | `resources.limits.memory` | `2Gi` | Memory limit | | ||
| | `autoscaling.enabled` | `false` | Enable HPA | | ||
| | `autoscaling.minReplicas` | `1` | Minimum replicas | | ||
| | `autoscaling.maxReplicas` | `5` | Maximum replicas | | ||
| | `podDisruptionBudget.enabled` | `false` | Enable PDB | | ||
| | `persistence.enabled` | `false` | Enable context PVC (local mode) | | ||
| | `persistence.size` | `1Gi` | Context PVC size | | ||
| | `persistence.existingClaim` | `""` | Use an existing PVC | | ||
| | `projectsPersistence.enabled` | `false` | Enable projects PVC (api mode) | | ||
| | `projectsPersistence.size` | `5Gi` | Projects PVC size | | ||
| | `postgresql.enabled` | `true` | Deploy bundled PostgreSQL | | ||
| | `postgresql.auth.database` | `nao` | PostgreSQL database name | | ||
| | `postgresql.auth.username` | `nao` | PostgreSQL username | | ||
| | `postgresql.auth.password` | `""` | PostgreSQL password (**change in production**) | | ||
|
|
||
| For the full list of values and their descriptions, see [`values.yaml`](./values.yaml). | ||
|
|
||
| ## Using an external database | ||
|
|
||
| Set `postgresql.enabled=false` and provide a connection URI: | ||
|
|
||
| ```yaml | ||
| postgresql: | ||
| enabled: false | ||
|
|
||
| secrets: | ||
| dbUri: "postgres://user:password@my-postgres-host:5432/nao" | ||
| ``` | ||
|
|
||
| SQLite is also supported for single-node / testing deployments: | ||
|
|
||
| ```yaml | ||
| postgresql: | ||
| enabled: false | ||
|
|
||
| secrets: | ||
| dbUri: "sqlite:./db.sqlite" | ||
| ``` | ||
|
|
||
| ## Upgrade | ||
|
|
||
| ```bash | ||
| helm upgrade nao ./helm --namespace nao -f my-values.yaml | ||
| ``` | ||
|
|
||
| ## Rollback | ||
|
|
||
| ```bash | ||
| # List revision history | ||
| helm history nao --namespace nao | ||
|
|
||
| # Roll back to a previous revision | ||
| helm rollback nao <revision> --namespace nao | ||
| ``` | ||
|
|
||
| ## Running chart tests | ||
|
|
||
| ```bash | ||
| helm test nao --namespace nao | ||
| ``` | ||
|
|
||
| ## Uninstall | ||
|
|
||
| ```bash | ||
| helm uninstall nao --namespace nao | ||
| ``` | ||
|
|
||
| > **Note:** PersistentVolumeClaims are not deleted automatically. Remove them manually if no longer needed: | ||
| > ```bash | ||
| > kubectl delete pvc -l app.kubernetes.io/instance=nao --namespace nao | ||
| > ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| Thank you for installing {{ .Chart.Name }} {{ .Chart.AppVersion }}. | ||
|
|
||
| Release name : {{ .Release.Name }} | ||
| Namespace : {{ .Release.Namespace }} | ||
|
|
||
| To access nao locally, run: | ||
|
|
||
| kubectl port-forward --namespace {{ .Release.Namespace }} \ | ||
| svc/{{ include "nao.fullname" . }} 5005:{{ .Values.service.port }} | ||
|
|
||
| Then open http://127.0.0.1:5005 in your browser. | ||
|
|
||
| Context mode: {{ .Values.config.contextSource }} | ||
| {{- if eq .Values.config.contextSource "local" }} | ||
| → Make sure a PVC containing a valid nao project is mounted at {{ .Values.config.contextPath }}. | ||
| {{- else if eq .Values.config.contextSource "git" }} | ||
| → The project will be cloned from {{ .Values.config.contextGitUrl }} (branch: {{ .Values.config.contextGitBranch }}). | ||
| {{- else if eq .Values.config.contextSource "api" }} | ||
| → Deploy projects via: nao deploy --url http://<nao-host> | ||
| {{- end }} | ||
|
|
||
| To check the release status: | ||
| helm status {{ .Release.Name }} --namespace {{ .Release.Namespace }} | ||
| helm get all {{ .Release.Name }} --namespace {{ .Release.Namespace }} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,109 @@ | ||
| {{/* | ||
| Expand the name of the chart. | ||
| */}} | ||
| {{- define "nao.name" -}} | ||
| {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Create a default fully qualified app name. | ||
| We truncate at 63 chars because some Kubernetes name fields are limited to this. | ||
| */}} | ||
| {{- define "nao.fullname" -}} | ||
| {{- if .Values.fullnameOverride }} | ||
| {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} | ||
| {{- else }} | ||
| {{- $name := default .Chart.Name .Values.nameOverride }} | ||
| {{- if contains $name .Release.Name }} | ||
| {{- .Release.Name | trunc 63 | trimSuffix "-" }} | ||
| {{- else }} | ||
| {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} | ||
| {{- end }} | ||
| {{- end }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Create chart name and version as used by the chart label. | ||
| */}} | ||
| {{- define "nao.chart" -}} | ||
| {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Common labels | ||
| */}} | ||
| {{- define "nao.labels" -}} | ||
| helm.sh/chart: {{ include "nao.chart" . }} | ||
| {{ include "nao.selectorLabels" . }} | ||
| {{- if .Chart.AppVersion }} | ||
| app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} | ||
| {{- end }} | ||
| app.kubernetes.io/managed-by: {{ .Release.Service }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Common annotations — applied at metadata.annotations on top-level objects | ||
| (Deployment, StatefulSet, etc.), as required by org-wide admission policies. | ||
| */}} | ||
| {{- define "nao.annotations" -}} | ||
| {{- with .Values.commonAnnotations }} | ||
| {{- toYaml . }} | ||
| {{- end }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Selector labels | ||
| */}} | ||
| {{- define "nao.selectorLabels" -}} | ||
| app.kubernetes.io/name: {{ include "nao.name" . }} | ||
| app.kubernetes.io/instance: {{ .Release.Name }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Service account name | ||
| */}} | ||
| {{- define "nao.serviceAccountName" -}} | ||
| {{- if .Values.serviceAccount.create }} | ||
| {{- default (include "nao.fullname" .) .Values.serviceAccount.name }} | ||
| {{- else }} | ||
| {{- default "default" .Values.serviceAccount.name }} | ||
| {{- end }} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| Full image reference (repository:tag) | ||
| */}} | ||
| {{- define "nao.image" -}} | ||
| {{- $tag := .Values.image.tag | default .Chart.AppVersion -}} | ||
| {{- printf "%s:%s" .Values.image.repository $tag -}} | ||
| {{- end }} | ||
|
|
||
| {{/* | ||
| PostgreSQL host — uses the bitnami subchart service name when postgresql.enabled=true, | ||
| otherwise falls back to a user-supplied host embedded in secrets.dbUri. | ||
| */}} | ||
| {{- define "nao.postgresqlHost" -}} | ||
| {{- if .Values.postgresql.enabled -}} | ||
| {{- printf "%s-postgresql" (include "nao.fullname" .) -}} | ||
| {{- end -}} | ||
| {{- end }} | ||
| {{/* | ||
| Build the DB_URI from subchart values when postgresql.enabled=true. | ||
| Does not support auth.existingSecret — when using existingSecret, | ||
| set secrets.dbUri manually with the full connection string. | ||
| */}} | ||
| {{- define "nao.dbUri" -}} | ||
| {{- if .Values.postgresql.enabled -}} | ||
| {{- if not .Values.postgresql.auth.existingSecret }} | ||
| {{- printf "postgres://%s:%s@%s:5432/%s" | ||
| .Values.postgresql.auth.username | ||
| .Values.postgresql.auth.password | ||
| (include "nao.postgresqlHost" .) | ||
| .Values.postgresql.auth.database -}} | ||
| {{- else }} | ||
| {{- .Values.secrets.dbUri }} | ||
| {{- end }} | ||
| {{- else -}} | ||
| {{- .Values.secrets.dbUri -}} | ||
| {{- end -}} | ||
| {{- end }} | ||
|
cubic-dev-ai[bot] marked this conversation as resolved.
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P1: The default image tag resolves to
:latest(viaappVersion: "latest"defaulted intoimage.tag), which is a mutable tag. This means the same chart version can pull a different image after a pod restart or node re-schedule with no chart version change, making rollbacks unreliable and deployments unreproducible. Since this is the official Helm chart for deploying nao as a managed service, a specific default tag (e.g., the latest stable release) would be more appropriate. Consider either pinningappVersionto a specific release tag or adding adefaultTagentry in values.yaml so the chart version and image tag are independently manageable.Prompt for AI agents