Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion charts/langflow/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Langflow Helm Chart

Langflow is a visual builder for AI workflows, RAG applications, agents, and integrations with model providers and vector databases.
This HelmForge chart deploys the official `docker.io/langflowai/langflow:1.10.0` image with persistent local state by default and explicit
This HelmForge chart deploys the official `docker.io/langflowai/langflow:1.10.1` image with persistent local state by default and explicit
production paths for secret management, PostgreSQL-compatible databases, ingress, Gateway API, and horizontal scaling.

## Install
Expand All @@ -12,6 +12,10 @@ helm install langflow helmforge/langflow
```

The default deployment starts one Langflow pod on port `7860` and persists `/app/langflow`, which contains local configuration and the default SQLite database.
Ingress class rendering is optional. Set `ingress.ingressClassName: ""` to omit `spec.ingressClassName`.
When `networkPolicy.enabled=true`, ingress is restricted to the configured peers.
It also enables egress isolation with built-in DNS and HTTPS allowances, then appends `networkPolicy.extraEgress` for database, provider, or proxy rules.
Set `networkPolicy.dnsEgressPeers` when your cluster DNS pods do not use the default kube-system/kube-dns labels.

## Production Configuration

Expand Down
79 changes: 59 additions & 20 deletions charts/langflow/templates/NOTES.txt
Original file line number Diff line number Diff line change
@@ -1,57 +1,96 @@
{{/* SPDX-License-Identifier: Apache-2.0 */}}
Langflow has been deployed.

Access:
1. Access

- Service: http://{{ include "langflow.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.service.port }}
- Port-forward: kubectl port-forward svc/{{ include "langflow.fullname" . }} 7860:{{ .Values.service.port }} -n {{ .Release.Namespace }}
- Browser: http://127.0.0.1:7860
- Service DNS: http://{{ include "langflow.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.service.port }}
- Port-forward:
kubectl port-forward svc/{{ include "langflow.fullname" . }} 7860:{{ .Values.service.port }} -n {{ .Release.Namespace }}
- Browser URL:
http://127.0.0.1:7860

State:
2. Runtime

- Image: {{ include "langflow.image" . }}
- HTTP port: {{ .Values.app.port }}
- Config directory: {{ .Values.persistence.mountPath }}

Comment thread
coderabbitai[bot] marked this conversation as resolved.
3. State

{{- if .Values.persistence.enabled }}
- Persistence: enabled
- PVC source: {{ default (include "langflow.fullname" .) .Values.persistence.existingClaim }}
- Size: {{ .Values.persistence.size }}
- Access modes: {{ join "," .Values.persistence.accessModes }}
{{- else }}
- Persistence: disabled; local SQLite data and config are lost when the pod is deleted
- Persistence: disabled.
- Local SQLite data and local config are lost when the pod is deleted.
{{- end }}
{{- if or .Values.database.url .Values.database.existingSecret }}
- Database: external via LANGFLOW_DATABASE_URL
- Database: external via LANGFLOW_DATABASE_URL.
{{- else }}
- Database: local SQLite in the config directory
- Database: local SQLite in the config directory.
{{- end }}

{{- if or .Values.auth.secretKey .Values.auth.existingSecret }}
Authentication:
4. Authentication

{{- if or .Values.auth.secretKey .Values.auth.existingSecret }}
- Auth Secret: {{ default (include "langflow.fullname" .) .Values.auth.existingSecret }}
- Secret key entry: {{ .Values.auth.secretKeyKey }}
- Superuser entry: {{ .Values.auth.superuserKey }}
- Superuser password entry: {{ .Values.auth.superuserPasswordKey }}
{{- else }}
Authentication:

- No chart-managed Langflow auth Secret is configured.
- Set auth.existingSecret before exposing Langflow to users.
{{- end }}

{{- if gt (int .Values.replicaCount) 1 }}
Scaling:
5. Scaling

- Replicas: {{ .Values.replicaCount }}
- Shared database is configured; verify all replicas can read/write the same flows.
{{- if gt (int .Values.replicaCount) 1 }}
- Shared database is configured; verify all replicas can read and write the same flows.
- If persistence is enabled, use ReadWriteMany storage or an externally managed RWX claim.
{{- else }}
- Single-replica mode can use local SQLite under {{ .Values.persistence.mountPath }}.
{{- end }}

6. Exposure

{{- if .Values.ingress.enabled }}
- Ingress is enabled.
- Protect Langflow with authentication before exposing it to users.
{{- else }}
- Ingress is disabled.
{{- end }}
{{- if .Values.gateway.enabled }}
- Gateway API HTTPRoute is enabled.
{{- else }}
- Gateway API HTTPRoute is disabled.
{{- end }}
{{- if .Values.networkPolicy.enabled }}
- NetworkPolicy is enabled for inbound Langflow traffic.
{{- else }}
- NetworkPolicy is disabled.
{{- end }}

Troubleshooting:
7. Troubleshooting

- kubectl get pod -l app.kubernetes.io/instance={{ .Release.Name }} -n {{ .Release.Namespace }}
- kubectl logs deploy/{{ include "langflow.fullname" . }} -n {{ .Release.Namespace }}
- kubectl describe pvc {{ include "langflow.fullname" . }} -n {{ .Release.Namespace }}
- Pods:
kubectl get pod -l app.kubernetes.io/instance={{ .Release.Name }} -n {{ .Release.Namespace }}
- Logs:
kubectl logs deploy/{{ include "langflow.fullname" . }} -n {{ .Release.Namespace }}
{{- if .Values.persistence.enabled }}
- PVC:
kubectl describe pvc {{ default (include "langflow.fullname" .) .Values.persistence.existingClaim }} -n {{ .Release.Namespace }}
{{- end }}
- Service endpoints:
kubectl get endpoints {{ include "langflow.fullname" . }} -n {{ .Release.Namespace }}

Resources:
8. Resources

- Chart documentation: https://helmforge.dev/docs/charts/langflow
- Langflow documentation: https://docs.langflow.org/
- Langflow deployment docs: https://docs.langflow.org/deployment-kubernetes-prod
- HelmForge issues: https://github.com/helmforgedev/charts/issues

Happy Helmforging :)
16 changes: 16 additions & 0 deletions charts/langflow/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,19 @@ app.kubernetes.io/part-of: helmforge
{{- end -}}
{{- define "langflow.serviceAccountName" -}}{{- if .Values.serviceAccount.create -}}{{- default (include "langflow.fullname" .) .Values.serviceAccount.name -}}{{- else -}}{{- default "default" .Values.serviceAccount.name -}}{{- end -}}{{- end -}}
{{- define "langflow.image" -}}{{- printf "%s:%s" .Values.image.repository .Values.image.tag -}}{{- end -}}

{{- define "langflow.validate" -}}
{{- if and (gt (int .Values.replicaCount) 1) (not (or .Values.database.url .Values.database.existingSecret)) -}}
{{- fail "replicaCount > 1 requires database.url or database.existingSecret for shared Langflow state" -}}
{{- end -}}
{{- if and (gt (int .Values.replicaCount) 1) .Values.persistence.enabled (not .Values.persistence.existingClaim) (not (has "ReadWriteMany" .Values.persistence.accessModes)) -}}
{{- fail "replicaCount > 1 with persistence.enabled requires persistence.accessModes to include ReadWriteMany or persistence.enabled=false" -}}
{{- end -}}
{{- $podLabels := .Values.podLabels | default dict -}}
{{- if hasKey $podLabels "app.kubernetes.io/name" -}}
{{- fail "podLabels must not override the selector label app.kubernetes.io/name" -}}
{{- end -}}
{{- if hasKey $podLabels "app.kubernetes.io/instance" -}}
{{- fail "podLabels must not override the selector label app.kubernetes.io/instance" -}}
{{- end -}}
{{- end -}}
2 changes: 1 addition & 1 deletion charts/langflow/templates/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ spec:
template:
metadata:
labels:
{{- include "langflow.labels" . | nindent 8 }}
{{- include "langflow.selectorLabels" . | nindent 8 }}
{{- with .Values.podLabels }}
{{- toYaml . | nindent 8 }}
{{- end }}
Expand Down
4 changes: 3 additions & 1 deletion charts/langflow/templates/ingress.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ metadata:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
ingressClassName: {{ .Values.ingress.ingressClassName | quote }}
{{- with .Values.ingress.ingressClassName }}
ingressClassName: {{ . | quote }}
{{- end }}
{{- with .Values.ingress.tls }}
tls:
{{- toYaml . | nindent 4 }}
Expand Down
22 changes: 22 additions & 0 deletions charts/langflow/templates/networkpolicy.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{{/* SPDX-License-Identifier: Apache-2.0 */}}
{{- if .Values.networkPolicy.enabled }}
{{- $extraEgress := .Values.networkPolicy.extraEgress | default list }}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
{{- $dnsEgressPeers := .Values.networkPolicy.dnsEgressPeers | default list }}
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
Expand All @@ -12,6 +14,7 @@ spec:
{{- include "langflow.selectorLabels" . | nindent 6 }}
policyTypes:
- Ingress
- Egress
ingress:
- from:
{{- if .Values.networkPolicy.ingressFrom }}
Expand All @@ -22,4 +25,23 @@ spec:
ports:
- protocol: TCP
port: http
egress:
- to:
{{- toYaml $dnsEgressPeers | nindent 8 }}
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
- to:
- ipBlock:
cidr: 0.0.0.0/0
- ipBlock:
cidr: ::/0
ports:
- protocol: TCP
port: 443
{{- with $extraEgress }}
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}
7 changes: 1 addition & 6 deletions charts/langflow/templates/validate.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
{{/* SPDX-License-Identifier: Apache-2.0 */}}
{{- if and (gt (int .Values.replicaCount) 1) (not (or .Values.database.url .Values.database.existingSecret)) -}}
{{- fail "replicaCount > 1 requires database.url or database.existingSecret for shared Langflow state" -}}
{{- end -}}
{{- if and (gt (int .Values.replicaCount) 1) .Values.persistence.enabled (not (has "ReadWriteMany" .Values.persistence.accessModes)) -}}
{{- fail "replicaCount > 1 with persistence.enabled requires persistence.accessModes to include ReadWriteMany or persistence.enabled=false" -}}
{{- end -}}
{{- include "langflow.validate" . -}}
36 changes: 36 additions & 0 deletions charts/langflow/tests/networkpolicy_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,44 @@ tests:
asserts:
- isKind:
of: NetworkPolicy
- contains:
path: spec.policyTypes
content: Egress
- contains:
path: spec.ingress[0].ports
content:
protocol: TCP
port: http
- equal:
path: spec.egress[0].to[0].namespaceSelector.matchLabels["kubernetes.io/metadata.name"]
value: kube-system
- equal:
path: spec.egress[0].to[0].podSelector.matchLabels["k8s-app"]
value: kube-dns
- equal:
path: spec.egress[1].ports[0].port
value: 443
- it: appends extra egress rules after default DNS and HTTPS
set:
networkPolicy:
enabled: true
extraEgress:
- to:
- ipBlock:
cidr: 10.0.0.0/8
ports:
- protocol: TCP
port: 5432
asserts:
- contains:
path: spec.policyTypes
content: Egress
- equal:
path: spec.egress[0].ports[0].port
value: 53
- equal:
path: spec.egress[1].ports[0].port
value: 443
- equal:
path: spec.egress[2].ports[0].port
value: 5432
16 changes: 16 additions & 0 deletions charts/langflow/tests/templates_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ tests:
- equal:
path: spec.template.spec.containers[0].image
value: docker.io/langflowai/langflow:1.10.1
- equal:
path: spec.template.metadata.labels["app.kubernetes.io/name"]
value: langflow
- notExists:
path: spec.template.metadata.labels["helm.sh/chart"]
- it: renders service
template: templates/service.yaml
asserts:
Expand Down Expand Up @@ -42,6 +47,17 @@ tests:
- equal:
path: metadata.annotations["cert-manager.io/cluster-issuer"]
value: letsencrypt
- it: omits empty ingress class name
template: templates/ingress.yaml
set:
ingress.enabled: true
ingress.ingressClassName: ""
ingress.hosts[0].host: langflow.example.com
ingress.hosts[0].paths[0].path: /
ingress.hosts[0].paths[0].pathType: Prefix
asserts:
- notExists:
path: spec.ingressClassName
- it: renders HTTPRoute hostnames
template: templates/httproute.yaml
set:
Expand Down
15 changes: 15 additions & 0 deletions charts/langflow/tests/validation_test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,18 @@ tests:
asserts:
- failedTemplate:
errorMessage: "replicaCount > 1 with persistence.enabled requires persistence.accessModes to include ReadWriteMany or persistence.enabled=false"
- it: allows scaling with an existing persistence claim
set:
replicaCount: 2
database.existingSecret: langflow-database
persistence.existingClaim: langflow-data-rwx
persistence.accessModes[0]: ReadWriteOnce
asserts:
- notFailedTemplate: {}
- it: fails when podLabels override selector labels
set:
podLabels:
app.kubernetes.io/name: custom
asserts:
- failedTemplate:
errorMessage: "podLabels must not override the selector label app.kubernetes.io/name"
14 changes: 12 additions & 2 deletions charts/langflow/values.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"type": "object",
"properties": {
"repository": { "type": "string", "default": "docker.io/langflowai/langflow" },
"tag": { "type": "string", "default": "1.10.0" },
"tag": { "type": "string", "default": "1.10.1" },
"pullPolicy": { "type": "string", "enum": ["Always", "IfNotPresent", "Never"], "default": "IfNotPresent" }
}
},
Expand Down Expand Up @@ -76,7 +76,17 @@
"type": "object",
"properties": {
"enabled": { "type": "boolean", "default": false },
"ingressFrom": { "type": "array", "items": { "type": "object" } }
"ingressFrom": { "type": "array", "items": { "type": "object" } },
"dnsEgressPeers": {
"type": "array",
"description": "DNS peers allowed by the built-in egress allowance",
"items": { "type": "object" }
},
"extraEgress": {
"type": "array",
"description": "Additional egress rules appended after the built-in DNS and HTTPS allowances",
"items": { "type": "object" }
}
}
},
"probes": { "$ref": "#/definitions/probes" },
Expand Down
10 changes: 10 additions & 0 deletions charts/langflow/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ networkPolicy:
enabled: false
# -- Ingress peers allowed to reach Langflow. Empty allows all namespaces when policy is enabled.
ingressFrom: []
# -- DNS peers allowed when egress isolation is enabled.
dnsEgressPeers:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
# -- Additional egress rules appended after the built-in DNS and HTTPS allowances.
extraEgress: []

probes:
# -- Startup probe settings.
Expand Down