|
|
|
|
@ -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)
|
|
|
|
|
|