Skip to content
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

Next Gen Chart #132

Merged
merged 16 commits into from
Feb 14, 2025
921 changes: 673 additions & 248 deletions README.md

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions charts/n8n/Chart.lock
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
dependencies:
- name: redis
repository: https://charts.bitnami.com/bitnami
version: 18.6.1
digest: sha256:679512a5d6167cd529b9b6d861a6605f62683c3497b8f920fc344dd00bf0ba82
generated: "2024-06-30T21:39:40.024252056+02:00"
- name: valkey
repository: oci://registry-1.docker.io/bitnamicharts
version: 2.2.3
digest: sha256:3bed83ffaec482e8e31eae77f966ecbd95258b6124ced0824d825805dc23e197
generated: "2025-01-18T14:00:37.663128+01:00"
14 changes: 7 additions & 7 deletions charts/n8n/Chart.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
apiVersion: v2
name: n8n
version: 0.25.2
appVersion: 1.62.5
version: 1.0.0
appVersion: 1.76.1
type: application
description: "A Kubernetes Helm chart for n8n a free and open fair-code licensed node based Workflow Automation Tool. Easily automate tasks across different services."
home: https://github.com/8gears/n8n-helm-chart
Expand All @@ -25,13 +25,13 @@ maintainers:
url: https://github.com/n8n-io

dependencies:
- name: redis
version: 18.6.1
repository: https://charts.bitnami.com/bitnami
- name: valkey
version: 2.2.3
repository: oci://registry-1.docker.io/bitnamicharts
condition: redis.enabled
Comment on lines +28 to 31
Copy link

Choose a reason for hiding this comment

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

💡 Codebase verification

⚠️ Potential issue

⚠️ Review comment is correct and reveals additional concerns

The verification confirms both issues and reveals more concerns:

  • Valkey is not advertised as a Redis-compatible replacement but a separate key/value store
  • The redis.enabled condition is misleading and should be valkey.enabled
  • Migration documentation is crucial as this is not a drop-in replacement
  • The entire dependency change from Redis to Valkey needs architectural review as they are different products
🔗 Analysis chain

⚠️ Valkey integration requires additional work

The Redis to Valkey migration has several issues:

  1. The condition redis.enabled should be renamed to valkey.enabled
  2. Migration documentation is needed
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check Valkey's compatibility with Redis
curl -s "https://raw.githubusercontent.com/bitnami/charts/main/bitnami/valkey/values.yaml" | head -n 50

Length of output: 2117


Script:

#!/bin/bash
# Search for Redis-related content in Valkey's full values.yaml and README
echo "=== Checking values.yaml for Redis mentions ==="
curl -s "https://raw.githubusercontent.com/bitnami/charts/main/bitnami/valkey/values.yaml" | grep -i "redis" || echo "No Redis mentions found"

echo -e "\n=== Checking README for Redis compatibility or migration info ==="
curl -s "https://raw.githubusercontent.com/bitnami/charts/main/bitnami/valkey/README.md" | grep -i -A 5 -B 5 "redis" || echo "No Redis mentions found"

echo -e "\n=== Checking if Valkey is meant to be Redis compatible ==="
curl -s "https://raw.githubusercontent.com/bitnami/charts/main/bitnami/valkey/Chart.yaml" | grep -i "description" -A 2

Length of output: 2656

annotations:
artifacthub.io/prerelease: "false"
artifacthub.io/prerelease: "true"
# supported kinds are added, changed, deprecated, removed, fixed and security.
artifacthub.io/changes: |
- kind: changed
description: "Updated application to version 1.62.5"
description: "Major chart redesign and also update to n8n version 1.74.1. This version is not compatible with the previous version. Please read the release notes for more details."
8 changes: 4 additions & 4 deletions charts/n8n/templates/NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ . }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
{{- else if contains "NodePort" (default "ClusterIP" .Values.main.service.type) }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "n8n.fullname" . }})
export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
{{- else if contains "LoadBalancer" .Values.service.type }}
{{- else if contains "LoadBalancer" (default "ClusterIP" .Values.main.service.type) }}
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "n8n.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "n8n.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.service.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
echo http://$SERVICE_IP:{{ .Values.main.service.port }}
{{- else if contains "ClusterIP" (default "ClusterIP" .Values.main.service.type) }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "n8n.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}")
echo "Visit http://127.0.0.1:8080 to use your application"
Expand Down
108 changes: 33 additions & 75 deletions charts/n8n/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -50,92 +50,50 @@ app.kubernetes.io/name: {{ include "n8n.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

{{/*
Selector labels
*/}}
{{- define "n8n.deploymentPodEnvironments" -}}
{{- range $key, $value := .Values.extraEnv }}
- name: {{ $key }}
value: {{ $value | quote}}
{{ end }}
{{- range $key, $value := .Values.extraEnvSecrets }}
- name: {{ $key }}
valueFrom:
secretKeyRef:
name: {{ $value.name | quote }}
key: {{ $value.key | quote }}
{{ end }}
- name: "N8N_PORT" #! we better set the port once again as ENV Var, see: https://community.n8n.io/t/default-config-is-not-set-or-the-port-to-be-more-precise/3158/3?u=vad1mo
value: {{ get .Values.config "port" | default "5678" | quote }}
{{- if .Values.n8n.encryption_key }}
- name: "N8N_ENCRYPTION_KEY"
valueFrom:
secretKeyRef:
key: N8N_ENCRYPTION_KEY
name: {{ include "n8n.fullname" . }}
{{- end }}
{{- if or .Values.config .Values.secret }}
- name: "N8N_CONFIG_FILES"
value: {{ include "n8n.configFiles" . | quote }}
{{ end }}
{{- if .Values.scaling.enabled }}
- name: "QUEUE_BULL_REDIS_HOST"
{{- if .Values.scaling.redis.host }}
value: "{{ .Values.scaling.redis.host }}"
{{ else }}
value: "{{ .Release.Name }}-redis-master"
{{ end }}
- name: "EXECUTIONS_MODE"
value: "queue"
{{ end }}
{{- if .Values.scaling.redis.password }}
- name: "QUEUE_BULL_REDIS_PASSWORD"
value: "{{ .Values.scaling.redis.password }}"
{{ end }}
{{- if .Values.scaling.webhook.enabled }}
- name: "N8N_DISABLE_PRODUCTION_MAIN_PROCESS"
value: "true"
{{ end }}
{{- end }}

{{/*
Create the name of the service account to use
*/}}
{{/* Create the name of the service account to use */}}
{{- define "n8n.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "n8n.fullname" .) .Values.serviceAccount.name }}
{{- if .Values.main.serviceAccount.create }}
{{- default (include "n8n.fullname" .) .Values.main.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- default "default" .Values.main.serviceAccount.name }}
{{- end }}

{{/* Create a list of config files for n8n */}}
{{- define "n8n.configFiles" -}}
{{- $conf_val := "" }}
{{- $sec_val := "" }}
{{- $separator := "" }}
{{- if .Values.config }}
{{- $conf_val = "/n8n-config/config.json" }}
{{- end }}
{{- if or .Values.secret .Values.existingSecret }}
{{- $sec_val = "/n8n-secret/secret.json" }}
{{- end }}
{{- if and .Values.config (or .Values.secret .Values.existingSecret) }}
{{- $separator = "," }}
{{- end }}
{{- print $conf_val $separator $sec_val }}
{{- end }}


{{/* PVC existing, emptyDir, Dynamic */}}
{{- define "n8n.pvc" -}}
{{- if or (not .Values.persistence.enabled) (eq .Values.persistence.type "emptyDir") -}}
{{- if or (not .Values.main.persistence.enabled) (eq .Values.main.persistence.type "emptyDir") -}}
emptyDir: {}
{{- else if and .Values.persistence.enabled .Values.persistence.existingClaim -}}
{{- else if and .Values.main.persistence.enabled .Values.main.persistence.existingClaim -}}
persistentVolumeClaim:
claimName: {{ .Values.persistence.existingClaim }}
{{- else if and .Values.persistence.enabled (eq .Values.persistence.type "dynamic") -}}
claimName: {{ .Values.main.persistence.existingClaim }}
{{- else if and .Values.main.persistence.enabled (eq .Values.main.persistence.type "dynamic") -}}
persistentVolumeClaim:
claimName: {{ include "n8n.fullname" . }}
{{- end }}
{{- end }}


{{/* Create environment variables from yaml tree */}}
{{- define "toEnvVars" -}}
{{- $prefix := "" }}
{{- if .prefix }}
{{- $prefix = printf "%s_" .prefix }}
{{- end }}
{{- range $key, $value := .values }}
{{- if kindIs "map" $value -}}
{{- dict "values" $value "prefix" (printf "%s%s" $prefix ($key | upper)) "isSecret" $.isSecret | include "toEnvVars" -}}
{{- else -}}
{{- if $.isSecret -}}
{{ $prefix }}{{ $key | upper }}: {{ $value | b64enc }}{{ "\n" }}
{{- else -}}
{{ $prefix }}{{ $key | upper }}: {{ $value | quote }}{{ "\n" }}
{{- end -}}
{{- end -}}
{{- end -}}
{{- end }}





8 changes: 3 additions & 5 deletions charts/n8n/templates/configmap.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
{{- if .Values.config }}
{{- if .Values.main.config }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ include "n8n.fullname" . }}
name: app-config-{{ include "n8n.fullname" . }}
labels:
{{- include "n8n.labels" . | nindent 4 }}
data:
config.json: |
{{ .Values.config | toPrettyJson | indent 4 }}

{{- include "toEnvVars" (dict "values" .Values.main.config "prefix" "") | nindent 2 }}
{{- end }}
100 changes: 51 additions & 49 deletions charts/n8n/templates/deployment.webhooks.yaml
Original file line number Diff line number Diff line change
@@ -1,104 +1,106 @@
{{- if .Values.scaling.webhook.enabled }}
{{- if .Values.webhook.enabled }}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "n8n.fullname" . }}-webhook
labels:
{{- include "n8n.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.scaling.webhook.count }}
{{- if not .Values.webhook.autoscaling.enabled }}
replicas: {{ .Values.webhook.replicaCount }}
{{- end }}
strategy:
type: {{ .Values.deploymentStrategy.type }}
{{- if eq .Values.deploymentStrategy.type "RollingUpdate" }}
type: {{ .Values.webhook.deploymentStrategy.type }}
{{- if eq .Values.webhook.deploymentStrategy.type "RollingUpdate" }}
rollingUpdate:
maxSurge: {{ default "25%" .Values.deploymentStrategy.maxSurge }}
maxUnavailable: {{ default "25%" .Values.deploymentStrategy.maxUnavailable }}
maxSurge: {{ default "25%" .Values.webhook.deploymentStrategy.maxSurge }}
maxUnavailable: {{ default "25%" .Values.webhook.deploymentStrategy.maxUnavailable }}
{{- end }}
selector:
matchLabels:
{{- include "n8n.selectorLabels" . | nindent 6 }}
app.kubernetes.io/type: webhooks
app.kubernetes.io/type: webhook
template:
metadata:
annotations:
checksum/config: {{ print .Values | sha256sum }}
{{- with .Values.podAnnotations }}
{{- with .Values.webhook.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "n8n.selectorLabels" . | nindent 8 }}
app.kubernetes.io/type: webhooks
{{- if .Values.podLabels }}
{{ toYaml .Values.podLabels | nindent 8 }}
app.kubernetes.io/type: webhook
{{- if .Values.webhook.podLabels }}
{{ toYaml .Values.webhook.podLabels | nindent 8 }}
{{- end }}

spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "n8n.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
{{- if .Values.initContainers }}
{{- toYaml .Values.webhook.podSecurityContext | nindent 8 }}
{{- if .Values.webhook.initContainers }}
initContainers:
{{ tpl (toYaml .Values.initContainers) . | nindent 8 }}
{{ tpl (toYaml .Values.webhook.initContainers) . | nindent 8 }}
{{- end }}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add default security context for enhanced security.

Consider adding default security context values to enforce running as non-root user, which is a Kubernetes security best practice.

securityContext:
+ {{- $defaultSecurityContext := dict "runAsNonRoot" true "runAsUser" 1000 "runAsGroup" 1000 }}
+ {{- if .Values.webhook.podSecurityContext }}
  {{- toYaml .Values.webhook.podSecurityContext | nindent 8 }}
+ {{- else }}
+ {{- toYaml $defaultSecurityContext | nindent 8 }}
+ {{- end }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{{- toYaml .Values.webhook.podSecurityContext | nindent 8 }}
{{- if .Values.webhook.initContainers }}
initContainers:
{{ tpl (toYaml .Values.initContainers) . | nindent 8 }}
{{ tpl (toYaml .Values.webhook.initContainers) . | nindent 8 }}
{{- end }}
{{- $defaultSecurityContext := dict "runAsNonRoot" true "runAsUser" 1000 "runAsGroup" 1000 }}
{{- if .Values.webhook.podSecurityContext }}
{{- toYaml .Values.webhook.podSecurityContext | nindent 8 }}
{{- else }}
{{- toYaml $defaultSecurityContext | nindent 8 }}
{{- end }}
{{- if .Values.webhook.initContainers }}
initContainers:
{{ tpl (toYaml .Values.webhook.initContainers) . | nindent 8 }}
{{- end }}

containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
- name: {{ .Chart.Name }}-webhook
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
{{- if .Values.webhook.command }}
command:
{{- toYaml .Values.webhook.command | nindent 12 }}
{{- else }}
command: ["n8n"]
args: ["webhook"]
env:
{{- include "n8n.deploymentPodEnvironments" . | nindent 12 }}
{{- end }}
{{- if .Values.webhook.commandArgs }}
args:
{{- toYaml .Values.webhook.commandArgs | nindent 12 }}
{{- else }}
args:
- "webhook"
{{- end }}
ports:
- name: http
containerPort: {{ get .Values.config "port" | default 5678 }}
containerPort: {{ get .Values.main.config "port" | default 5678 }}
protocol: TCP
{{- with .Values.webhook.livenessProbe }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.webhook.readinessProbe }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add default probe configurations.

Consider adding default liveness and readiness probe configurations to ensure proper health checking of the webhook component.

          {{- with .Values.webhook.livenessProbe }}
          livenessProbe:
            {{- toYaml . | nindent 12 }}
+         {{- else }}
+         livenessProbe:
+           httpGet:
+             path: /healthz
+             port: http
+           initialDelaySeconds: 30
+           periodSeconds: 10
          {{- end }}
          {{- with .Values.webhook.readinessProbe }}
          readinessProbe:
            {{- toYaml . | nindent 12 }}
+         {{- else }}
+         readinessProbe:
+           httpGet:
+             path: /healthz
+             port: http
+           initialDelaySeconds: 5
+           periodSeconds: 10
          {{- end }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{{- with .Values.webhook.livenessProbe }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.webhook.readinessProbe }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- end }}
{{- with .Values.webhook.livenessProbe }}
livenessProbe:
{{- toYaml . | nindent 12 }}
{{- else }}
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 30
periodSeconds: 10
{{- end }}
{{- with .Values.webhook.readinessProbe }}
readinessProbe:
{{- toYaml . | nindent 12 }}
{{- else }}
readinessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 5
periodSeconds: 10
{{- end }}

envFrom:
- configMapRef:
name: {{ include "n8n.fullname" . }}
- secretRef:
name: {{ include "n8n.fullname" . }}
lifecycle:
{{- toYaml .Values.webhook.lifecycle | nindent 12 }}
securityContext:
{{- toYaml .Values.webhook.securityContext | nindent 12 }}
resources:
{{- toYaml .Values.webhookResources | nindent 12 }}
{{- toYaml .Values.webhook.resources | nindent 12 }}
volumeMounts:
- name: data
mountPath: {{ if semverCompare ">=1.0" (.Values.image.tag | default .Chart.AppVersion) }}/home/node/.n8n{{ else }}/root/.n8n{{ end }}
{{- if .Values.config }}
- name: config-volume
mountPath: /n8n-config
{{- end }}
{{- if or (.Values.secret) (.Values.existingSecret) }}
- name: secret-volume
mountPath: /n8n-secret
{{- end }}
{{- with .Values.nodeSelector }}
mountPath: /home/node/.n8n
{{- with .Values.webhook.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
{{- with .Values.webhook.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
{{- with .Values.webhook.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
- name: "data"
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add default resource limits.

Consider adding default resource limits to prevent unbounded resource consumption and ensure proper scheduling.

          resources:
+           {{- $defaultResources := dict "requests" (dict "cpu" "100m" "memory" "128Mi") "limits" (dict "cpu" "500m" "memory" "256Mi") }}
+           {{- if .Values.webhook.resources }}
            {{- toYaml .Values.webhook.resources | nindent 12 }}
+           {{- else }}
+           {{- toYaml $defaultResources | nindent 12 }}
+           {{- end }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{{- toYaml .Values.webhook.resources | nindent 12 }}
volumeMounts:
- name: data
mountPath: {{ if semverCompare ">=1.0" (.Values.image.tag | default .Chart.AppVersion) }}/home/node/.n8n{{ else }}/root/.n8n{{ end }}
{{- if .Values.config }}
- name: config-volume
mountPath: /n8n-config
{{- end }}
{{- if or (.Values.secret) (.Values.existingSecret) }}
- name: secret-volume
mountPath: /n8n-secret
{{- end }}
{{- with .Values.nodeSelector }}
mountPath: /home/node/.n8n
{{- with .Values.webhook.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
{{- with .Values.webhook.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
{{- with .Values.webhook.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
- name: "data"
{{- $defaultResources := dict "requests" (dict "cpu" "100m" "memory" "128Mi") "limits" (dict "cpu" "500m" "memory" "256Mi") }}
{{- if .Values.webhook.resources }}
{{- toYaml .Values.webhook.resources | nindent 12 }}
{{- else }}
{{- toYaml $defaultResources | nindent 12 }}
{{- end }}
volumeMounts:
- name: data
mountPath: /home/node/.n8n
{{- with .Values.webhook.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.webhook.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.webhook.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
- name: "data"

{{ include "n8n.pvc" . }}
{{- if .Values.config }}
- name: config-volume
configMap:
name: {{ include "n8n.fullname" . }}
{{- end }}
{{- if or (.Values.secret) (.Values.existingSecret) }}
- name: secret-volume
secret:
secretName: {{ if .Values.existingSecret }}{{ .Values.existingSecret }}{{ else }}{{ include "n8n.fullname" . }}{{ end }}
items:
- key: "secret.json"
path: "secret.json"
{{- end }}
{{- end }}
Loading