feat: add n8n Helm chart for Kubernetes on Raspberry Pi (ARM64)
Helm Chart Release / release-chart (push) Successful in 4s

- n8nio/n8n 2.19.2 (multi-arch, linux/arm64 ready)
- Single-container deployment with persistent storage
- SQLite default / PostgreSQL option
- Basic auth, encryption key via Secret
- Ingress, HPA, PDB, NetworkPolicy support
- Gitea CI: weekly auto-update + release workflow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-02 09:37:35 +09:00
parent 4490781aec
commit 0b6b542f77
15 changed files with 1006 additions and 0 deletions
+104
View File
@@ -0,0 +1,104 @@
# .gitea/workflows/helm-release.yaml
# 汎用Helmチャート公開ワークフロー(全リポジトリ共通)
name: Helm Chart Release
on:
push:
branches:
- main
- master
workflow_dispatch:
env:
REGISTRY_URL: https://git.cafepieters.com
OWNER: helmchart
jobs:
release-chart:
runs-on: ubuntu-latest
steps:
- name: Checkout
run: |
echo "🔄 Cloning repository..."
git clone --depth 1 $GITHUB_SERVER_URL/$GITHUB_REPOSITORY.git .
echo "✅ Repository cloned"
- name: Install Helm
run: |
echo "📦 Installing Helm..."
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm version
echo "✅ Helm installed"
- name: Get Chart Info
id: chart_info
run: |
CHART_NAME=$(grep '^name:' Chart.yaml | awk '{print $2}')
CHART_VERSION=$(grep '^version:' Chart.yaml | awk '{print $2}')
echo "Chart Name: ${CHART_NAME}"
echo "Chart Version: ${CHART_VERSION}"
echo "CHART_NAME=${CHART_NAME}" >> $GITHUB_ENV
echo "CHART_VERSION=${CHART_VERSION}" >> $GITHUB_ENV
- name: Validate Chart
run: |
echo "🔍 Validating Helm chart..."
helm lint .
echo "✅ Chart validation passed"
- name: Package Chart
run: |
echo "📦 Packaging Helm chart..."
helm package .
CHART_FILE=$(ls *.tgz)
echo "✅ Packaged: ${CHART_FILE}"
echo "CHART_FILE=${CHART_FILE}" >> $GITHUB_ENV
- name: Publish to Gitea Package Registry
run: |
echo "🚀 Publishing ${CHART_FILE} to Gitea Package Registry..."
curl --fail-with-body \
-u "${{ secrets.REGISTRY_USER }}:${{ secrets.REGISTRY_TOKEN }}" \
-X POST \
--upload-file "${CHART_FILE}" \
"${REGISTRY_URL}/api/packages/${OWNER}/helm/api/charts"
echo "✅ Chart published successfully!"
- name: Summary
if: success()
run: |
echo "================================"
echo "✅ Deployment Successful!"
echo "================================"
echo "Repository: ${{ github.repository }}"
echo "Chart Name: ${CHART_NAME}"
echo "Chart Version: ${CHART_VERSION}"
echo "Chart File: ${CHART_FILE}"
echo "Branch: ${{ github.ref }}"
echo "Commit: ${{ github.sha }}"
echo "================================"
echo ""
echo "📦 Install with:"
echo "helm repo add cafepieters ${REGISTRY_URL}/api/packages/${OWNER}/helm"
echo "helm repo update"
echo "helm install my-${CHART_NAME} cafepieters/${CHART_NAME}"
- name: Error Report
if: failure()
run: |
echo "================================"
echo "❌ Deployment Failed!"
echo "================================"
echo "Check the logs above for details"
echo "Common issues:"
echo "- Missing Chart.yaml"
echo "- Invalid Helm chart structure"
echo "- Missing REGISTRY_USER or REGISTRY_TOKEN secrets"
echo "- Insufficient permissions on Personal Access Token"
echo "================================"
@@ -0,0 +1,197 @@
name: Update Docker Image Tags and Release Helm Chart
on:
schedule:
- cron: '0 3 * * 1' # 毎週月曜日 3:00 AM (JST 12:00 PM)
workflow_dispatch:
env:
REGISTRY_URL: https://git.cafepieters.com
OWNER: helmchart
jobs:
update-and-release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Install Helm
uses: azure/setup-helm@v3
with:
version: 'v3.12.0'
- name: Check jq availability
run: |
if ! command -v jq &> /dev/null; then
echo "Installing jq..."
apt-get update && apt-get install -y jq
fi
jq --version
- name: Check for new n8n version
id: n8n
run: |
set -e
echo "Checking n8n versions..."
CURRENT=$(grep "tag:" values.yaml | head -1 | sed 's/.*tag: *"\([^"]*\)".*/\1/' | tr -d ' ')
echo "Current n8n: $CURRENT"
LATEST=$(curl -s "https://registry.hub.docker.com/v2/repositories/n8nio/n8n/tags?page_size=100" | \
jq -r '.results[].name' | \
grep -E '^[0-9]+\.[0-9]+\.[0-9]+$' | \
sort -V | tail -1)
if [ -z "$LATEST" ]; then
echo "Warning: Could not fetch latest n8n version, using current"
LATEST="$CURRENT"
fi
echo "Latest n8n: $LATEST"
echo "current=$CURRENT" >> $GITHUB_OUTPUT
echo "latest=$LATEST" >> $GITHUB_OUTPUT
- name: Determine update and release conditions
id: check_update
run: |
set -e
N8N_CURRENT="${{ steps.n8n.outputs.current }}"
N8N_LATEST="${{ steps.n8n.outputs.latest }}"
echo "n8n: $N8N_CURRENT vs $N8N_LATEST"
UPDATE_NEEDED=false
RELEASE_NEEDED=false
if [ "$N8N_CURRENT" != "$N8N_LATEST" ]; then
UPDATE_NEEDED=true
RELEASE_NEEDED=true
echo "Update and release needed"
else
echo "Already up to date"
fi
echo "update_needed=$UPDATE_NEEDED" >> $GITHUB_OUTPUT
echo "release_needed=$RELEASE_NEEDED" >> $GITHUB_OUTPUT
- name: Update values.yaml
if: steps.check_update.outputs.update_needed == 'true'
run: |
set -e
echo "Updating values.yaml..."
N8N_OLD="${{ steps.n8n.outputs.current }}"
N8N_NEW="${{ steps.n8n.outputs.latest }}"
sed -i "s/tag: \"${N8N_OLD}\"/tag: \"${N8N_NEW}\"/" values.yaml
echo "n8n updated: $N8N_OLD -> $N8N_NEW"
echo "values.yaml updated"
git diff values.yaml
- name: Update Chart.yaml version
if: steps.check_update.outputs.release_needed == 'true'
run: |
set -e
APP_VERSION="${{ steps.n8n.outputs.latest }}"
sed -i "s/^version: .*/version: \"$APP_VERSION\"/" Chart.yaml
sed -i "s/^appVersion: .*/appVersion: \"$APP_VERSION\"/" Chart.yaml
echo "Chart.yaml updated to version $APP_VERSION"
cat Chart.yaml
- name: Commit changes
if: steps.check_update.outputs.update_needed == 'true'
run: |
git config user.name "Claude"
git config user.email "claude@cafepieters.com"
git add values.yaml Chart.yaml
git commit -m "chore: update n8n to ${{ steps.n8n.outputs.latest }}"
git push origin main
- name: Package Helm Chart
if: steps.check_update.outputs.release_needed == 'true'
run: |
helm package .
echo "Helm chart packaged"
- name: Create Git Tag
if: steps.check_update.outputs.release_needed == 'true'
run: |
APP_VERSION="${{ steps.n8n.outputs.latest }}"
if git rev-parse "v$APP_VERSION" >/dev/null 2>&1; then
echo "Tag v$APP_VERSION already exists, skipping"
else
git tag -a "v$APP_VERSION" -m "Release n8n $APP_VERSION"
git push origin "v$APP_VERSION"
echo "Git tag v$APP_VERSION created"
fi
- name: Create Gitea Release
if: steps.check_update.outputs.release_needed == 'true'
env:
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
run: |
APP_VERSION="${{ steps.n8n.outputs.latest }}"
CHART_NAME=$(grep '^name:' Chart.yaml | awk '{print $2}')
PACKAGE_FILE="${CHART_NAME}-${APP_VERSION}.tgz"
RELEASE_BODY="n8n Helm Chart v${APP_VERSION} - n8n: ${{ steps.n8n.outputs.latest }}"
EXISTING=$(curl -s \
-H "Authorization: token ${GITEA_TOKEN}" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases/tags/v${APP_VERSION}" | jq -r '.id // empty')
if [ -n "$EXISTING" ]; then
echo "Release v$APP_VERSION already exists (id=$EXISTING), skipping"
else
RELEASE_ID=$(curl -s -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\":\"v${APP_VERSION}\",\"name\":\"v${APP_VERSION}\",\"body\":\"${RELEASE_BODY}\"}" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases" | jq -r '.id')
curl -X POST \
-H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/gzip" \
--data-binary "@${PACKAGE_FILE}" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets?name=${PACKAGE_FILE}"
echo "Release v$APP_VERSION created with asset: ${PACKAGE_FILE}"
fi
- name: Publish to Gitea Package Registry
if: steps.check_update.outputs.release_needed == 'true'
run: |
CHART_NAME=$(grep '^name:' Chart.yaml | awk '{print $2}')
APP_VERSION="${{ steps.n8n.outputs.latest }}"
PACKAGE_FILE="${CHART_NAME}-${APP_VERSION}.tgz"
echo "Publishing ${PACKAGE_FILE} to Gitea Package Registry..."
curl --fail-with-body \
-u "${{ secrets.REGISTRY_USER }}:${{ secrets.REGISTRY_TOKEN }}" \
-X POST \
--upload-file "${PACKAGE_FILE}" \
"${REGISTRY_URL}/api/packages/${OWNER}/helm/api/charts"
echo "Chart published to registry successfully"
- name: Summary
run: |
APP_VERSION="${{ steps.n8n.outputs.latest }}"
UPDATE_NEEDED="${{ steps.check_update.outputs.update_needed }}"
RELEASE_NEEDED="${{ steps.check_update.outputs.release_needed }}"
echo "========================================"
if [ "$UPDATE_NEEDED" = "true" ]; then
echo "Update completed!"
else
echo "Already up to date, no changes."
fi
echo "========================================"
echo "n8n: ${APP_VERSION}"
if [ "$RELEASE_NEEDED" = "true" ]; then
echo "Chart Version: ${APP_VERSION} (released)"
echo "Registry: ${REGISTRY_URL}/api/packages/${OWNER}/helm"
else
echo "No release needed"
fi
echo "========================================"
+73
View File
@@ -0,0 +1,73 @@
# n8n Helm Chart - CLAUDE.md
## リポジトリ概要
n8n ワークフロー自動化ツールを Kubernetes 上にデプロイするHelmチャートです。
Raspberry Pi などのベアメタル上で動作する Kubernetes クラスタを想定した構成になっています。
`n8nio/n8n` 公式マルチアーキテクチャイメージ(linux/arm64 対応)を使用します。
## ARM 対応について
`n8nio/n8n` は linux/arm64 に対応したマルチアーキテクチャイメージです。
Raspberry Pi 4 以降(arm64)で動作します。
## Git 情報
- **ユーザー名**: Claude
- **メールアドレス**: claude@cafepieters.com
- **リポジトリ**: ssh://git@192.168.9.65/helmchart/n8n.git
## チャート構成
| リソース | 説明 |
|---|---|
| Deployment | n8n 本体(シングルコンテナ) |
| Service | LoadBalancer / ClusterIP(ポート 5678 |
| PVC | n8n データ(ワークフロー・認証情報・SQLite DB)永続化 |
| Secret | 暗号化キー・Basic認証パスワード・DBパスワード |
| Ingress | オプション(nginx ingress controller 対応) |
| HPA | オプション(※SQLiteモード時はスケールアウト非推奨) |
| PDB | Pod Disruption Budget |
| NetworkPolicy | オプション |
## データ永続化
n8n のデータは `/home/node/.n8n` に保存されます。
`persistence.enabled: true`(デフォルト)で PVC に永続化されます。
**persistence.enabled: false の場合、Pod 再起動でデータが失われます。**
## データベース
- デフォルト: SQLite`/home/node/.n8n/database.sqlite`
- 本番推奨: PostgreSQL`n8n.database.type: postgresdb`
## リリースフローのルール
### バージョン番号の方針
- Helmチャートのバージョン番号(`Chart.yaml``version` / `appVersion`)は、**n8n のバージョン番号と同一**とする。
### 自動リリース条件
- **n8n バージョン更新時**: `values.yaml``Chart.yaml` を更新し、Gitタグ・Giteaリリース・Gitea Package Registry への発行まで行う。
### 手動リリース(臨時)
- 改修作業などで手動リリースが必要な場合は、バージョン末尾にアルファベットを付与する。
- 例: `2.19.2``2.19.2-a`, `2.19.2-b`
## ワークフロー構成
### `.gitea/workflows/image-update-and-release.yaml`
毎週月曜日 3:00 AMJST 12:00 PM)に自動実行され、以下を行う:
1. Docker Hub から n8n の最新バージョンを取得
2. 更新がある場合は `values.yaml``Chart.yaml` を更新
3. Gitタグ・Giteaリリース・Gitea Package Registry への発行を実施
### `.gitea/workflows/helm-release.yaml`
`main` ブランチへのプッシュ時に自動実行。Gitea Package Registry にチャートを発行する。
## 必要な Gitea Secrets
| シークレット名 | 用途 |
|---|---|
| `GITEA_TOKEN` | Gitea API(リリース作成・タグ操作) |
| `REGISTRY_USER` | Gitea Package Registry ユーザー名 |
| `REGISTRY_TOKEN` | Gitea Package Registry トークン |
+22
View File
@@ -0,0 +1,22 @@
apiVersion: v2
name: n8n
description: A Helm chart for n8n workflow automation on Kubernetes (ARM/Raspberry Pi ready)
type: application
version: "2.19.2"
appVersion: "2.19.2"
keywords:
- n8n
- workflow
- automation
- integration
maintainers:
- name: Pieter
url: https://git.cafepieters.com/helmchart/repo/
home: https://n8n.io/
sources:
- https://github.com/n8n-io/n8n
icon: https://n8n.io/favicon.ico
kubeVersion: ">=1.19.0-0"
annotations:
category: Automation
licenses: Sustainable Use License
+74
View File
@@ -0,0 +1,74 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "n8n.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "n8n.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 "n8n.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "n8n.labels" -}}
helm.sh/chart: {{ include "n8n.chart" . }}
{{ include "n8n.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "n8n.selectorLabels" -}}
app.kubernetes.io/name: {{ include "n8n.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "n8n.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "n8n.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}
{{/*
Name of the secret holding the encryption key and auth credentials
*/}}
{{- define "n8n.secretName" -}}
{{- .Values.n8n.existingSecret | default (include "n8n.fullname" .) }}
{{- end }}
{{/*
Name of the PVC for n8n data
*/}}
{{- define "n8n.pvcName" -}}
{{- .Values.persistence.existingClaim | default (include "n8n.fullname" .) }}
{{- end }}
+149
View File
@@ -0,0 +1,149 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "n8n.fullname" . }}
labels:
{{- include "n8n.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "n8n.selectorLabels" . | nindent 6 }}
strategy:
type: Recreate
template:
metadata:
annotations:
checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "n8n.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "n8n.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: n8n
securityContext:
{{- toYaml .Values.securityContext | nindent 10 }}
image: "{{ .Values.image.registry }}/{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: http
containerPort: 5678
protocol: TCP
env:
- name: N8N_PORT
value: "5678"
- name: N8N_PROTOCOL
value: {{ .Values.n8n.protocol | quote }}
- name: N8N_HOST
value: {{ .Values.n8n.host | quote }}
{{- if .Values.n8n.webhookUrl }}
- name: WEBHOOK_URL
value: {{ .Values.n8n.webhookUrl | quote }}
{{- end }}
- name: GENERIC_TIMEZONE
value: {{ .Values.n8n.timezone | quote }}
- name: N8N_LOG_LEVEL
value: {{ .Values.n8n.logLevel | quote }}
- name: N8N_USER_FOLDER
value: "/home/node/.n8n"
- name: N8N_ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: {{ include "n8n.secretName" . }}
key: encryption-key
{{- if .Values.n8n.basicAuth.enabled }}
- name: N8N_BASIC_AUTH_ACTIVE
value: "true"
- name: N8N_BASIC_AUTH_USER
value: {{ .Values.n8n.basicAuth.user | quote }}
- name: N8N_BASIC_AUTH_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.n8n.basicAuth.existingSecret | default (include "n8n.fullname" .) }}
key: {{ .Values.n8n.basicAuth.passwordKey }}
{{- end }}
- name: EXECUTIONS_DATA_PRUNE
value: {{ .Values.n8n.executions.pruneData | quote }}
- name: EXECUTIONS_DATA_MAX_AGE
value: {{ .Values.n8n.executions.pruneDataMaxAge | quote }}
- name: EXECUTIONS_DATA_MAX_COUNT
value: {{ .Values.n8n.executions.pruneDataMaxCount | quote }}
- name: DB_TYPE
value: {{ .Values.n8n.database.type | quote }}
{{- if eq .Values.n8n.database.type "postgresdb" }}
- name: DB_POSTGRESDB_HOST
value: {{ .Values.n8n.database.postgresdb.host | quote }}
- name: DB_POSTGRESDB_PORT
value: {{ .Values.n8n.database.postgresdb.port | quote }}
- name: DB_POSTGRESDB_DATABASE
value: {{ .Values.n8n.database.postgresdb.database | quote }}
- name: DB_POSTGRESDB_USER
value: {{ .Values.n8n.database.postgresdb.user | quote }}
- name: DB_POSTGRESDB_PASSWORD
valueFrom:
secretKeyRef:
name: {{ .Values.n8n.database.postgresdb.existingSecret | default (include "n8n.fullname" .) }}
key: {{ .Values.n8n.database.postgresdb.passwordKey }}
{{- end }}
{{- range $key, $val := .Values.n8n.extraEnv }}
- name: {{ $key }}
value: {{ $val | quote }}
{{- end }}
{{- if .Values.livenessProbe.enabled }}
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.livenessProbe.failureThreshold }}
successThreshold: {{ .Values.livenessProbe.successThreshold }}
{{- end }}
{{- if .Values.readinessProbe.enabled }}
readinessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
failureThreshold: {{ .Values.readinessProbe.failureThreshold }}
successThreshold: {{ .Values.readinessProbe.successThreshold }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 10 }}
volumeMounts:
- name: data
mountPath: /home/node/.n8n
volumes:
- name: data
{{- if .Values.persistence.enabled }}
persistentVolumeClaim:
claimName: {{ include "n8n.pvcName" . }}
{{- else }}
emptyDir: {}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
+32
View File
@@ -0,0 +1,32 @@
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "n8n.fullname" . }}
labels:
{{- include "n8n.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "n8n.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}
+35
View File
@@ -0,0 +1,35 @@
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "n8n.fullname" . }}
labels:
{{- include "n8n.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if .Values.ingress.className }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- toYaml .Values.ingress.tls | nindent 4 }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "n8n.fullname" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}
+22
View File
@@ -0,0 +1,22 @@
{{- if .Values.networkPolicy.enabled }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: {{ include "n8n.fullname" . }}
labels:
{{- include "n8n.labels" . | nindent 4 }}
spec:
podSelector:
matchLabels:
{{- include "n8n.selectorLabels" . | nindent 6 }}
policyTypes:
{{- toYaml .Values.networkPolicy.policyTypes | nindent 4 }}
{{- if .Values.networkPolicy.ingress }}
ingress:
{{- toYaml .Values.networkPolicy.ingress | nindent 4 }}
{{- end }}
{{- if .Values.networkPolicy.egress }}
egress:
{{- toYaml .Values.networkPolicy.egress | nindent 4 }}
{{- end }}
{{- end }}
+18
View File
@@ -0,0 +1,18 @@
{{- if .Values.podDisruptionBudget.enabled }}
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: {{ include "n8n.fullname" . }}
labels:
{{- include "n8n.labels" . | nindent 4 }}
spec:
selector:
matchLabels:
{{- include "n8n.selectorLabels" . | nindent 6 }}
{{- if .Values.podDisruptionBudget.minAvailable }}
minAvailable: {{ .Values.podDisruptionBudget.minAvailable }}
{{- end }}
{{- if .Values.podDisruptionBudget.maxUnavailable }}
maxUnavailable: {{ .Values.podDisruptionBudget.maxUnavailable }}
{{- end }}
{{- end }}
+21
View File
@@ -0,0 +1,21 @@
{{- if and .Values.persistence.enabled (not (.Values.persistence.existingClaim)) -}}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "n8n.fullname" . }}
labels:
{{- include "n8n.labels" . | nindent 4 }}
{{- with .Values.persistence.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
accessModes:
- {{ .Values.persistence.accessMode }}
resources:
requests:
storage: {{ .Values.persistence.size }}
{{- if .Values.persistence.storageClass }}
storageClassName: {{ .Values.persistence.storageClass }}
{{- end }}
{{- end }}
+19
View File
@@ -0,0 +1,19 @@
{{- if not .Values.n8n.existingSecret -}}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "n8n.fullname" . }}
labels:
{{- include "n8n.labels" . | nindent 4 }}
annotations:
helm.sh/resource-policy: keep
type: Opaque
data:
encryption-key: {{ .Values.n8n.encryptionKey | default (randAlphaNum 32) | b64enc | quote }}
{{- if and .Values.n8n.basicAuth.enabled (not .Values.n8n.basicAuth.existingSecret) }}
basic-auth-password: {{ .Values.n8n.basicAuth.password | default (randAlphaNum 16) | b64enc | quote }}
{{- end }}
{{- if and (eq .Values.n8n.database.type "postgresdb") (not .Values.n8n.database.postgresdb.existingSecret) }}
postgres-password: {{ .Values.n8n.database.postgresdb.password | b64enc | quote }}
{{- end }}
{{- end }}
+19
View File
@@ -0,0 +1,19 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "n8n.fullname" . }}
labels:
{{- include "n8n.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: {{ .Values.service.targetPort }}
protocol: TCP
name: http
selector:
{{- include "n8n.selectorLabels" . | nindent 4 }}
+12
View File
@@ -0,0 +1,12 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "n8n.serviceAccountName" . }}
labels:
{{- include "n8n.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
+209
View File
@@ -0,0 +1,209 @@
# Default values for n8n
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
registry: docker.io
repository: n8nio/n8n
tag: "2.19.2"
pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
create: true
annotations: {}
name: ""
podAnnotations: {}
podSecurityContext:
fsGroup: 1000
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
readOnlyRootFilesystem: false
runAsNonRoot: true
runAsUser: 1000
service:
type: LoadBalancer
# type: ClusterIP
port: 5678
targetPort: 5678
annotations: {}
ingress:
enabled: false
className: "nginx"
annotations: {}
# {
# acme.cert-manager.io/http01-ingress-class: "nginx",
# cert-manager.io/cluster-issuer: "letsencrypt-issuer",
# nginx.ingress.kubernetes.io/from-to-www-redirect: "true",
# nginx.ingress.kubernetes.io/proxy-body-size: "100m"
# }
hosts:
- host: n8n.local
paths:
- path: /
pathType: Prefix
tls: []
# - secretName: n8n-tls
# hosts:
# - n8n.local
# Resource limits suitable for Raspberry Pi
resources:
limits:
cpu: 1000m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 3
targetCPUUtilizationPercentage: 80
targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- n8n
topologyKey: kubernetes.io/hostname
# n8n specific configuration
n8n:
# Encryption key for stored credentials (auto-generated if not provided)
encryptionKey: ""
existingSecret: ""
# Host and protocol settings (used for webhook URLs)
host: "n8n.local"
protocol: "http"
# webhookUrl: "https://n8n.example.com/"
# Timezone
timezone: "Asia/Tokyo"
# Log level: error, warn, info, verbose, debug
logLevel: "info"
# Basic authentication
basicAuth:
enabled: false
user: "admin"
password: ""
existingSecret: ""
passwordKey: "basic-auth-password"
# Execution data pruning
executions:
pruneData: true
pruneDataMaxAge: 336 # hours (14 days)
pruneDataMaxCount: 10000
# Database configuration
database:
# type: sqlite (default) or postgresdb
type: "sqlite"
# PostgreSQL settings (used when type=postgresdb)
postgresdb:
host: "postgres.default.svc.cluster.local"
port: 5432
database: "n8n"
user: ""
password: ""
existingSecret: ""
passwordKey: "postgres-password"
# Extra environment variables
extraEnv: {}
# extraEnv:
# N8N_METRICS: "true"
# N8N_DIAGNOSTICS_ENABLED: "false"
# Persistent storage for n8n data (workflows, credentials, sqlite DB)
persistence:
enabled: true
storageClass: ""
accessMode: ReadWriteOnce
size: 5Gi
annotations: {}
# existingClaim: ""
# Liveness and readiness probes
livenessProbe:
enabled: true
initialDelaySeconds: 60
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 6
successThreshold: 1
readinessProbe:
enabled: true
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
# Network Policy
networkPolicy:
enabled: false
policyTypes:
- Ingress
- Egress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 5678
egress:
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 443
- protocol: TCP
port: 80
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53
# Pod Disruption Budget
podDisruptionBudget:
enabled: true
minAvailable: 1