Skip to content

Add support for traefik as an ingress#140

Open
gml3ff wants to merge 4 commits into
n8n-io:mainfrom
gml3ff:main
Open

Add support for traefik as an ingress#140
gml3ff wants to merge 4 commits into
n8n-io:mainfrom
gml3ff:main

Conversation

@gml3ff

@gml3ff gml3ff commented May 20, 2026

Copy link
Copy Markdown
Contributor

Pull Request

Description

As announced on November 11, 2025, Kubernetes has deprecated the Nginx ingress. To account for this change, this PR adds support for using Traefik instead.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Example/configuration update
  • CI/CD improvements

Related Issues

Fixes # (issue)
Relates to # (issue)

Changes Made

  • Adds support for traefik by adding annotations to the main service to support sticky sessions
  • Allows user to choose between traefik and nginx in the values.yaml
  • Makes nginx annotations only appear if nginx is the selected ingress option

Testing Performed

  • Tested on minikube; session stickiness verified by viewing the Traefik dashboard and curling status endpoints.

Chart Validation

  • helm lint charts/n8n passes
  • ./scripts/validate-examples.sh passes
  • Template rendering works with all examples

Deployment Testing (if applicable)

  • Tested with minimal configuration
  • Tested with production configuration
  • Tested upgrade path from previous version
  • All pods start successfully
  • Application is accessible

Specific Testing for Changes

Describe any specific testing you performed for your changes:

  • Ran with options for nginx and traefik on minikube to verify session stickiness

Breaking Changes

If this includes breaking changes, describe what they are and provide migration instructions:

  • None, backwards compatibility is maintained

Documentation Updates

  • Updated Chart.yaml version (if needed)
  • Updated CHANGELOG.md
  • Updated README.md (if needed)
  • Updated examples (if needed)
  • Updated CONTRIBUTING.md (if needed)

Checklist

  • My code follows the project's style guidelines
  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings or errors
  • I have added examples that demonstrate the changes (if applicable)
  • All new and existing tests pass

Screenshots (if applicable)

Add screenshots to help explain your changes.

Additional Notes

Any additional information that reviewers should know.

@cubic-dev-ai cubic-dev-ai Bot 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.

3 issues found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="charts/n8n/templates/service-main.yaml">

<violation number="1" location="charts/n8n/templates/service-main.yaml:8">
P2: Traefik backend detection misses the common annotation-based class (`kubernetes.io/ingress.class: traefik`), so sticky-session service annotations may not render for valid Traefik configurations.</violation>
</file>

<file name="charts/n8n/values.yaml">

<violation number="1" location="charts/n8n/values.yaml:159">
P1: Using `className` to conditionally render annotations breaks sticky sessions for users with default or custom NGINX ingress class names.</violation>

<violation number="2" location="charts/n8n/values.yaml:164">
P2: The comment incorrectly claims `cookieMaxAge` is ignored by Traefik, leading to an unimplemented feature.</violation>
</file>
Architecture diagram
sequenceDiagram
    participant Client as External Client
    participant TE as Traefik Ingress
    participant NE as Nginx Ingress
    participant Svc as n8n Service
    participant Pod as n8n Pod

    Note over Client,Pod: Ingress Selection Flow (values.yaml: ingress.className)

    alt Traefik Ingress Selected
        Client->>TE: Request to n8n
        TE->>Svc: Route request
        Note over Svc: Service annotations include:<br/>traefik.ingress.kubernetes.io/service.sticky.cookie=true
        Svc->>Pod: Forward to pod (sticky session via cookie)
        Pod-->>Svc: Response
        Svc-->>TE: Response with Set-Cookie
        TE-->>Client: Response + sticky cookie
    else Nginx Ingress Selected (default/backward compatible)
        Client->>NE: Request to n8n
        Note over NE: Ingress annotations include:<br/>nginx.ingress.kubernetes.io/affinity: cookie
        NE->>Svc: Route request with cookie affinity
        Note over Svc: No Traefik annotations added
        Svc->>Pod: Forward to pod (sticky session)
        Pod-->>Svc: Response
        Svc-->>NE: Response with Set-Cookie
        NE-->>Client: Response + sticky cookie
    end

    Note over Svc: Key configuration decisions:<br/>- sticky.enabled controls both ingress types<br/>- cookieName shared between Traefik/nginx<br/>- cookieExpires/cookieMaxAge only for nginx<br/>- cookieSecure applies only to Traefik
Loading

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread charts/n8n/values.yaml
# ----- Ingress -----
ingress:
enabled: false
className: "" # supported types are "traefik" & "nginx", "traefik" is suggested as nginx-ingress is deprecated

@cubic-dev-ai cubic-dev-ai Bot May 20, 2026

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.

P1: Using className to conditionally render annotations breaks sticky sessions for users with default or custom NGINX ingress class names.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At charts/n8n/values.yaml, line 159:

<comment>Using `className` to conditionally render annotations breaks sticky sessions for users with default or custom NGINX ingress class names.</comment>

<file context>
@@ -156,9 +156,12 @@ service:
 # ----- Ingress -----
 ingress:
   enabled: false
+  className: "" # supported types are "traefik" & "nginx", "traefik" is suggested as nginx-ingress is deprecated
   sticky:
     enabled: false
</file context>
Fix with Cubic

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is the default configuration as it existed originally

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yes, agreed. Users with a custom class name like "nginx-internal", etc. would have had the same behavior before this PR change.

namespace: {{ .Release.Namespace }}
labels:
{{- include "n8n.labels" . | nindent 4 }}
{{- $isTraefik := and .Values.ingress.enabled .Values.ingress.sticky.enabled (or (eq .Values.ingress.className "traefik") (hasKey (default dict .Values.ingress.annotations) "traefik.ingress.kubernetes.io/router.entrypoints")) }}

@cubic-dev-ai cubic-dev-ai Bot May 20, 2026

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.

P2: Traefik backend detection misses the common annotation-based class (kubernetes.io/ingress.class: traefik), so sticky-session service annotations may not render for valid Traefik configurations.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At charts/n8n/templates/service-main.yaml, line 8:

<comment>Traefik backend detection misses the common annotation-based class (`kubernetes.io/ingress.class: traefik`), so sticky-session service annotations may not render for valid Traefik configurations.</comment>

<file context>
@@ -5,7 +5,14 @@ metadata:
   namespace: {{ .Release.Namespace }}
   labels:
     {{- include "n8n.labels" . | nindent 4 }}
+  {{- $isTraefik := and .Values.ingress.enabled .Values.ingress.sticky.enabled (or (eq .Values.ingress.className "traefik") (hasKey (default dict .Values.ingress.annotations) "traefik.ingress.kubernetes.io/router.entrypoints")) }}
   {{- $merged := merge (deepCopy (.Values.service.main.annotations | default dict)) (.Values.service.annotations | default dict) }}
+  {{- if $isTraefik }}
</file context>
Suggested change
{{- $isTraefik := and .Values.ingress.enabled .Values.ingress.sticky.enabled (or (eq .Values.ingress.className "traefik") (hasKey (default dict .Values.ingress.annotations) "traefik.ingress.kubernetes.io/router.entrypoints")) }}
{{- $isTraefik := and .Values.ingress.enabled .Values.ingress.sticky.enabled (or (eq .Values.ingress.className "traefik") (and (hasKey (default dict .Values.ingress.annotations) "kubernetes.io/ingress.class") (eq (get (default dict .Values.ingress.annotations) "kubernetes.io/ingress.class") "traefik")) (hasKey (default dict .Values.ingress.annotations) "traefik.ingress.kubernetes.io/router.entrypoints")) }}
Fix with Cubic

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sticky-service annotations did appear during testing.

Comment thread charts/n8n/values.yaml
enabled: false
cookieName: n8n_affinity
cookieSecure: true # set to false when using plain HTTP (e.g. local minikube)
# cookieExpires and cookieMaxAge are nginx-only, these are ignored by traefik

@cubic-dev-ai cubic-dev-ai Bot May 20, 2026

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.

P2: The comment incorrectly claims cookieMaxAge is ignored by Traefik, leading to an unimplemented feature.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At charts/n8n/values.yaml, line 164:

<comment>The comment incorrectly claims `cookieMaxAge` is ignored by Traefik, leading to an unimplemented feature.</comment>

<file context>
@@ -156,9 +156,12 @@ service:
     enabled: false
     cookieName: n8n_affinity
+    cookieSecure: true          # set to false when using plain HTTP (e.g. local minikube)
+    # cookieExpires and cookieMaxAge are nginx-only, these are ignored by traefik
     cookieExpires: "172800"  # 2 days in seconds
     cookieMaxAge: "172800"   # 2 days in seconds
</file context>
Fix with Cubic

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is correct according to testing, these settings were ignored.

{{- $_ := set $merged "traefik.ingress.kubernetes.io/service.sticky.cookie.name" .Values.ingress.sticky.cookieName }}
{{- $_ := set $merged "traefik.ingress.kubernetes.io/service.sticky.cookie.secure" (.Values.ingress.sticky.cookieSecure | toString) }}
{{- $_ := set $merged "traefik.ingress.kubernetes.io/service.sticky.cookie.httponly" "true" }}
{{- end }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
{{- end }}
{{- $_ := set $merged "traefik.ingress.kubernetes.io/service.sticky.cookie.maxage" (.Values.ingress.sticky.cookieMaxAge | toString) }}
{{- end }}

Comment thread charts/n8n/values.yaml
enabled: false
cookieName: n8n_affinity
cookieSecure: true # set to false when using plain HTTP (e.g. local minikube)
# cookieExpires and cookieMaxAge are nginx-only, these are ignored by traefik

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
# cookieExpires and cookieMaxAge are nginx-only, these are ignored by traefik
# cookieExpires is nginx-only and ignored by traefik

I think what the bot wants to say is that the statement is not fully correct. cookieExpires is not support by Traefik, but cookieMaxAge is indeed supported.

namespace: {{ .Release.Namespace }}
labels:
{{- include "n8n.labels" . | nindent 4 }}
{{- $isTraefik := and .Values.ingress.enabled .Values.ingress.sticky.enabled (or (eq .Values.ingress.className "traefik") (hasKey (default dict .Values.ingress.annotations) "traefik.ingress.kubernetes.io/router.entrypoints")) }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
{{- $isTraefik := and .Values.ingress.enabled .Values.ingress.sticky.enabled (or (eq .Values.ingress.className "traefik") (hasKey (default dict .Values.ingress.annotations) "traefik.ingress.kubernetes.io/router.entrypoints")) }}
{{- $ingressAnnotations := default dict .Values.ingress.annotations }}
{{- $legacyClass := index $ingressAnnotations "kubernetes.io/ingress.class" }}
{{- $isTraefik := and .Values.ingress.enabled .Values.ingress.sticky.enabled (or (eq .Values.ingress.className "traefik") (eq $legacyClass "traefik") (hasKey $ingressAnnotations "traefik.ingress.kubernetes.io/router.entrypoints")) }}

I think this actually a valid finding. The previous check only looked for the existence of kubernetes.io/ingress.class, so a user with kubernetes.io/ingress.class: traefik would have incorrectly gotten Nginx annotations rendered on their Ingress.

Comment on lines +9 to +11
{{- if .Values.ingress.sticky.enabled }}
{{- $isNginx := or (eq .Values.ingress.className "nginx") (hasKey (default dict .Values.ingress.annotations) "kubernetes.io/ingress.class") }}
{{- if $isNginx }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggested change
{{- if .Values.ingress.sticky.enabled }}
{{- $isNginx := or (eq .Values.ingress.className "nginx") (hasKey (default dict .Values.ingress.annotations) "kubernetes.io/ingress.class") }}
{{- if $isNginx }}
{{- if .Values.ingress.sticky.enabled }}
{{- $legacyClass := index (default dict .Values.ingress.annotations) "kubernetes.io/ingress.class" }}
{{- $isNginx := or (eq .Values.ingress.className "nginx") (eq $legacyClass "nginx") }}
{{- if $isNginx }}

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.

2 participants