norish upgrade

This commit is contained in:
Richard Tomik
2026-08-20 15:32:03 +02:00
parent 0e5c73976b
commit 84168077ee
5 changed files with 226 additions and 12 deletions

View File

@ -2,8 +2,8 @@ apiVersion: v2
name: norish
description: Norish helm chart for Kubernetes - A recipe management and meal planning application
type: application
version: 0.0.5
appVersion: "v0.15.4-beta"
version: 0.0.6
appVersion: "v0.20.0-beta"
maintainers:
- name: Richard Tomik
email: no@m.com
@ -14,4 +14,4 @@ keywords:
- norish
home: https://github.com/rtomik/helm-charts
sources:
- https://github.com/norishapp/norish
- https://github.com/norish-recipes/norish

View File

@ -1,6 +1,6 @@
# Norish Helm Chart
A Helm chart for deploying [Norish](https://github.com/norishapp/norish), a recipe management and meal planning application, on Kubernetes.
A Helm chart for deploying [Norish](https://github.com/norish-recipes/norish), a recipe management and meal planning application, on Kubernetes.
## Introduction
@ -112,6 +112,24 @@ config:
passwordAuthEnabled: "true"
```
Set the redirect URI in your provider to:
`https://norish.example.com/api/auth/oauth2/callback/oidc`
Optionally map OIDC group claims to the admin role and to households:
```yaml
config:
auth:
oidc:
enabled: true
claimMapping:
enabled: true
scopes: "groups"
groupsClaim: "groups"
adminGroup: "norish_admin"
householdGroupPrefix: "norish_household_"
```
### GitHub OAuth
1. Create a GitHub OAuth App at https://github.com/settings/developers
@ -148,6 +166,30 @@ persistence:
existingClaim: "my-existing-pvc"
```
### Adopting an Existing (non-Helm) Norish Install
Resource names are derived from the chart name, not the release name, so this chart always
creates `norish-secret` and `norish-uploads`. If you already run Norish from hand-written
manifests using those same names, point the chart at the existing objects instead of
letting it template new ones:
```yaml
config:
masterKey:
existingSecret: "norish-secret" # reuse the existing key
secretKey: "master-key"
persistence:
existingClaim: "norish-uploads"
redis:
existingSecret: "norish-secret"
urlKey: "redis-url"
```
⚠️ **Never let a new `MASTER_KEY` be generated for an existing database.** The key derives
the encryption keys, so replacing it makes every previously encrypted value unreadable.
Helm refuses to adopt resources it does not own, but a GitOps tool configured to replace or
prune resources will not stop you — set `existingSecret` before the first sync.
## Parameters
### Global Parameters
@ -162,7 +204,7 @@ persistence:
| Name | Description | Default |
|------|-------------|---------|
| `image.repository` | Norish image repository | `norishapp/norish` |
| `image.tag` | Image tag | `v0.15.4-beta` |
| `image.tag` | Image tag | `v0.20.0-beta` |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
| `imagePullSecrets` | Image pull secrets | `[]` |
@ -245,6 +287,11 @@ persistence:
| `config.logLevel` | Log level (`trace`, `debug`, `info`, `warn`, `error`, `fatal`) | `""` |
| `config.trustedOrigins` | Additional trusted origins (comma-separated) | `""` |
| `config.passwordAuthEnabled` | Enable/disable password auth | `""` |
| `config.enableRegistration` | Allow self-registration of new users | `""` |
| `config.uploadsDir` | Uploads directory / volume mount path (`UPLOADS_DIR`) | `/app/uploads` |
| `config.parserApiTimeoutMs` | Recipe parser API timeout in ms | `""` |
| `config.defaultLocale` | Instance default locale | `""` |
| `config.enabledLocales` | Comma-separated list of enabled locales (empty = all) | `""` |
| `config.extraEnv` | Extra environment variables | `[]` |
### Master Key Configuration (Required)
@ -270,6 +317,11 @@ Generate with: `openssl rand -base64 32`
| `config.auth.oidc.existingSecret` | Existing secret name | `""` |
| `config.auth.oidc.clientIdKey` | Key for client ID in secret | `oidc-client-id` |
| `config.auth.oidc.clientSecretKey` | Key for client secret in secret | `oidc-client-secret` |
| `config.auth.oidc.claimMapping.enabled` | Assign admin role / households from OIDC claims | `false` |
| `config.auth.oidc.claimMapping.scopes` | Extra scopes to request (comma-separated) | `""` |
| `config.auth.oidc.claimMapping.groupsClaim` | Claim containing user groups | `groups` |
| `config.auth.oidc.claimMapping.adminGroup` | Group granting the server admin role | `norish_admin` |
| `config.auth.oidc.claimMapping.householdGroupPrefix` | Prefix for household groups | `norish_household_` |
### GitHub OAuth
@ -316,13 +368,16 @@ Generate with: `openssl rand -base64 32`
| Name | Description | Default |
|------|-------------|---------|
| `probes.startup.enabled` | Enable startup probe | `true` |
| `probes.startup.path` | Startup probe path | `/api/v1/health` |
| `probes.startup.initialDelaySeconds` | Startup initial delay | `10` |
| `probes.startup.periodSeconds` | Startup period | `10` |
| `probes.startup.failureThreshold` | Startup failure threshold | `30` |
| `probes.liveness.enabled` | Enable liveness probe | `true` |
| `probes.liveness.path` | Liveness probe path (see [Upgrading](#upgrading) for why this is not the health endpoint) | `/` |
| `probes.liveness.initialDelaySeconds` | Liveness initial delay | `30` |
| `probes.liveness.periodSeconds` | Liveness period | `10` |
| `probes.readiness.enabled` | Enable readiness probe | `true` |
| `probes.readiness.path` | Readiness probe path | `/api/v1/health` |
| `probes.readiness.initialDelaySeconds` | Readiness initial delay | `5` |
| `probes.readiness.periodSeconds` | Readiness period | `5` |
@ -336,13 +391,102 @@ Generate with: `openssl rand -base64 32`
No configuration changes required. Redis, PostgreSQL, and Chrome headless are already configured. Back up your database before upgrading as a precaution.
### From chart 0.0.5 to chart 0.0.6 (app v0.20.0-beta)
⚠️ **Back up your database and your uploads volume before upgrading.**
**Which app version were you on?** Chart 0.0.5 declared `appVersion: v0.15.4-beta` but
shipped `image.tag: v0.16.2-beta` in values.yaml, and the tag always wins. So unless you
pinned `image.tag` yourself, you were already running **v0.16.2-beta** and the two
v0.16.x data-loss items below have already happened to you — skip them. Chart 0.0.6 fixes
that mismatch: both `appVersion` and `image.tag` are now `v0.20.0-beta`.
**v0.16.0-beta — data loss (calendar) — only if you pinned `image.tag` to v0.15.x or older**
All calendar data is permanently deleted on upgrade. The calendar was rebuilt on a new
database schema and upstream provides no migration path. Recipes, groceries and planning
data outside the calendar are unaffected.
**v0.16.1-beta — data loss (custom units) — only if you pinned `image.tag` to v0.16.0 or older**
Custom UOM (unit of measure) data is wiped as part of the move to a locale-aware schema.
Custom units have to be re-created after the upgrade.
The remaining items apply to everyone upgrading from chart 0.0.5.
**v0.17.0-beta — image restructure**
Upstream migrated to a pnpm/Turborepo monorepo. The Docker image, its internal paths and
the package layout all changed. The image name is unchanged (`norishapp/norish`), and this
chart needs no value changes for it, but the release is explicitly flagged as
"back up your data before upgrading" by upstream.
**v0.18.0-beta — breaking: health endpoint moved**
The previous `/api/health` endpoint was removed; the endpoint is now `/api/v1/health`.
Verified on both versions: on v0.17.3-beta `/api/health` returns `{"status":"ok"}`, on
v0.20.0-beta it falls through to the auth redirect. Note that a removed API path returns a
**307 redirect**, not a 404 — so an HTTP probe or uptime check still pointing at
`/api/health` reports *success* while checking nothing at all. Repoint it explicitly.
This chart's probe defaults changed accordingly:
| Value | Old default | New default |
|-------|-------------|-------------|
| `probes.startup.path` | `/` | `/api/v1/health` |
| `probes.readiness.path` | `/` | `/api/v1/health` |
| `probes.liveness.path` | `/` | `/` (unchanged — see below) |
If you pinned these paths in your own values, update them. Any external uptime monitor or
ingress health check pointing at the old endpoint must be updated as well.
**Why liveness deliberately does not use the health endpoint.** Since v0.18.1-beta the
endpoint also reports database health, and it returns **503** when PostgreSQL is
unreachable — verified on a test cluster by scaling the database to zero. With liveness
pointed at it, the sequence is:
1. Six consecutive 503s fail the liveness probe and the kubelet restarts the container.
2. On restart the app runs its migrations, cannot reach the database, and exits 1.
3. The pod enters `CrashLoopBackOff`, so it stays down for the backoff interval even
after the database comes back.
A short database blip therefore becomes a multi-minute outage. With liveness on `/` (which
returns a 307 redirect — a probe success) a running pod rides out a DB blip: readiness
still fails, so the pod is removed from the Service endpoints, and it serves again as soon
as the database returns, with no restart.
Note that the app cannot start at all without a reachable database, by design — it runs
migrations at boot and exits on failure. Liveness on the app root does not hide that; it
only avoids restarting a process that is alive and would otherwise recover on its own.
**v0.18.0-beta — recipe import pipeline**
Imports moved to the `recipe-scrapers` Python package. The Chrome headless sidecar is
still required (upstream still ships it and still lists `CHROME_WS_ENDPOINT` as a core
required setting), so leave `chrome.enabled: true`. Import timeouts can be tuned with the
new `config.parserApiTimeoutMs`.
**Migration path — tested**
The v0.17.3-beta → v0.20.0-beta upgrade was verified on a Kubernetes test cluster against
a schema created by v0.17.3-beta: migrations applied automatically at boot (31 → 40
applied migrations, 29 → 34 tables), with no manual steps and no errors. The app applies
migrations itself on startup; there is nothing to run by hand.
**v0.19.0-beta / v0.20.0-beta — no configuration changes**
Web app refresh (HeroUI v3, home screen, cooking mode), offline support, recipe
provenance and AI workflow improvements. Nothing to change in this chart.
**New chart values in this release** (all optional, all default to the previous behaviour):
`config.enableRegistration`, `config.uploadsDir`, `config.parserApiTimeoutMs`,
`config.defaultLocale`, `config.enabledLocales` and `config.auth.oidc.claimMapping.*`.
The Chrome sidecar also now passes `--disable-features=dbus`, matching the upstream Docker
Compose example.
## Troubleshooting
- **Master Key Not Set**: Generate with `openssl rand -base64 32`
- **Login Failures**: Password auth is enabled by default when no OAuth/OIDC is configured. Verify callback URLs match your ingress hostname.
- **Database Connection Failed**: Verify host, credentials, and that the database exists.
- **Chrome Headless Issues**: Chrome requires `SYS_ADMIN` capability and 256Mi-512Mi memory. Check logs with `kubectl logs -l app.kubernetes.io/name=norish -c chrome-headless`
- **Recipe Parsing Failures**: Ensure Chrome is running. `CHROME_WS_ENDPOINT` is automatically configured by the chart.
- **Recipe Parsing Failures**: Ensure Chrome is running. `CHROME_WS_ENDPOINT` is automatically configured by the chart. Raise `config.parserApiTimeoutMs` if imports time out.
- **Pod Never Becomes Ready After Upgrade**: On v0.18.0+ the health endpoint is `/api/v1/health`. Probes still pointing at the old endpoint will fail. Check with `kubectl exec deploy/norish -c norish -- wget -qO- http://127.0.0.1:3000/api/v1/health` (use `127.0.0.1`, not `localhost` — that resolves to `::1` in the container and is refused). A healthy response reports `status`, `db.status`, the app version and the `recipe-scrapers` version.
- **CrashLoopBackOff With "Migration failed" / "Server startup failed"**: The app runs migrations at boot and exits if PostgreSQL is unreachable. Verify `database.host`, credentials and that the database exists; the pod recovers on its own once the database is reachable.
```bash
kubectl get pods -l app.kubernetes.io/name=norish
@ -351,5 +495,7 @@ kubectl logs -l app.kubernetes.io/name=norish
## Links
- [Norish GitHub](https://github.com/norishapp/norish)
- [Norish GitHub](https://github.com/norish-recipes/norish)
- [Norish Documentation](https://docs.norish.dev)
- [Norish Releases](https://github.com/norish-recipes/norish/releases)
- [Chart Source](https://github.com/rtomik/helm-charts/tree/main/charts/norish)

View File

@ -71,4 +71,4 @@ IMPORTANT CONFIGURATION NOTES:
Configure ONE provider (OIDC, GitHub, or Google) to create your admin account.
{{- end }}
For more information, visit: https://github.com/norishapp/norish
For more information, visit: https://github.com/norish-recipes/norish

View File

@ -96,6 +96,24 @@ spec:
- name: PASSWORD_AUTH_ENABLED
value: {{ .Values.config.passwordAuthEnabled | quote }}
{{- end }}
{{- if .Values.config.enableRegistration }}
- name: ENABLE_REGISTRATION
value: {{ .Values.config.enableRegistration | quote }}
{{- end }}
- name: UPLOADS_DIR
value: {{ .Values.config.uploadsDir | quote }}
{{- if .Values.config.parserApiTimeoutMs }}
- name: PARSER_API_TIMEOUT_MS
value: {{ .Values.config.parserApiTimeoutMs | quote }}
{{- end }}
{{- if .Values.config.defaultLocale }}
- name: DEFAULT_LOCALE
value: {{ .Values.config.defaultLocale | quote }}
{{- end }}
{{- if .Values.config.enabledLocales }}
- name: ENABLED_LOCALES
value: {{ .Values.config.enabledLocales | quote }}
{{- end }}
{{- if .Values.database.existingSecret }}
- name: DB_USERNAME
valueFrom:
@ -164,6 +182,20 @@ spec:
- name: OIDC_WELLKNOWN
value: {{ .Values.config.auth.oidc.wellKnown | quote }}
{{- end }}
{{- if .Values.config.auth.oidc.claimMapping.enabled }}
- name: OIDC_CLAIM_MAPPING_ENABLED
value: "true"
{{- with .Values.config.auth.oidc.claimMapping.scopes }}
- name: OIDC_SCOPES
value: {{ . | quote }}
{{- end }}
- name: OIDC_GROUPS_CLAIM
value: {{ .Values.config.auth.oidc.claimMapping.groupsClaim | quote }}
- name: OIDC_ADMIN_GROUP
value: {{ .Values.config.auth.oidc.claimMapping.adminGroup | quote }}
- name: OIDC_HOUSEHOLD_GROUP_PREFIX
value: {{ .Values.config.auth.oidc.claimMapping.householdGroupPrefix | quote }}
{{- end }}
- name: OIDC_CLIENT_ID
valueFrom:
secretKeyRef:
@ -234,7 +266,7 @@ spec:
{{- end }}
volumeMounts:
- name: uploads
mountPath: /app/uploads
mountPath: {{ .Values.config.uploadsDir }}
{{- with .Values.extraVolumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
@ -256,6 +288,7 @@ spec:
- "--no-sandbox"
- "--disable-gpu"
- "--disable-dev-shm-usage"
- "--disable-features=dbus"
- "--remote-debugging-address=0.0.0.0"
- "--remote-debugging-port={{ .Values.chrome.port }}"
- "--headless"

View File

@ -5,7 +5,7 @@ fullnameOverride: ""
## Image settings
image:
repository: norishapp/norish
tag: "v0.16.2-beta"
tag: "v0.20.0-beta"
pullPolicy: IfNotPresent
imagePullSecrets: []
@ -96,7 +96,7 @@ probes:
timeoutSeconds: 5
failureThreshold: 30
successThreshold: 1
path: /
path: /api/v1/health
liveness:
enabled: true
initialDelaySeconds: 30
@ -104,6 +104,11 @@ probes:
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
# Deliberately NOT /api/v1/health: since v0.18.1 that endpoint returns 503 when the
# database is unreachable, so a transient DB outage would restart the pod. The app
# root confirms the process is alive without coupling liveness to the database.
# Readiness still uses the health endpoint, which is what pulls the pod out of the
# Service during a DB outage.
path: /
readiness:
enabled: true
@ -112,7 +117,7 @@ probes:
timeoutSeconds: 3
failureThreshold: 3
successThreshold: 1
path: /
path: /api/v1/health
## Application configuration
config:
@ -154,6 +159,26 @@ config:
# Defaults to false if OIDC or OAuth is configured, true otherwise
passwordAuthEnabled: ""
# Allow new users to register themselves (ENABLE_REGISTRATION)
# Leave empty to use the application default (false).
# The first account created always becomes the server owner/admin.
enableRegistration: ""
# Uploads directory inside the container (UPLOADS_DIR)
# Also used as the mount path for the uploads volume
uploadsDir: "/app/uploads"
# Timeout for recipe parser API calls in milliseconds (PARSER_API_TIMEOUT_MS)
# Leave empty to use the application default (15000)
parserApiTimeoutMs: ""
# Instance default locale, must match a supported locale (DEFAULT_LOCALE)
defaultLocale: ""
# Comma-separated list of enabled locales (ENABLED_LOCALES)
# Leave empty to enable all locales. Example: "en,de,nl"
enabledLocales: ""
# Authentication provider configuration
# Configure ONE provider for initial admin account creation
# After first login, manage additional providers via Settings → Admin
@ -173,6 +198,16 @@ config:
clientIdKey: "oidc-client-id"
clientSecretKey: "oidc-client-secret"
# Claim mapping: auto-assign the admin role and households from OIDC claims
# Disabled by default for security
claimMapping:
enabled: false
# Additional scopes to request, comma-separated (e.g. "groups" for Keycloak)
scopes: ""
groupsClaim: "groups" # Claim containing the user groups
adminGroup: "norish_admin" # Group that grants the server admin role
householdGroupPrefix: "norish_household_" # Prefix for household groups
# GitHub OAuth
github:
enabled: false