Skip to content

feat(n8n): add n8n application chart - #311

Open
arunesh-j wants to merge 11 commits into
mainfrom
feature/n8n-chart
Open

arunesh-j wants to merge 11 commits into
mainfrom
feature/n8n-chart

Conversation

@arunesh-j

@arunesh-j arunesh-j commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Adds charts/n8n, an application chart deploying n8n backed by the repo's own postgres chart.

One pod serves the editor, the REST API and the webhook endpoints, and runs executions in the same process (EXECUTIONS_MODE=regular). Written to the conventions of charts/localai and charts/litellm: its own templates, no service subchart, and a values.schema.json carrying the zop.dev UI metadata (category, mutable, default, description).

image

Queue mode is deliberately out of scope for v0.0.1 — see the note at the end.

What it ships

charts/n8n/
├── Chart.yaml            v0.0.1, appVersion 2.34.5, annotations.type: application
├── values.yaml
├── values.schema.json
└── templates/  _helpers · configmap · secret · deployment · pvc · service
                ingress · serviceMonitor · alerts · validate · NOTES.txt

Dependency: postgres 0.0.13 from https://helm.zop.dev, condition: postgres.enabled.

Details that came out of reading the existing charts

Postgres credentials are mapped key by key. The subchart publishes DB_USER/DB_PASSWORD/DATABASE_URL, but n8n reads DB_POSTGRESDB_*. litellm can consume DATABASE_URL wholesale with envFrom; this chart cannot, so the two credential keys are wired with explicit secretKeyRefs and the rest come from the ConfigMap.

The PrometheusRule is named after the chart fullname, not the release. charts/postgres/templates/alerts.yaml already creates a rule called {{ .Release.Name }}, and two objects of the same kind and name in one release make Helm refuse to install — the same collision that pins redis to 0.0.1 in charts/outline.

postgres is pinned to 0.0.13, not the 0.0.12 every other consumer uses. 0.0.13 (#305) fixes alerts.yaml rendering a null service label that the PrometheusRule CRD rejects. On 0.0.12 this chart would have to carry postgres.name purely as a workaround.

A wait-for-db init container. The subchart provisions the database and role from a Pod that sleeps first, so on a fresh install the role n8n authenticates as does not exist yet. It authenticates rather than TCP-probing, because postgres accepts connections before the role exists.

N8N_ENCRYPTION_KEY is generated once and read back on upgrade via lookup, the pattern charts/postgres/templates/secret.yaml uses. Rotating it makes every stored credential undecryptable, so this is the one piece of state the chart is most careful with. existingSecret is supported.

One public URL, three variables. WEBHOOK_URL, N8N_HOST and N8N_PROTOCOL all derive from a single value, so the protocol n8n advertises cannot contradict the URL it hands out. n8n bakes that address into every registered webhook and OAuth callback, and getting it wrong leaves a working editor whose webhooks point at localhost.

Binary data mode follows persistence.enabledfilesystem when a volume exists, default (inlined into Postgres) when it does not. Filesystem mode keeps payloads out of execution_data; writing them to a container filesystem that vanishes on restart would be worse than putting them in the database.

Test evidence

Static — helm lint clean, no warnings or notices. All 8 misconfigurations fail the render with actionable messages; all 6 valid configurations render. The packaged tarball was verified to render standalone, guards included.

End-to-end on minikube (Kubernetes v1.34, helm 3.18.4):

Check Result
Install, default values n8n 1/1 Running, 0 restarts
Datastore 125 tables in Postgres; no database.sqlite on the volume
Health /healthz and /healthz/readiness both {"status":"ok"}
Metrics /metrics serving, n8n_instance_role_leader 1
Owner account created through /rest/owner/setup
Webhook execution GET /webhook/…200 {"verdict":"chart works"}, execution recorded status=success mode=webhook
Binary data /home/node/.n8n confirmed a separate ext4 mount; payload on the PVC, execution row reports binary=201B held outside the JSON
Upgrade encryption key and DB credentials byte-identical; a credential stored beforehand still decrypts in a genuinely recreated pod, no decryption errors
External database postgres.enabled=false against a hand-provisioned role — migrated into its own database, zero subchart objects

Three things the cluster corrected, all now folded in:

  1. The init container earns its place. In the external-database test the role did not exist yet and the pod sat at Init:0/1 for 2m26s with zero restarts, then started cleanly once it was created. Without it, that window is a crash loop.
  2. The Prometheus Operator CRDs are required regardless of metrics.enabled/alerts.enabled. Those flags gate this chart's objects; the postgres subchart renders its ServiceMonitor and PrometheusRule unconditionally, so a cluster without the CRDs fails with no matches for kind "PrometheusRule". The README documents this and gives the two kubectl apply lines.
  3. n8n 2.34.5 logs four deprecation notices at boot, each announcing a default that is about to change. All four are pinned in values.yaml so an image bump cannot quietly alter behaviour. Where the coming default is the safer one (unverified community nodes, decompression bounds) the chart adopts it now, having no existing behaviour to preserve; N8N_RUNNERS_TASK_TIMEOUT keeps today's 300s, because the planned cut to 60s would start killing legitimate long-running Code nodes. Task runners are already enabled by default in 2.x, so no N8N_RUNNERS_ENABLED is needed.

Not tested on a cluster: persistence.enabled=false. It renders correctly and flips binary mode to default, but was never installed that way.

Notes for the reviewer

Queue mode is out of scope for v0.0.1. It needs Redis, and charts/redis ≥ 0.0.5 ships an alerts.yaml whose PrometheusRule is also named {{ .Release.Name }} — so depending on both subcharts hits the collision described above. charts/outline works around it by pinning redis to 0.0.1, the last version without alerts. Inheriting that workaround for a feature nobody has asked for yet seemed a poor trade; better to solve the collision once, when queue mode is actually wanted.

Rebased onto main; the site note is now obsolete. This originally said the chart would not appear on helm.zop.dev because the site read a hardcoded docs/src/js/config.js. #309 has since replaced that with a reader over docs/index.yaml and closed #308, so n8n renders on the site as soon as this merges — its index entry carries annotations.type: application, a description and an icon, which is all the new code filters on.

docs/index.yaml conflicted and was regenerated, not hand-merged. Both this branch and main had regenerated the whole file. Resolved by discarding both versions and re-running helm repo index . --url https://helm.zop.dev over the merged docs/. Verified afterwards that the only semantic change against main is the added n8n v0.0.1 entry — 183 tarball digests match, nothing removed, nothing altered, and main's postgres v0.0.14 and litellm v0.0.2 entries are intact.

Open question: this chart pins postgres 0.0.13, while main has moved to 0.0.14. 0.0.13 still resolves, so the chart installs and lints clean. But #315 converted the postgres init from a bare Pod to a Job specifically so helm --wait can finish, and litellm bumped to 0.0.14 (#317) to pick that up. On 0.0.13 an n8n install run with --wait will hang on the never-Ready init Pod. Happy to move this to 0.0.14 and re-test before merge — say the word.

A single n8n instance serving the editor, REST API and webhook endpoints,
backed by the zopdev postgres chart.

Notable details, all of them things the existing charts made necessary:

- the postgres subchart publishes DB_USER/DB_PASSWORD, so credentials are
  mapped key by key into n8n's DB_POSTGRESDB_* rather than pulled in with
  envFrom the way litellm can with DATABASE_URL
- a wait-for-db init container, because that subchart creates the role from
  a Pod that sleeps first and n8n would otherwise crash-loop through the
  first minute of a fresh install
- the PrometheusRule is named after the chart fullname, not the release;
  the postgres subchart's rule already claims the release name and two
  same-named objects make helm refuse to install
- N8N_ENCRYPTION_KEY is generated once and read back on upgrade, since
  rotating it makes every stored credential undecryptable
- WEBHOOK_URL, N8N_HOST and N8N_PROTOCOL all derive from one public URL,
  so the protocol n8n advertises cannot contradict the URL it hands out
Both changes come from what the container actually reported on minikube.

n8n 2.34.5 logs four deprecation notices at boot, each saying a default is
about to change. They are pinned in values.yaml so an image bump cannot
quietly alter how a release behaves. Where the coming default is the safer
one (unverified community nodes, decompression bounds) the chart adopts it
now, having no existing behaviour to preserve; the runner task timeout keeps
today's 300s, because the planned cut to 60s would start killing legitimate
long-running Code nodes.

The README claimed the Prometheus Operator CRDs were only needed with
metrics or alerts enabled. They are needed regardless: those flags gate this
chart's objects, while the postgres subchart renders a ServiceMonitor and a
PrometheusRule unconditionally, so without the CRDs the install fails on
`no matches for kind "PrometheusRule"`.
Sets the icon, which clears the last `helm lint` notice, then packages the
chart and regenerates the repository index per CONTRIBUTING. Packaged as
v0.0.1 to match how every other chart is published, since the tag carries a
`v` prefix that Chart.yaml does not.
docs/index.yaml conflicted because both sides regenerated it wholesale.
Resolved by discarding both versions and re-running

  helm repo index . --url https://helm.zop.dev

over the merged docs/ directory, which is the only resolution that cannot
silently drop an entry. Verified afterwards that the sole semantic change
against main is the added n8n v0.0.1 entry: 183 tarball digests match, no
entry removed, none altered - postgres v0.0.14 and litellm v0.0.2 from
main are intact.

@jatintalgotra-zd jatintalgotra-zd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the chart against the repo conventions and against n8n's own docs and source. The structure is good — helper/label conventions match litellm exactly, the postgresSecretName derivation is correct against the subchart, naming the PrometheusRule after the fullname to dodge the {{ .Release.Name }} collision is the right call, and the packaged tarball is byte-identical to source with docs/index.yaml cleanly at 182 → 183 entries. The four pinned deprecation vars are also exactly right — all four carry checkValue: (value) => value === undefined in n8n's deprecation registry, so setting them genuinely silences the warnings.

Flagging only the major things below.


1. N8N_DEFAULT_BINARY_DATA_MODE: "default" is in-memory, not Postgres

This is the most significant one. When persistence.enabled=false the chart sets default and documents it as Postgres storage in five places (configmap.yaml:33, values.yaml:24, README.md:184, values.schema.json:57, NOTES.txt:4, plus the PR description).

From n8n's packages/core/src/binary-data/binary-data.config.ts:

export const BINARY_DATA_MODES = ['default', 'filesystem', 's3', 'azure', 'database']
availableModes = ['filesystem', 's3', 'database']   // 'default' is not in the available list
  • types.ts"except default in-memory mode"
  • Docs — "default keeps binary data in memory… or database to use the DB"
  • n8n's deprecation registry — "In-memory binary data storage (default mode) will be removed in a future version. Switch to filesystem, s3, or database."

So this path accumulates payloads in the pod's heap against a 2Gi limit — an OOMKill, which is the failure the branch was written to avoid — and emits a deprecation warning. It's also the one path the PR notes was never installed on a cluster.

The Postgres-backed mode the chart is describing is database (N8N_BINARY_DATA_DATABASE_MAX_FILE_SIZE, capped at 1024 MiB for the BYTEA limit). One-word fix.

2. postgres 0.0.13 — and the tarball vendors it

Raised in the PR description as an open question; I don't think it can be deferred. postgres-v0.0.13 renders the DB init as kind: Pod, and Helm v3.18.3's pkg/kube/ready.go:230 isPodReady returns true only on PodReady=True — there's no PodSucceeded exemption, so a completed Pod is Ready=False forever and helm install --wait blocks until timeout.

The part that makes it a blocker rather than a follow-up: docs/n8n-v0.0.1.tgz vendors charts/postgres at version: v0.0.13, same as the litellm/localai tarballs. Publishing freezes the broken init into v0.0.1 permanently. Bumping to 0.0.14 means re-running helm dependency update, repackaging, and regenerating the index. (waitForDatabase.image.tag: "17.4.0" still matches 0.0.14's version, so no change needed there.)

3. WEBHOOK_URL is deprecated — n8n wants N8N_WEBHOOK_URL

configmap.yaml:44 emits the deprecated name. From n8n's config source:

/** Public base URL for both test and production webhooks. Successor to the deprecated `WEBHOOK_URL`. */
@Env('N8N_WEBHOOK_URL')

It's in the deprecation registry with no checkValue, so it warns whenever it's set — unlike the four vars the chart deliberately pins, which only warn when unset. Worth fixing given the chart's stated goal of keeping the boot log clean. n8n's reverse-proxy guide pairs it with N8N_PROXY_HOPS=1, which the chart also never sets.

4. Enabling the ingress without TLS produces an instance nobody can log into

--set ingress.enabled=true --set ingress.host=n8n.example.com with no tlsSecretName renders N8N_PROTOCOL: http. N8N_SECURE_COOKIE defaults to true (auth.config.ts), is passed straight through in frontend.service.ts:237 — whose own comment reads "Blocks insecure access incompatible with the authentication cookie" — and there is no host-based override anywhere in the codebase. http://localhost works only because browsers treat it as a secure context (and Safari doesn't).

So the editor comes up and login is broken in every browser. The chart already knows the protocol is http at render time, so it can set N8N_SECURE_COOKIE: "false" in that branch, or fail the render. Same class of trap the chart carefully guards against for webhook URLs.

5. The env guard list covers 8 of 19 derived keys

$owned in validate.yaml misses 11 keys the ConfigMap emits, so an override appends a duplicate YAML key rather than being rejected — and last-key-wins produces exactly the contradiction the design set out to prevent:

helm template n8n . --set ingress.enabled=true --set ingress.host=n8n.example.com \
                    --set env.WEBHOOK_URL=https://other.example.com/

  WEBHOOK_URL:  "http://n8n.example.com/"     ← derived
  N8N_HOST:     "n8n.example.com"
  N8N_PROTOCOL: "http"
  ...
  WEBHOOK_URL:  "https://other.example.com/"  ← from env, wins

n8n now advertises https://other.example.com/ while N8N_PROTOCOL says http. Unguarded: WEBHOOK_URL, N8N_HOST, N8N_PROTOCOL, EXECUTIONS_MODE, N8N_PORT, N8N_LISTEN_ADDRESS, N8N_METRICS, DB_POSTGRESDB_SCHEMA, GENERIC_TIMEZONE, TZ, N8N_DIAGNOSTICS_ENABLED. Either extend $owned or build the map with merge so overrides replace instead of duplicating.

6. Uninstall keeps the data but deletes the encryption key

The n8n PVC has resource-policy: keep, and the postgres subchart uses volumeClaimTemplates — Kubernetes never deletes those. The encryption Secret has no annotations at all, so helm uninstall removes it.

Uninstall → reinstall over the retained database therefore generates a fresh key and makes every stored credential undecryptable. That's the outcome the chart works hardest to prevent everywhere else. Either annotate the Secret, or say it explicitly in README §Uninstalling — which currently mentions only pvc n8n-n8n, not the postgres volume either.

7. Alerts are missing servicealert: "true"

Across the 13 charts with a PrometheusRule, all 67 alert rules carry this label — 100%, localai and holmesgpt included. n8n's two rules have none. If that's what the zop.dev alert pipeline routes on, these alerts render but go nowhere.

Also N8nRestarting matches on container="n8n" with no release scoping, so a second n8n release in the same namespace fires this release's alert — holmesgpt already has the right shape (pod=~"{{ $deployment }}-.*").


Smaller items (site card renders as "N8n" without a display.js entry; N8N_LISTEN_ADDRESS: "0.0.0.0" narrows n8n's :: default and the comment says the opposite; appVersion 2.34.5 while stable/latest now resolve to 2.34.6; terminationGracePeriodSeconds colliding with N8N_GRACEFUL_SHUTDOWN_TIMEOUT; missing N8N_EDITOR_BASE_URL) I'll leave out of this pass — happy to write them up separately if useful.

…kie, guards

All seven review findings, each verified against the shipped image or a
minikube install rather than taken on faith.

postgres 0.0.14, not 0.0.13. Up to 0.0.13 the database init ran as a bare Pod,
and a completed Pod never reports Ready, so `helm install --wait` blocked until
it timed out. The published tarball vendors the subchart, so 0.0.13 would have
frozen that into v0.0.1 permanently. `helm install --wait` now returns in 2m27s.

N8N_DEFAULT_BINARY_DATA_MODE=database when persistence is off, not "default".
Despite the name, `default` keeps payloads in the process heap - the image's own
breaking-change rule reads "The in-memory binary data storage mode (`default`) is
removed. Instances using it must switch to `filesystem`, `s3`, or `database`" -
so that path was an OOMKill against the memory limit, and it was documented as
Postgres storage in five places. Installed and confirmed: binary_data table
holds the payload, 1 row.

N8N_WEBHOOK_URL replaces the deprecated WEBHOOK_URL, which warns whenever it is
set rather than when unset, unlike the four variables the chart pins on purpose.

N8N_SECURE_COOKIE=false when the derived protocol is http. n8n marks the auth
cookie Secure by default and browsers will not send it over plain http, so
enabling the ingress without TLS produced an editor nobody could log into.

The env guard covered 8 of 19 derived keys, so an override appended a duplicate
ConfigMap key and last-one-wins. The list now lives in one helper that both
validate.yaml and configmap.yaml read, and the ConfigMap skips owned keys so no
template ordering can emit a key twice.

The encryption Secret now carries resource-policy: keep, matching the volumes
holding the data it decrypts.

Both alert rules carry servicealert: "true" - every one of the repo's other 67
rules has it - and the restart alert is scoped to this release's pods instead of
firing on any container named n8n in the namespace.

Also: appVersion 2.35.3, what `stable` resolves to now; a display.js entry so
the site card reads "n8n" rather than titleCased "N8n".

One correction found by testing rather than review: uninstall-and-reinstall over
the retained volumes does NOT work, because the postgres subchart's root secret
is not retained while its volume is, so the new superuser password no longer
matches the data directory. The README said the opposite; it now documents
upgrade as the way to keep an instance and lists both secrets to back up.

@jatintalgotra-zd jatintalgotra-zd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at 5ea2a68. All seven findings are fixed, and I verified each rather than reading the diff:

  • Binary modepersistence.enabled=false now renders database, =true still filesystem; corrected in all five places it was wrong.
  • postgres 0.0.14 — and, importantly, the tarball was repackaged: it vendors postgres v0.0.14 with kind: Job ×1 and kind: Pod ×0.
  • N8N_WEBHOOK_URL — emitted; the deprecated name is gone from the ConfigMap but retained in the guard list, which is the right call.
  • N8N_SECURE_COOKIE — correctly scoped: emitted only on the http path, absent on https and on the no-public-URL default.
  • env guard — the shared n8n.ownedEnv helper is airtight. splitList yields 21 clean tokens with no whitespace contamination (worth checking, since a trailing newline on the last element would have silently un-guarded N8N_ENCRYPTION_KEY), all 21 keys are individually rejected at render, and no ConfigMap key is emitted twice anywhere in the render matrix.
  • Encryption Secretresource-policy: keep present.
  • Alertsservicealert: "true" on both rules, restart alert rescoped to this release's pods.

Packaging is clean: lint passes, ten render paths succeed, tarball templates are byte-identical to source, the index digest matches the tgz, and the semantic diff of index.yaml against main is exactly the one added n8n block with all 182 pre-existing digests intact. appVersion: 2.35.3 also checks out — stable and latest were both re-pointed at that digest earlier today.

Your uninstall correction is right, and I confirmed the mechanism: postgres 0.0.14's root secret carries no resource-policy, and its .Release.IsInstall branch mints a fresh password without consulting lookup, so a reinstall can never match a retained data directory. That looks like a postgres-chart bug worth its own issue rather than anything n8n should work around.


One major issue, and it got sharper with this commit

Deriving the protocol from ingress.tlsSecretName alone now drops the Secure flag on genuinely-HTTPS deployments.

tlsSecretName is only one of several ways TLS gets terminated. With an ALB/ACM or GKE managed certificate the site is HTTPS-only and there is no tls: block at all:

helm template n8n . --set ingress.enabled=true --set ingress.host=n8n.example.com \
  --set ingress.className=alb \
  --set 'ingress.annotations.alb\.ingress\.kubernetes\.io/certificate-arn=arn:aws:acm:...'

  N8N_WEBHOOK_URL:   "http://n8n.example.com/"
  N8N_PROTOCOL:      "http"
  N8N_SECURE_COOKIE: "false"

Same for cert-manager.io/cluster-issuer set as an annotation. The advertised-scheme half of this predates the commit; what is new is that the instance now also serves its auth cookie without Secure on a site reachable only over HTTPS. That is a real downgrade, and it is silent — nothing in the render, the NOTES, or the logs says it happened.

A schemeless webhookUrl triggers the same path: --set webhookUrl=n8n.example.com yields N8N_WEBHOOK_URL: "n8n.example.com/" plus N8N_SECURE_COOKIE: "false".

The escape hatch works — webhookUrl: https://n8n.example.com/ restores both the scheme and the Secure flag — and _helpers.tpl shows you already had upstream termination in mind when you split protocol out of the URL. The gap is that nothing routes a user to it: I grepped the README and NOTES and there is no mention of annotation-terminated TLS.

Worth considering, roughly in order of preference:

  1. Make the cookie downgrade opt-in rather than inferred — a insecureCookie: true value (or an explicit ingress.tls: true|false) so an absent field never silently weakens the cookie. Inferring "no TLS" from "no tlsSecretName" is the part that doesn't hold.
  2. Reject a schemeless webhookUrl in validate.yaml — a hasPrefix "http" check, in the same spirit as the other guards.
  3. At minimum, document the annotation-TLS case next to the existing http concession in the README, and have NOTES print a line when the cookie is being downgraded.

Everything else I raised earlier (N8N_LISTEN_ADDRESS, N8N_PROXY_HOPS, N8N_EDITOR_BASE_URL, terminationGracePeriodSeconds, dead N8nDown when metrics.enabled=false, editDisabled) is minor and I'm happy for it to land after this, or not at all.

Follow-up review found that deriving the protocol from ingress.tlsSecretName
alone dropped the Secure flag on deployments that are HTTPS-only. tlsSecretName
is one way TLS gets terminated; an ALB with an ACM certificate, a GKE managed
certificate and a cert-manager annotation all serve HTTPS while rendering no
tls: block at all. Confirmed: with an alb className and a certificate-arn
annotation the chart emitted N8N_PROTOCOL http AND N8N_SECURE_COOKIE false, so
the previous commit had turned a wrong advertised scheme into a silent security
downgrade on a site reachable only over https.

The cookie is now an explicit opt-in, insecureCookie, and is never inferred.
Absent, n8n keeps its own secure default. A genuinely plain-http instance sets
it and NOTES says so; an https instance can no longer be downgraded by an
unrelated missing field.

Because refusing to guess means the http case must be discoverable, NOTES now
prints a warning naming all three ways out - serve TLS from the ingress, tell
the chart the real scheme with webhookUrl, or opt in - and the README documents
annotation-terminated TLS next to the http concession. Branch selection verified
across six value combinations.

Also rejects a schemeless webhookUrl: `--set webhookUrl=n8n.example.com` used to
render N8N_WEBHOOK_URL "n8n.example.com/", a URL no caller can resolve, and the
missing scheme was what silently selected the http path.

secret.yaml still claimed a reinstall re-adopts the key and keeps working. That
was disproved on the cluster last round and corrected in the README and NOTES,
but not in the comment itself.
@jatintalgotra-zd

Copy link
Copy Markdown
Contributor

Re-reviewed at b590909. No major issues left.

The cookie inference is fixed the right way — insecureCookie is an explicit opt-in, never derived from a missing tlsSecretName. Verified the ALB/ACM repro no longer drops Secure, schemeless webhookUrl is rejected, and NOTES warns on the http path with all three ways out (checked across six value combinations).

No regressions: lint clean, 11 render paths pass, all 21 guarded env keys still rejected, tarball matches source and vendors postgres v0.0.14, index digest matches with all 182 prior entries intact.

Remaining minors, none blocking: N8N_LISTEN_ADDRESS: "0.0.0.0" narrows n8n's :: default and its comment says the opposite; no N8N_PROXY_HOPS / N8N_EDITOR_BASE_URL; terminationGracePeriodSeconds collides with n8n's 30s shutdown; N8nDown renders with no scrape target when metrics.enabled=false; no editDisabled in the schema.

Separately worth an issue on the postgres chart: it doesn't retain its root secret while Kubernetes retains its volume, which is what makes uninstall irreversible here.

Each verified against n8n 2.34.5's compiled config rather than its docs.

N8N_LISTEN_ADDRESS is no longer set. n8n's own default is `::`, which accepts
IPv4 and IPv6, so pinning 0.0.0.0 narrowed it to IPv4 for no gain - and the
comment claimed the opposite was true. Dropped from the owned list too, so a
node with IPv6 disabled can now set it through `env`.

N8N_PROXY_HOPS is exposed as proxyHops, defaulting to n8n's own 0. Per-client
rate limiting and audit logging need the real client IP out of X-Forwarded-For,
which is typically 1 hop behind an ingress - but the unsafe direction is upward,
since trusting more hops than exist lets a client spoof its address, so the
default stays low and the value is documented rather than inferred.

N8N_EDITOR_BASE_URL now follows the same public URL as the webhooks, so invite
and password-reset links cannot point somewhere the webhooks do not.

terminationGracePeriodSeconds: 40. n8n's graceful shutdown budget is 30s and
Kubernetes also defaults to 30s, so SIGKILL landed exactly when n8n expected to
finish draining.

N8nDown is gated on metrics.enabled. `up` only exists for a job Prometheus
scrapes, and this chart's ServiceMonitor creates it; with metrics off the series
is absent rather than 0, so the rule sat there looking like coverage while
providing none. N8nRestarting needs no gate - it reads kube-state-metrics.

editDisabled on persistence.size and image.repository, matching the convention
across the repo: every chart marks its disk size, and the two newest also mark
the image repository.
…offer

The schema described all 17 top-level values, which turned the zop.dev
configuration form into a wall of plumbing - the init container's image, the
subchart's database list, the encryption secret, the free-form env maps. It now
describes 5: image, ingress, persistence, timezone, resources.

This follows the charts that already got it right rather than the ones I copied.
openobserve-standalone declares 3 properties against 28 values keys, scylladb 3
against 7, redis and postgres 4 and 6 - all converging on image/version,
resources and disk. Nothing in the repo sets additionalProperties:false at the
root, so the values left out stay settable with --set or a values file; they are
simply not rendered.

Verified that the trimmed values still work end to end: postgres.services,
externalDatabase, encryptionKey.existingSecret, metrics, alerts, insecureCookie,
proxyHops, webhookUrl, env, waitForDatabase and service.type all still render,
and every validate.yaml guard still fires on them. The schema is the form
definition; templates/validate.yaml remains the correctness check.
Matches the other application charts, which get the button in
docs/readme-deploy-badges. Kept on this branch so the chart does not land
without it.
Matches the change to the other application charts.
docs/index.yaml conflicted again because both sides regenerated it wholesale -
main gained the qdrant chart (#310) and the README fixes (#319). Resolved the
same way: discard both versions and re-run

  helm repo index . --url https://helm.zop.dev

over the merged docs/ directory. Verified the only semantic change against main
is the added n8n v0.0.1 entry - 184 tarball digests match, nothing removed,
nothing altered, and qdrant v0.0.1 from main is intact.

Also adds n8n to the applications table in the top-level README. #319 landed
that table with a Deploy column while this branch was open, so without this the
chart would merge unlisted. Both of its links were checked live.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docs(site): helm.zop.dev renders integrations from a hardcoded config.js — new charts never appear

2 participants