Compare commits

..

7 Commits

Author SHA1 Message Date
7438b18313 done tick version bump, fixed bug with adguard 2026-09-15 12:14:12 +02:00
5497301740 removed checkmk 2026-09-15 11:45:13 +02:00
d84a669a62 adguard home release 0.0.1 2026-09-15 11:37:19 +02:00
84168077ee norish upgrade 2026-08-20 15:32:03 +02:00
0e5c73976b added Checkmk init release 2026-06-17 15:17:45 +02:00
256f7e6807 version bump norish 2026-04-13 16:15:48 +02:00
839d5ce530 norish release 0.15.4 2026-02-03 19:42:29 +01:00
23 changed files with 3132 additions and 2429 deletions

View File

@ -0,0 +1,19 @@
apiVersion: v2
name: adguard-home
description: AdGuard Home helm chart for Kubernetes - Network-wide ad and tracker blocking DNS server
type: application
version: 0.0.2
appVersion: "v0.107.79"
maintainers:
- name: Richard Tomik
email: no@m.com
keywords:
- dns
- ad-blocking
- dns-filtering
- adguard-home
- privacy
- network-security
home: https://github.com/rtomik/helm-charts
sources:
- https://github.com/AdguardTeam/AdGuardHome

View File

@ -0,0 +1,55 @@
1. Get the admin dashboard URL by running these commands:
{{- if .Values.ingress.enabled }}
{{- range $host := .Values.ingress.hosts }}
{{- range .paths }}
http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }}
{{- end }}
{{- end }}
{{- else if contains "NodePort" .Values.service.type }}
export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[?(@.name=='web')].nodePort}" services {{ include "adguard-home.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 }}
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 "adguard-home.fullname" . }}'
export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "adguard-home.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}")
echo http://$SERVICE_IP:{{ .Values.ports.web.port }}
{{- else if contains "ClusterIP" .Values.service.type }}
export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "adguard-home.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}")
echo "Visit http://127.0.0.1:8080 to use the AdGuard Home setup wizard / dashboard"
kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:{{ .Values.ports.web.port }}
{{- end }}
2. On first access, complete the AdGuard Home setup wizard (admin user, DNS listener
interfaces/ports). The resulting AdGuardHome.yaml is written to the conf volume.
3. DNS listens on port {{ .Values.ports.dns.port }} (TCP/UDP).
{{- if not .Values.hostNetwork }}
NOTE: hostNetwork is disabled, so clients will see the Service/pod IP, not their
real IP, in the AdGuard Home query log/filters. Set hostNetwork: true, or use a
LoadBalancer Service with externalTrafficPolicy: Local, to preserve client IPs.
{{- end }}
{{- if .Values.ports.dhcp.enabled }}
4. DHCP server is enabled on UDP 67/68.
{{- if not .Values.hostNetwork }}
WARNING: DHCP requires hostNetwork: true to function correctly - it will not
work through a ClusterIP/LoadBalancer Service.
{{- end }}
{{- end }}
{{- if or .Values.persistence.work.enabled .Values.persistence.conf.enabled }}
5. Data is persisted using PVCs:
{{- if .Values.persistence.work.enabled }}
- {{ .Values.persistence.work.existingClaim | default (printf "%s-work" (include "adguard-home.fullname" .)) }} (query log, filter cache, stats)
{{- end }}
{{- if .Values.persistence.conf.enabled }}
- {{ .Values.persistence.conf.existingClaim | default (printf "%s-conf" (include "adguard-home.fullname" .)) }} (AdGuardHome.yaml)
{{- end }}
{{- else }}
5. WARNING: No persistence enabled. Configuration and filter data will be lost when
the pod restarts.
{{- end }}
For more information about using this Helm chart, please refer to the readme.md file.

View File

@ -0,0 +1,235 @@
# AdGuard Home Helm Chart
A Helm chart for deploying [AdGuard Home](https://github.com/AdguardTeam/AdGuardHome), a network-wide DNS ad and tracker blocker, on Kubernetes.
## Introduction
This chart deploys AdGuard Home on a Kubernetes cluster using the Helm package manager. AdGuard Home is a self-hosted DNS server that blocks ads and trackers for every device on your network, and can also act as a DHCP server, DNS-over-TLS/HTTPS/QUIC resolver, and DNSCrypt server.
Source code: https://github.com/rtomik/helm-charts/tree/main/charts/adguard-home
## Prerequisites
- Kubernetes 1.19+
- Helm 3.0+
- PV provisioner support (if persistence is needed)
- A way to route real LAN clients to the DNS Service (LoadBalancer/MetalLB, NodePort, or `hostNetwork: true`) if you intend to use it as your network's resolver
## Installing the Chart
```bash
helm repo add rtomik https://rtomik.github.io/helm-charts
helm install adguard-home rtomik/adguard-home
```
## Uninstalling the Chart
```bash
helm uninstall adguard-home
```
## Configuration Examples
### Minimal Installation (admin UI only, no DNS exposed on the LAN)
```yaml
persistence:
work:
enabled: true
size: 2Gi
conf:
enabled: true
size: 100Mi
ingress:
enabled: true
hosts:
- host: adguard.example.com
paths:
- path: /
pathType: Prefix
tls:
- hosts:
- adguard.example.com
secretName: adguard-tls
```
### Expose DNS to the LAN via LoadBalancer (e.g. MetalLB)
```yaml
service:
type: LoadBalancer
loadBalancerIP: 192.168.1.53
annotations:
metallb.universe.tf/allow-shared-ip: adguard-home
hostNetwork: true # preserves real client IPs in the query log/filters
```
### Enable DNS-over-TLS / DNS-over-QUIC and DNS-over-HTTPS
```yaml
ports:
dot:
enabled: true
https:
enabled: true
```
### Enable the DHCP server
DHCP requires `hostNetwork: true` - it cannot be proxied through a ClusterIP/LoadBalancer Service.
```yaml
hostNetwork: true
ports:
dhcp:
enabled: true
```
### Use an existing PVC
```yaml
persistence:
work:
existingClaim: "adguard-work-pvc"
conf:
existingClaim: "adguard-conf-pvc"
```
## Parameters
### Global Parameters
| Name | Description | Default |
|------|-------------|---------|
| `nameOverride` | Override the release name | `""` |
| `fullnameOverride` | Fully override the release name | `""` |
### Image Parameters
| Name | Description | Default |
|------|-------------|---------|
| `image.repository` | AdGuard Home image repository | `adguard/adguardhome` |
| `image.tag` | Image tag | `v0.107.79` |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
| `imagePullSecrets` | Image pull secrets | `[]` |
### Deployment Parameters
| Name | Description | Default |
|------|-------------|---------|
| `replicaCount` | Number of replicas (keep at 1 - AdGuard Home is not multi-writer safe) | `1` |
| `revisionHistoryLimit` | Revisions to retain | `3` |
| `podSecurityContext.runAsNonRoot` | Run as non-root | `true` |
| `podSecurityContext.runAsUser` | User ID | `1000` |
| `podSecurityContext.runAsGroup` | Group ID | `1000` |
| `podSecurityContext.fsGroup` | Filesystem group ID | `1000` |
| `containerSecurityContext.capabilities.add` | Capabilities added (NET_BIND_SERVICE for ports < 1024) | `["NET_BIND_SERVICE"]` |
| `hostNetwork` | Use host networking (required for DHCP, recommended for accurate client IPs) | `false` |
| `nodeSelector` | Node selector | `{}` |
| `tolerations` | Tolerations | `[]` |
| `affinity` | Affinity rules | `{}` |
### Ports Parameters
| Name | Description | Default |
|------|-------------|---------|
| `ports.web.port` | Admin dashboard / setup wizard | `3000` |
| `ports.dns.port` | Plain DNS (TCP+UDP) | `53` |
| `ports.dot.enabled` | Enable DNS-over-TLS / DNS-over-QUIC | `false` |
| `ports.dot.port` | DoT/DoQ port (TCP+UDP) | `853` |
| `ports.https.enabled` | Enable DNS-over-HTTPS / HTTPS admin dashboard | `false` |
| `ports.https.port` | HTTPS port (TCP+UDP) | `443` |
| `ports.dnscrypt.enabled` | Enable DNSCrypt | `false` |
| `ports.dnscrypt.port` | DNSCrypt port (TCP+UDP) | `5443` |
| `ports.dhcp.enabled` | Enable DHCP server (67/68 UDP, requires `hostNetwork: true`) | `false` |
| `ports.pprof.enabled` | Enable the debug pprof API | `false` |
| `ports.pprof.port` | pprof port | `6060` |
### Service Parameters
| Name | Description | Default |
|------|-------------|---------|
| `service.type` | Service type | `ClusterIP` |
| `service.annotations` | Service annotations | `{}` |
| `service.loadBalancerIP` | Static LoadBalancer IP (e.g. for MetalLB) | `""` |
### Ingress Parameters
| Name | Description | Default |
|------|-------------|---------|
| `ingress.enabled` | Enable ingress (routes to the admin dashboard only) | `false` |
| `ingress.className` | Ingress class name | `""` |
| `ingress.annotations` | Ingress annotations | See values.yaml |
| `ingress.hosts` | Ingress hosts | See values.yaml |
| `ingress.tls` | TLS configuration | See values.yaml |
### Persistence Parameters
| Name | Description | Default |
|------|-------------|---------|
| `persistence.work.enabled` | Persist `/opt/adguardhome/work` (query log, filter cache, stats) | `true` |
| `persistence.work.existingClaim` | Use an existing PVC instead of creating one | `""` |
| `persistence.work.storageClass` | Storage class | `""` |
| `persistence.work.accessMode` | Access mode | `ReadWriteOnce` |
| `persistence.work.size` | PVC size | `1Gi` |
| `persistence.conf.enabled` | Persist `/opt/adguardhome/conf` (AdGuardHome.yaml) | `true` |
| `persistence.conf.existingClaim` | Use an existing PVC instead of creating one | `""` |
| `persistence.conf.storageClass` | Storage class | `""` |
| `persistence.conf.accessMode` | Access mode | `ReadWriteOnce` |
| `persistence.conf.size` | PVC size | `100Mi` |
### Resource Parameters
| Name | Description | Default |
|------|-------------|---------|
| `resources` | Resource limits and requests | `{}` |
### Health Check Parameters
| Name | Description | Default |
|------|-------------|---------|
| `probes.liveness.enabled` | Enable liveness probe (TCP check on the admin web port) | `true` |
| `probes.liveness.initialDelaySeconds` | Liveness initial delay | `15` |
| `probes.liveness.periodSeconds` | Liveness period | `30` |
| `probes.readiness.enabled` | Enable readiness probe (TCP check on the admin web port) | `true` |
| `probes.readiness.initialDelaySeconds` | Readiness initial delay | `5` |
| `probes.readiness.periodSeconds` | Readiness period | `10` |
### Other Parameters
| Name | Description | Default |
|------|-------------|---------|
| `extraEnv` | Additional environment variables | `[]` |
| `extraVolumeMounts` | Additional volume mounts | `[]` |
| `extraVolumes` | Additional volumes | `[]` |
## Notes
- AdGuard Home has no supported way to bootstrap its admin account or DNS settings via
environment variables - complete the setup wizard once at `http://<web-service>:3000`
after the first install. The resulting `AdGuardHome.yaml` is written to the `conf` PVC,
so it survives pod restarts/upgrades.
- Because AdGuard Home stores state on disk and isn't multi-writer safe, do not scale
`replicaCount` beyond `1`.
- If you plan to use this as your network's DNS resolver, prefer `hostNetwork: true` (or
a LoadBalancer Service with `externalTrafficPolicy: Local`) so AdGuard Home sees real
client IPs rather than a single Service/NAT IP for every device.
## Troubleshooting
- **Clients all show up as the same IP / filters by client don't work**: enable `hostNetwork` or use `externalTrafficPolicy: Local` on a LoadBalancer Service.
- **DHCP doesn't hand out leases**: DHCP only works with `hostNetwork: true`.
- **Setup wizard settings don't persist**: verify `persistence.conf.enabled` is `true` and the PVC is bound.
```bash
kubectl logs -f deployment/adguard-home
kubectl describe pod -l app.kubernetes.io/name=adguard-home
```
## Links
- [AdGuard Home GitHub](https://github.com/AdguardTeam/AdGuardHome)
- [AdGuard Home Docker documentation](https://adguard-dns.io/kb/adguard-home/docker/)
- [Chart Source](https://github.com/rtomik/helm-charts/tree/main/charts/adguard-home)

View File

@ -0,0 +1,45 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "adguard-home.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
*/}}
{{- define "adguard-home.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- printf "%s" $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "adguard-home.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "adguard-home.labels" -}}
helm.sh/chart: {{ include "adguard-home.chart" . }}
{{ include "adguard-home.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "adguard-home.selectorLabels" -}}
app.kubernetes.io/name: {{ include "adguard-home.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}

View File

@ -0,0 +1,175 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "adguard-home.fullname" . }}
labels:
{{- include "adguard-home.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
revisionHistoryLimit: {{ .Values.revisionHistoryLimit }}
selector:
matchLabels:
{{- include "adguard-home.selectorLabels" . | nindent 6 }}
strategy:
type: Recreate
template:
metadata:
labels:
{{- include "adguard-home.selectorLabels" . | nindent 8 }}
annotations:
{{- with .Values.podAnnotations }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- if .Values.hostNetwork }}
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
{{- end }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
{{- if .Values.persistence.conf.enabled }}
initContainers:
- name: init-config
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
# AdGuard Home's first-run setup wizard defaults the admin web interface
# port to 80, regardless of ports.web.port below - if a user accepts that
# default, the Service/Ingress (which forward to ports.web.port) end up
# pointing at the wrong container port and the UI becomes unreachable
# (502/Bad Gateway) as soon as setup completes. Seeding http.address here
# before the wizard ever runs makes it pre-fill the correct port instead.
command:
- sh
- -c
- |
if [ ! -f /opt/adguardhome/conf/AdGuardHome.yaml ]; then
cat > /opt/adguardhome/conf/AdGuardHome.yaml <<'CONF'
http:
address: 0.0.0.0:{{ .Values.ports.web.port }}
CONF
fi
volumeMounts:
- name: conf
mountPath: /opt/adguardhome/conf
{{- end }}
containers:
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.containerSecurityContext | nindent 12 }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
ports:
- name: web
containerPort: {{ .Values.ports.web.port }}
protocol: TCP
- name: dns-tcp
containerPort: {{ .Values.ports.dns.port }}
protocol: TCP
- name: dns-udp
containerPort: {{ .Values.ports.dns.port }}
protocol: UDP
{{- if .Values.ports.dot.enabled }}
- name: dot-tcp
containerPort: {{ .Values.ports.dot.port }}
protocol: TCP
- name: dot-udp
containerPort: {{ .Values.ports.dot.port }}
protocol: UDP
{{- end }}
{{- if .Values.ports.https.enabled }}
- name: https-tcp
containerPort: {{ .Values.ports.https.port }}
protocol: TCP
- name: https-udp
containerPort: {{ .Values.ports.https.port }}
protocol: UDP
{{- end }}
{{- if .Values.ports.dnscrypt.enabled }}
- name: dnscrypt-tcp
containerPort: {{ .Values.ports.dnscrypt.port }}
protocol: TCP
- name: dnscrypt-udp
containerPort: {{ .Values.ports.dnscrypt.port }}
protocol: UDP
{{- end }}
{{- if .Values.ports.dhcp.enabled }}
- name: dhcp-server
containerPort: 67
protocol: UDP
- name: dhcp-client
containerPort: 68
protocol: UDP
{{- end }}
{{- if .Values.ports.pprof.enabled }}
- name: pprof
containerPort: {{ .Values.ports.pprof.port }}
protocol: TCP
{{- end }}
{{- if .Values.probes.liveness.enabled }}
livenessProbe:
tcpSocket:
port: web
initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.liveness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }}
failureThreshold: {{ .Values.probes.liveness.failureThreshold }}
successThreshold: {{ .Values.probes.liveness.successThreshold }}
{{- end }}
{{- if .Values.probes.readiness.enabled }}
readinessProbe:
tcpSocket:
port: web
initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }}
periodSeconds: {{ .Values.probes.readiness.periodSeconds }}
timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }}
failureThreshold: {{ .Values.probes.readiness.failureThreshold }}
successThreshold: {{ .Values.probes.readiness.successThreshold }}
{{- end }}
{{- with .Values.extraEnv }}
env:
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
{{- if .Values.persistence.work.enabled }}
- name: work
mountPath: /opt/adguardhome/work
{{- end }}
{{- if .Values.persistence.conf.enabled }}
- name: conf
mountPath: /opt/adguardhome/conf
{{- end }}
{{- with .Values.extraVolumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
resources:
{{- toYaml .Values.resources | nindent 12 }}
volumes:
{{- if .Values.persistence.work.enabled }}
- name: work
persistentVolumeClaim:
claimName: {{ .Values.persistence.work.existingClaim | default (printf "%s-work" (include "adguard-home.fullname" .)) }}
{{- end }}
{{- if .Values.persistence.conf.enabled }}
- name: conf
persistentVolumeClaim:
claimName: {{ .Values.persistence.conf.existingClaim | default (printf "%s-conf" (include "adguard-home.fullname" .)) }}
{{- end }}
{{- with .Values.extraVolumes }}
{{- toYaml . | nindent 8 }}
{{- 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 }}

View File

@ -0,0 +1,43 @@
{{- if .Values.ingress.enabled -}}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "adguard-home.fullname" . }}
labels:
{{- include "adguard-home.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:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
{{- if .secretName }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path }}
pathType: {{ .pathType }}
backend:
service:
name: {{ include "adguard-home.fullname" $ }}
port:
number: {{ $.Values.ports.web.port }}
{{- end }}
{{- end }}
{{- end }}

View File

@ -0,0 +1,43 @@
{{- if and .Values.persistence.work.enabled (not .Values.persistence.work.existingClaim) }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "adguard-home.fullname" . }}-work
labels:
{{- include "adguard-home.labels" . | nindent 4 }}
{{- with .Values.persistence.work.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
accessModes:
- {{ .Values.persistence.work.accessMode | quote }}
{{- if .Values.persistence.work.storageClass }}
storageClassName: {{ .Values.persistence.work.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.work.size | quote }}
{{- end }}
---
{{- if and .Values.persistence.conf.enabled (not .Values.persistence.conf.existingClaim) }}
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: {{ include "adguard-home.fullname" . }}-conf
labels:
{{- include "adguard-home.labels" . | nindent 4 }}
{{- with .Values.persistence.conf.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
accessModes:
- {{ .Values.persistence.conf.accessMode | quote }}
{{- if .Values.persistence.conf.storageClass }}
storageClassName: {{ .Values.persistence.conf.storageClass | quote }}
{{- end }}
resources:
requests:
storage: {{ .Values.persistence.conf.size | quote }}
{{- end }}

View File

@ -0,0 +1,76 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "adguard-home.fullname" . }}
labels:
{{- include "adguard-home.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
{{- if .Values.service.loadBalancerIP }}
loadBalancerIP: {{ .Values.service.loadBalancerIP }}
{{- end }}
ports:
- name: web
port: {{ .Values.ports.web.port }}
targetPort: web
protocol: TCP
- name: dns-tcp
port: {{ .Values.ports.dns.port }}
targetPort: dns-tcp
protocol: TCP
- name: dns-udp
port: {{ .Values.ports.dns.port }}
targetPort: dns-udp
protocol: UDP
{{- if .Values.ports.dot.enabled }}
- name: dot-tcp
port: {{ .Values.ports.dot.port }}
targetPort: dot-tcp
protocol: TCP
- name: dot-udp
port: {{ .Values.ports.dot.port }}
targetPort: dot-udp
protocol: UDP
{{- end }}
{{- if .Values.ports.https.enabled }}
- name: https-tcp
port: {{ .Values.ports.https.port }}
targetPort: https-tcp
protocol: TCP
- name: https-udp
port: {{ .Values.ports.https.port }}
targetPort: https-udp
protocol: UDP
{{- end }}
{{- if .Values.ports.dnscrypt.enabled }}
- name: dnscrypt-tcp
port: {{ .Values.ports.dnscrypt.port }}
targetPort: dnscrypt-tcp
protocol: TCP
- name: dnscrypt-udp
port: {{ .Values.ports.dnscrypt.port }}
targetPort: dnscrypt-udp
protocol: UDP
{{- end }}
{{- if .Values.ports.dhcp.enabled }}
- name: dhcp-server
port: 67
targetPort: dhcp-server
protocol: UDP
- name: dhcp-client
port: 68
targetPort: dhcp-client
protocol: UDP
{{- end }}
{{- if .Values.ports.pprof.enabled }}
- name: pprof
port: {{ .Values.ports.pprof.port }}
targetPort: pprof
protocol: TCP
{{- end }}
selector:
{{- include "adguard-home.selectorLabels" . | nindent 4 }}

View File

@ -0,0 +1,161 @@
## Global settings
nameOverride: ""
fullnameOverride: ""
## Image settings
image:
repository: adguard/adguardhome
tag: "v0.107.79"
pullPolicy: IfNotPresent
imagePullSecrets: []
## Deployment settings
## AdGuard Home keeps its state on disk (PVCs below) and is not designed to run as
## multiple replicas against the same data - keep replicaCount at 1.
replicaCount: 1
revisionHistoryLimit: 3
## Pod security settings
## AdGuard Home's own startup check refuses to run on its first launch unless the
## process euid is 0 ("this is the first launch of adguard home; you must run it as
## administrator"), so - unlike most charts here - this one runs as root. Capabilities
## are still dropped to the minimum the binary needs.
podSecurityContext:
runAsNonRoot: false
runAsUser: 0
fsGroup: 0
containerSecurityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: false
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
- CHOWN
- DAC_OVERRIDE
- SETUID
- SETGID
## Pod scheduling
nodeSelector: {}
tolerations: []
affinity: {}
podAnnotations: {}
## Use host networking. Required if you enable the DHCP server, and recommended
## when exposing DNS so AdGuard Home sees real client IPs instead of the pod/service IP.
hostNetwork: false
## Ports exposed by AdGuard Home. web+dns are enabled by default; the rest are
## optional protocols you can turn on as needed.
## https://adguard-dns.io/kb/adguard-home/docker/
ports:
web:
# Admin dashboard / setup wizard
port: 3000
dns:
# Plain DNS (TCP+UDP)
port: 53
dot:
# DNS-over-TLS / DNS-over-QUIC (TCP+UDP)
enabled: false
port: 853
https:
# DNS-over-HTTPS and HTTPS admin dashboard (TCP+UDP)
enabled: false
port: 443
dnscrypt:
# DNSCrypt (TCP+UDP)
enabled: false
port: 5443
dhcp:
# DHCP server (UDP 67/68). Requires hostNetwork: true.
enabled: false
pprof:
# Debug pprof API - leave disabled unless troubleshooting
enabled: false
port: 6060
## Service settings
## For exposing DNS to your LAN, set type to LoadBalancer (e.g. with MetalLB) and
## add annotations/loadBalancerIP as needed, or use type: NodePort.
service:
type: ClusterIP
annotations: {}
# loadBalancerIP: 192.168.1.53
## Ingress settings (routes to the admin web UI only)
ingress:
enabled: false
className: ""
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
hosts:
- host: adguard.domain.com
paths:
- path: /
pathType: Prefix
tls:
- hosts:
- adguard.domain.com
## Persistence settings
persistence:
work:
# /opt/adguardhome/work - query log, filter cache, stats
enabled: true
existingClaim: ""
storageClass: ""
accessMode: ReadWriteOnce
size: 1Gi
annotations: {}
conf:
# /opt/adguardhome/conf - AdGuardHome.yaml configuration
enabled: true
existingClaim: ""
storageClass: ""
accessMode: ReadWriteOnce
size: 100Mi
annotations: {}
## Resource limits and requests
resources: {}
# resources:
# limits:
# cpu: 500m
# memory: 256Mi
# requests:
# cpu: 50m
# memory: 64Mi
## Application health checks
## Uses a TCP check against the admin web port. The DNS listener only comes up after
## the first-run setup wizard is completed, so probing the DNS port instead would
## fail/restart the pod before it's ever configured; the web port is always up.
probes:
liveness:
enabled: true
initialDelaySeconds: 15
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
readiness:
enabled: true
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
successThreshold: 1
## Extra environment variables
extraEnv: []
## Extra volume mounts
extraVolumeMounts: []
## Extra volumes
extraVolumes: []

View File

@ -1,14 +1,12 @@
# Donetick Helm Chart
A Helm chart for deploying the Donetick task management application on Kubernetes.
A Helm chart for deploying [Donetick](https://github.com/donetick/donetick) on Kubernetes.
## Introduction
This chart deploys [Donetick](https://github.com/donetick/donetick) on a Kubernetes cluster using the Helm package manager.
Source code can be found here:
- https://github.com/rtomik/helm-charts/tree/main/charts/donetick
This chart deploys Donetick, a task management application, on a Kubernetes cluster using the Helm package manager. Donetick supports SQLite or PostgreSQL databases, real-time updates via WebSockets, OAuth2 authentication, and push notifications via Telegram and Pushover.
Source code: https://github.com/rtomik/helm-charts/tree/main/charts/donetick
## Prerequisites
@ -18,98 +16,72 @@ Source code can be found here:
## Installing the Chart
To install the chart with the release name `donetick`:
```bash
$ helm repo add donetick-chart https://rtomik.github.io/helm-charts
$ helm install donetick donetick-chart/donetick
helm repo add rtomik https://rtomik.github.io/helm-charts
helm install donetick rtomik/donetick
```
> **Tip**: List all releases using `helm list`
## Uninstalling the Chart
```bash
helm uninstall donetick
```
## Configuration Examples
### Basic Installation with SQLite (Default)
### Minimal Installation (SQLite)
The chart works out of the box with SQLite — no additional configuration required:
```bash
helm install donetick donetick-chart/donetick
helm install donetick rtomik/donetick
```
### Installation with External PostgreSQL
Create a values file for PostgreSQL configuration:
### PostgreSQL Configuration
```yaml
config:
database:
type: "postgres"
host: "postgresql.database.svc.cluster.local"
port: 5432
name: "donetick"
secrets:
existingSecret: "donetick-postgres-secret"
userKey: "username"
passwordKey: "password"
jwt:
secret: "your-secure-jwt-secret-at-least-32-characters-long"
server:
cors_allow_origins:
- "https://your-domain.com"
features:
notifications: true
realtime: true
ingress:
enabled: true
hosts:
- host: donetick.your-domain.com
paths:
- path: /
pathType: Prefix
tls:
- hosts:
- donetick.your-domain.com
persistence:
enabled: true
size: "5Gi"
```
### Production with Existing Secrets
```yaml
# values-postgres.yaml
config:
database:
type: "postgres"
host: "postgresql.database.svc.cluster.local"
port: 5432
user: "donetick"
password: "your-secure-password"
name: "donetick"
migration: true
# Update JWT secret for production
jwt:
secret: "your-secure-jwt-secret-at-least-32-characters-long"
# Configure server settings
server:
cors_allow_origins:
- "https://your-domain.com"
- "http://localhost:5173"
# Enable features as needed
features:
notifications: true
realtime: true
oauth: false
# Enable ingress for external access
ingress:
enabled: true
className: "nginx"
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
hosts:
- host: donetick.your-domain.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: donetick-tls
hosts:
- donetick.your-domain.com
# Configure persistence
persistence:
enabled: true
storageClass: "fast-ssd"
size: "5Gi"
```
Install with PostgreSQL configuration:
```bash
helm install donetick donetick-chart/donetick -f values-postgres.yaml
```
### Production Installation with External Secrets
For production deployments, use Kubernetes secrets for sensitive data:
```yaml
# values-production.yaml
config:
database:
type: "postgres"
host: "postgresql.database.svc.cluster.local"
port: 5432
name: "donetick"
# Use existing secret for postgres credentials
database:
type: "postgres"
host: "postgresql.database.svc.cluster.local"
@ -120,14 +92,11 @@ config:
userKey: "username"
passwordKey: "password"
# Use existing secret for JWT
jwt:
existingSecret: "donetick-jwt-secret"
secretKey: "jwt-secret"
secretKey: "jwtSecret"
session_time: "168h"
max_refresh: "168h"
# OAuth2 configuration with secrets
oauth2:
existingSecret: "donetick-oauth-secret"
clientIdKey: "client-id"
@ -137,26 +106,17 @@ config:
user_info_url: "https://your-oauth-provider.com/userinfo"
redirect_url: "https://donetick.your-domain.com/auth/callback"
# Production server settings
server:
cors_allow_origins:
- "https://donetick.your-domain.com"
rate_limit: 100
rate_period: "60s"
# Enable production features
features:
notifications: true
realtime: true
oauth: true
# Security context for production
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
# Resource limits for production
resources:
limits:
cpu: 500m
@ -165,299 +125,244 @@ resources:
cpu: 100m
memory: 128Mi
# Ingress with TLS
ingress:
enabled: true
className: "nginx"
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
hosts:
- host: donetick.your-domain.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: donetick-tls
hosts:
- hosts:
- donetick.your-domain.com
```
Create the required secrets:
```bash
# Postgres secret
kubectl create secret generic donetick-postgres-secret \
--from-literal=username='donetick' \
--from-literal=password='your-secure-db-password'
# JWT secret
kubectl create secret generic donetick-jwt-secret \
--from-literal=jwt-secret='your-very-secure-jwt-secret-at-least-32-characters-long'
--from-literal=jwtSecret='your-very-secure-jwt-secret-at-least-32-characters-long'
# OAuth secret (if using OAuth)
kubectl create secret generic donetick-oauth-secret \
--from-literal=client-id='your-oauth-client-id' \
--from-literal=client-secret='your-oauth-client-secret'
```
Install with production configuration:
```bash
helm install donetick donetick-chart/donetick -f values-production.yaml
```
## Uninstalling the Chart
To uninstall/delete the `donetick` deployment:
```bash
helm uninstall donetick
```
## Parameters
### Global parameters
### Global Parameters
| Name | Description | Value |
|------------------------|-------------------------------------------------------------------------------------|-------|
| `nameOverride` | String to partially override the release name | `""` |
| `fullnameOverride` | String to fully override the release name | `""` |
| Name | Description | Default |
|------|-------------|---------|
| `nameOverride` | Override the release name | `""` |
| `fullnameOverride` | Fully override the release name | `""` |
### Image parameters
### Image Parameters
| Name | Description | Value |
|-------------------------|--------------------------------------------------------------------------------------|--------------------|
| Name | Description | Default |
|------|-------------|---------|
| `image.repository` | Donetick image repository | `donetick/donetick` |
| `image.tag` | Donetick image tag | `v0.1.60` |
| `image.pullPolicy` | Donetick image pull policy | `IfNotPresent` |
| `imagePullSecrets` | Global Docker registry secret names as an array | `[]` |
| `image.tag` | Image tag | `v0.1.60` |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
| `imagePullSecrets` | Image pull secrets | `[]` |
### Secret Management
### Deployment Parameters
| Name | Description | Value |
|----------------------------------------|--------------------------------------------------------------------|---------------------|
| `config.jwt.existingSecret` | Name of existing secret for JWT token | `""` |
| `config.jwt.secretKey` | Key in the existing secret for JWT token | `"jwtSecret"` |
| `config.oauth2.existingSecret` | Name of existing secret for OAuth2 credentials | `""` |
| `config.oauth2.clientIdKey` | Key in the existing secret for OAuth2 client ID | `"client-id"` |
| `config.oauth2.clientSecretKey` | Key in the existing secret for OAuth2 client secret | `"client-secret"` |
| `config.database.secrets.existingSecret` | Name of existing secret for postgres credentials | `""` |
| `config.database.secrets.userKey` | Key in the existing secret for postgres username | `"username"` |
| `config.database.secrets.passwordKey` | Key in the existing secret for postgres password | `"password"` |
| Name | Description | Default |
|------|-------------|---------|
| `replicaCount` | Number of replicas | `1` |
| `revisionHistoryLimit` | Revisions to retain | `3` |
| `startupArgs` | Optional startup arguments | `[]` |
| `podSecurityContext.runAsNonRoot` | Run as non-root | `true` |
| `podSecurityContext.runAsUser` | User ID | `1000` |
| `podSecurityContext.fsGroup` | Filesystem group ID | `1000` |
| `nodeSelector` | Node selector | `{}` |
| `tolerations` | Tolerations | `[]` |
| `affinity` | Affinity rules | `{}` |
| `podAnnotations` | Pod annotations | `{}` |
### Deployment parameters
### Service Parameters
| Name | Description | Value |
|--------------------------------------|--------------------------------------------------------------------------|-----------|
| `replicaCount` | Number of Donetick replicas | `1` |
| `revisionHistoryLimit` | Number of revisions to retain for rollback | `3` |
| `podSecurityContext.runAsNonRoot` | Run containers as non-root user | `true` |
| `podSecurityContext.runAsUser` | User ID for the container | `1000` |
| `podSecurityContext.fsGroup` | Group ID for the container filesystem | `1000` |
| `containerSecurityContext` | Security context for the container | See values.yaml |
| `nodeSelector` | Node labels for pod assignment | `{}` |
| `tolerations` | Tolerations for pod assignment | `[]` |
| `affinity` | Affinity for pod assignment | `{}` |
| Name | Description | Default |
|------|-------------|---------|
| `service.type` | Service type | `ClusterIP` |
| `service.port` | Service port | `2021` |
| `service.annotations` | Service annotations | `{}` |
### Service parameters
### Ingress Parameters
| Name | Description | Value |
|----------------------------|------------------------------------------------------|-------------|
| `service.type` | Kubernetes Service type | `ClusterIP` |
| `service.port` | Service HTTP port | `2021` |
| `service.annotations` | Additional annotations for Service | `{}` |
### Pod Configuration
| Name | Description | Value |
|----------------------------|------------------------------------------------------|-------------|
| `podAnnotations` | Additional annotations for pods | `{}` |
### Ingress parameters
| Name | Description | Value |
|----------------------------|------------------------------------------------------|----------------------|
| `ingress.enabled` | Enable ingress record generation | `true` |
| `ingress.className` | IngressClass name | `"traefik"` |
| `ingress.annotations` | Additional annotations for the Ingress resource | See values.yaml |
| `ingress.hosts` | Array of host and path objects | See values.yaml |
| `ingress.tlsSecretName` | Global TLS secret name for all hosts | `""` |
| Name | Description | Default |
|------|-------------|---------|
| `ingress.enabled` | Enable ingress | `false` |
| `ingress.className` | Ingress class name | `""` |
| `ingress.annotations` | Ingress annotations | See values.yaml |
| `ingress.hosts` | Ingress hosts | See values.yaml |
| `ingress.tls` | TLS configuration | See values.yaml |
| `ingress.tls[].secretName` | Host-specific TLS secret name (overrides global) | `""` |
### Persistence parameters
### Persistence Parameters
| Name | Description | Value |
|-------------------------------|------------------------------------------------------|---------------|
| `persistence.enabled` | Enable persistence using PVC | `false` |
| `persistence.storageClass` | PVC Storage Class | `""` |
| `persistence.accessMode` | PVC Access Mode | `ReadWriteOnce` |
| `persistence.size` | PVC Size | `1Gi` |
### Health Checks
| Name | Description | Value |
|----------------------------------------|------------------------------------------------------|---------------|
| `probes.startup.enabled` | Enable startup probe | `true` |
| `probes.startup.initialDelaySeconds` | Initial delay for startup probe | `10` |
| `probes.startup.periodSeconds` | Period for startup probe | `10` |
| `probes.startup.failureThreshold` | Failure threshold for startup probe | `30` |
| `probes.liveness.enabled` | Enable liveness probe | `true` |
| `probes.liveness.initialDelaySeconds` | Initial delay for liveness probe | `30` |
| `probes.liveness.periodSeconds` | Period for liveness probe | `10` |
| `probes.readiness.enabled` | Enable readiness probe | `true` |
| `probes.readiness.initialDelaySeconds` | Initial delay for readiness probe | `5` |
| `probes.readiness.periodSeconds` | Period for readiness probe | `5` |
### Application Configuration
| Name | Description | Value |
|----------------------------------------|------------------------------------------------------|---------------|
| `config.name` | Application name | `selfhosted` |
| `config.is_done_tick_dot_com` | Enable donetick.com features | `false` |
| `config.is_user_creation_disabled` | Disable user registration | `false` |
### Real-time Configuration
| Name | Description | Value |
|----------------------------------------|------------------------------------------------------|---------------|
| `config.realtime.max_connections` | Maximum WebSocket connections | `100` |
| `config.realtime.ping_interval` | WebSocket ping interval | `30s` |
| `config.realtime.pong_wait` | WebSocket pong wait timeout | `60s` |
| `config.realtime.write_wait` | WebSocket write timeout | `10s` |
| `config.realtime.max_message_size` | Maximum WebSocket message size | `512` |
| Name | Description | Default |
|------|-------------|---------|
| `persistence.enabled` | Enable persistence | `false` |
| `persistence.storageClass` | Storage class | `""` |
| `persistence.accessMode` | Access mode | `ReadWriteOnce` |
| `persistence.size` | PVC size | `1Gi` |
| `persistence.annotations` | PVC annotations | `{}` |
### Database Configuration
| Name | Description | Value |
|----------------------------------------|------------------------------------------------------|---------------|
| `config.database.type` | Database type (sqlite or postgres) | `sqlite` |
| `config.database.migration` | Enable database migrations | `true` |
| `config.database.host` | PostgreSQL host (when type=postgres) | `""` |
| `config.database.port` | PostgreSQL port (when type=postgres) | `5432` |
| `config.database.user` | PostgreSQL user (when type=postgres) | `""` |
| `config.database.password` | PostgreSQL password (when type=postgres) | `""` |
| `config.database.name` | PostgreSQL database name (when type=postgres) | `""` |
| Name | Description | Default |
|------|-------------|---------|
| `config.database.type` | Database type (`sqlite` or `postgres`) | `sqlite` |
| `config.database.migration` | Enable migrations | `true` |
| `config.database.migration_skip` | Skip migrations | `false` |
| `config.database.migration_retry` | Migration retry count | `3` |
| `config.database.migration_timeout` | Migration timeout | `600s` |
| `config.database.host` | PostgreSQL host | `""` |
| `config.database.port` | PostgreSQL port | `5432` |
| `config.database.name` | PostgreSQL database name | `""` |
| `config.database.secrets.existingSecret` | Existing secret for credentials | `""` |
| `config.database.secrets.userKey` | Key for username in secret | `username` |
| `config.database.secrets.passwordKey` | Key for password in secret | `password` |
### JWT Configuration
| Name | Description | Value |
|----------------------------------------|------------------------------------------------------|---------------|
| `config.jwt.secret` | JWT signing secret | `changeme-this-secret-should-be-at-least-32-characters-long` |
| `config.jwt.session_time` | JWT session duration | `168h` |
| `config.jwt.max_refresh` | JWT maximum refresh duration | `168h` |
| Name | Description | Default |
|------|-------------|---------|
| `config.jwt.secret` | JWT signing secret (min 32 chars) | `changeme-...` |
| `config.jwt.session_time` | Session duration | `168h` |
| `config.jwt.max_refresh` | Max refresh duration | `168h` |
| `config.jwt.existingSecret` | Existing secret for JWT | `""` |
| `config.jwt.secretKey` | Key in secret | `jwtSecret` |
### Server Configuration
| Name | Description | Value |
|----------------------------------------|------------------------------------------------------|---------------|
| Name | Description | Default |
|------|-------------|---------|
| `config.server.port` | Server port | `2021` |
| `config.server.read_timeout` | Server read timeout | `10s` |
| `config.server.write_timeout` | Server write timeout | `10s` |
| `config.server.read_timeout` | Read timeout | `10s` |
| `config.server.write_timeout` | Write timeout | `10s` |
| `config.server.rate_period` | Rate limiting period | `60s` |
| `config.server.rate_limit` | Rate limiting requests per period | `300` |
| `config.server.rate_limit` | Rate limit per period | `300` |
| `config.server.serve_frontend` | Serve frontend files | `true` |
| `config.server.cors_allow_origins` | CORS allowed origins | See values.yaml |
### OAuth2 Configuration
| Name | Description | Default |
|------|-------------|---------|
| `config.oauth2.client_id` | OAuth2 client ID | `""` |
| `config.oauth2.client_secret` | OAuth2 client secret | `""` |
| `config.oauth2.existingSecret` | Existing secret for credentials | `""` |
| `config.oauth2.clientIdKey` | Key for client ID in secret | `client-id` |
| `config.oauth2.clientSecretKey` | Key for client secret in secret | `client-secret` |
| `config.oauth2.auth_url` | Authorization URL | `""` |
| `config.oauth2.token_url` | Token URL | `""` |
| `config.oauth2.user_info_url` | User info URL | `""` |
| `config.oauth2.redirect_url` | Redirect URL | `""` |
### Real-time Configuration
| Name | Description | Default |
|------|-------------|---------|
| `config.realtime.max_connections` | Max WebSocket connections | `100` |
| `config.realtime.ping_interval` | Ping interval | `30s` |
| `config.realtime.pong_wait` | Pong wait timeout | `60s` |
| `config.realtime.write_wait` | Write timeout | `10s` |
| `config.realtime.max_message_size` | Max message size | `512` |
### Notification Configuration
| Name | Description | Default |
|------|-------------|---------|
| `config.telegram.token` | Telegram bot token | `""` |
| `config.pushover.token` | Pushover token | `""` |
### Feature Flags
| Name | Description | Value |
|----------------------------------------|------------------------------------------------------|---------------|
| Name | Description | Default |
|------|-------------|---------|
| `config.features.notifications` | Enable notifications | `true` |
| `config.features.realtime` | Enable real-time features | `true` |
| `config.features.oauth` | Enable OAuth authentication | `false` |
| `config.features.oauth` | Enable OAuth | `false` |
| `config.is_user_creation_disabled` | Disable user registration | `false` |
## Database Setup
### Resource Parameters
### PostgreSQL Requirements
| Name | Description | Default |
|------|-------------|---------|
| `resources` | Resource limits and requests | `{}` |
When using PostgreSQL, ensure you have:
### Health Check Parameters
1. **Database Created**: Create a database for Donetick
```sql
CREATE DATABASE donetick;
CREATE USER donetick WITH PASSWORD 'your-secure-password';
GRANT ALL PRIVILEGES ON DATABASE donetick TO donetick;
```
| Name | Description | Default |
|------|-------------|---------|
| `probes.startup.enabled` | Enable startup probe | `true` |
| `probes.startup.path` | Startup probe path | `/health` |
| `probes.startup.initialDelaySeconds` | Startup initial delay | `30` |
| `probes.startup.periodSeconds` | Startup period | `15` |
| `probes.startup.failureThreshold` | Startup failure threshold | `80` |
| `probes.liveness.enabled` | Enable liveness probe | `true` |
| `probes.liveness.path` | Liveness probe path | `/health` |
| `probes.liveness.initialDelaySeconds` | Liveness initial delay | `30` |
| `probes.liveness.periodSeconds` | Liveness period | `10` |
| `probes.readiness.enabled` | Enable readiness probe | `true` |
| `probes.readiness.path` | Readiness probe path | `/health` |
| `probes.readiness.initialDelaySeconds` | Readiness initial delay | `5` |
| `probes.readiness.periodSeconds` | Readiness period | `5` |
2. **Network Access**: Ensure Donetick can reach your PostgreSQL instance
3. **Proper Credentials**: Configure database credentials in values or secrets
### Autoscaling Parameters
### Database Migration
Donetick automatically runs database migrations on startup when `config.database.migration: true`. For production:
1. **Review Migrations**: Check what migrations will be applied
2. **Backup Database**: Always backup before running migrations
3. **Monitor Startup**: Watch pod logs during initial deployment
| Name | Description | Default |
|------|-------------|---------|
| `autoscaling.enabled` | Enable HPA | `false` |
| `autoscaling.minReplicas` | Min replicas | `1` |
| `autoscaling.maxReplicas` | Max replicas | `5` |
| `autoscaling.targetCPUUtilizationPercentage` | Target CPU | `80` |
| `autoscaling.targetMemoryUtilizationPercentage` | Target memory | `80` |
## Troubleshooting
### Common Issues
### Real-time Configuration Panic
#### 1. Real-time Configuration Panic
**Error**: `Invalid real-time configuration: maxConnections must be positive, got 0`
**Solution**: Ensure the real-time configuration is properly set:
```yaml
config:
realtime:
max_connections: 100 # Must be > 0
```
Ensure `config.realtime.max_connections` is set to a positive value (default: `100`).
#### 2. Database Connection Issues
**Error**: Database connection failures
### Database Connection Issues
**Solutions**:
- Verify PostgreSQL is running and accessible
- Check database credentials in secrets
- Ensure database name exists
- Verify network policies allow connection
- Ensure the database exists
- Verify network policies allow the connection
#### 3. JWT Secret Issues
**Error**: JWT authentication failures
### JWT Authentication Failures
**Solution**: Ensure JWT secret is at least 32 characters:
```yaml
config:
jwt:
secret: "your-very-secure-jwt-secret-at-least-32-characters-long"
```
Ensure `config.jwt.secret` is at least 32 characters long. For production, use `config.jwt.existingSecret`.
#### 4. CORS Issues
**Error**: Cross-origin request blocked
### CORS Issues
Add your domain to `config.server.cors_allow_origins`:
**Solution**: Configure CORS origins:
```yaml
config:
server:
cors_allow_origins:
- "https://your-domain.com"
- "http://localhost:5173"
```
### Debugging
Check application logs:
```bash
kubectl logs deployment/donetick -f
```
Check configuration:
```bash
kubectl get configmap donetick-configmap -o yaml
```
Verify secrets:
```bash
kubectl get secret donetick-secrets -o yaml
```
## Links
## Contributing
Please feel free to contribute by opening issues or pull requests at:
https://github.com/rtomik/helm-charts
## License
This Helm chart is licensed under the MIT License.
- [Donetick GitHub](https://github.com/donetick/donetick)
- [Chart Source](https://github.com/rtomik/helm-charts/tree/main/charts/donetick)

View File

@ -4,10 +4,9 @@ A Helm chart for deploying [Jellyseerr](https://github.com/fallenbagel/jellyseer
## Introduction
This chart deploys Jellyseerr on a Kubernetes cluster using the Helm package manager. Jellyseerr is a fork of Overseerr for Jellyfin support.
This chart deploys Jellyseerr, a media request management application for Jellyfin, on a Kubernetes cluster using the Helm package manager. Jellyseerr is a fork of Overseerr with native Jellyfin support.
Source code can be found here:
- https://github.com/rtomik/helm-charts/tree/main/charts/jellyseerr
Source code: https://github.com/rtomik/helm-charts/tree/main/charts/jellyseerr
## Prerequisites
@ -17,104 +16,35 @@ Source code can be found here:
## Installing the Chart
To install the chart with the release name `jellyseerr`:
```bash
helm repo add rtomik-charts https://rtomik.github.io/helm-charts
helm install jellyseerr rtomik-charts/jellyseerr
helm repo add rtomik https://rtomik.github.io/helm-charts
helm install jellyseerr rtomik/jellyseerr
```
> **Tip**: List all releases using `helm list`
## Uninstalling the Chart
To uninstall/delete the `jellyseerr` deployment:
```bash
helm uninstall jellyseerr
```
## Parameters
## Configuration Examples
### Global parameters
### Minimal Installation
| Name | Description | Value |
|------------------------|---------------------------------------------------------------|--------|
| `nameOverride` | String to partially override the release name | `""` |
| `fullnameOverride` | String to fully override the release name | `""` |
```yaml
ingress:
enabled: true
hosts:
- host: jellyseerr.example.com
paths:
- path: /
pathType: Prefix
tls:
- hosts:
- jellyseerr.example.com
```
### Image parameters
| Name | Description | Value |
|-------------------------|--------------------------------------------------------------|--------------------------------|
| `image.repository` | Jellyseerr image repository | `ghcr.io/fallenbagel/jellyseerr` |
| `image.tag` | Jellyseerr image tag | `latest` |
| `image.pullPolicy` | Jellyseerr image pull policy | `IfNotPresent` |
| `imagePullSecrets` | Global Docker registry secret names as an array | `[]` |
### Deployment parameters
| Name | Description | Value |
|--------------------------------------|--------------------------------------------------|-----------|
| `replicaCount` | Number of Jellyseerr replicas | `1` |
| `revisionHistoryLimit` | Number of revisions to retain for rollback | `3` |
| `podSecurityContext.runAsNonRoot` | Run containers as non-root user | `true` |
| `podSecurityContext.runAsUser` | User ID for the container | `1000` |
| `podSecurityContext.fsGroup` | Group ID for the container filesystem | `1000` |
| `containerSecurityContext` | Security context for the container | See values.yaml |
| `nodeSelector` | Node labels for pod assignment | `{}` |
| `tolerations` | Tolerations for pod assignment | `[]` |
| `affinity` | Affinity for pod assignment | `{}` |
### Service parameters
| Name | Description | Value |
|----------------------------|----------------------------------------------|-------------|
| `service.type` | Kubernetes Service type | `ClusterIP` |
| `service.port` | Service HTTP port | `5055` |
### Ingress parameters
| Name | Description | Value |
|----------------------------|----------------------------------------------|------------------------|
| `ingress.enabled` | Enable ingress record generation | `false` |
| `ingress.className` | IngressClass name | `""` |
| `ingress.annotations` | Additional annotations for the Ingress resource | `{}` |
| `ingress.hosts` | Array of host and path objects | See values.yaml |
| `ingress.tls` | TLS configuration | `[]` |
### Persistence parameters
| Name | Description | Value |
|-------------------------------|----------------------------------------------|-----------------|
| `persistence.enabled` | Enable persistence using PVC | `true` |
| `persistence.existingClaim` | Use an existing PVC | `""` |
| `persistence.storageClass` | PVC Storage Class | `""` |
| `persistence.accessMode` | PVC Access Mode | `ReadWriteOnce` |
| `persistence.size` | PVC Storage Size | `1Gi` |
| `persistence.annotations` | Additional custom annotations for the PVC | `{}` |
### Environment variables
| Name | Description | Value |
|--------------------------|----------------------------------------------|-----------------|
| `env` | Environment variables for Jellyseerr | See values.yaml |
| `extraEnv` | Additional environment variables | `[]` |
### Resources parameters
| Name | Description | Value |
|--------------------------|----------------------------------------------|-----------------|
| `resources.limits` | The resources limits for containers | See values.yaml |
| `resources.requests` | The resources requests for containers | See values.yaml |
## Configuration
The following table lists the configurable parameters of the Jellyseerr chart and their default values.
### Environment Variables
You can configure Jellyseerr by setting environment variables:
### Custom Timezone and Logging
```yaml
env:
@ -126,20 +56,110 @@ env:
value: "5055"
```
### Using Persistence
By default, persistence is enabled with a 1Gi volume:
```yaml
persistence:
enabled: true
size: 1Gi
```
You can also use an existing PVC:
### Using an Existing PVC
```yaml
persistence:
enabled: true
existingClaim: my-jellyseerr-pvc
```
## Parameters
### Global Parameters
| Name | Description | Default |
|------|-------------|---------|
| `nameOverride` | Override the release name | `""` |
| `fullnameOverride` | Fully override the release name | `""` |
### Image Parameters
| Name | Description | Default |
|------|-------------|---------|
| `image.repository` | Jellyseerr image repository | `ghcr.io/fallenbagel/jellyseerr` |
| `image.tag` | Image tag | `2.5.2` |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
### Deployment Parameters
| Name | Description | Default |
|------|-------------|---------|
| `replicaCount` | Number of replicas | `1` |
| `revisionHistoryLimit` | Revisions to retain | `3` |
| `podSecurityContext.runAsNonRoot` | Run as non-root | `true` |
| `podSecurityContext.runAsUser` | User ID | `1000` |
| `podSecurityContext.fsGroup` | Filesystem group ID | `1000` |
| `nodeSelector` | Node selector | `{}` |
| `tolerations` | Tolerations | `[]` |
| `affinity` | Affinity rules | `{}` |
| `podAnnotations` | Pod annotations | `{}` |
### Service Parameters
| Name | Description | Default |
|------|-------------|---------|
| `service.type` | Service type | `ClusterIP` |
| `service.port` | Service port | `5055` |
### Ingress Parameters
| Name | Description | Default |
|------|-------------|---------|
| `ingress.enabled` | Enable ingress | `false` |
| `ingress.className` | Ingress class name | `""` |
| `ingress.annotations` | Ingress annotations | `{}` |
| `ingress.hosts` | Ingress hosts | See values.yaml |
| `ingress.tls` | TLS configuration | `[]` |
### Persistence Parameters
| Name | Description | Default |
|------|-------------|---------|
| `persistence.enabled` | Enable persistence | `true` |
| `persistence.existingClaim` | Use an existing PVC | `""` |
| `persistence.storageClass` | Storage class | `""` |
| `persistence.accessMode` | Access mode | `ReadWriteOnce` |
| `persistence.size` | PVC size | `1Gi` |
| `persistence.annotations` | PVC annotations | `{}` |
### Environment Variables
| Name | Description | Default |
|------|-------------|---------|
| `env` | Environment variables | See values.yaml |
| `extraEnv` | Additional environment variables | `[]` |
### Resource Parameters
| Name | Description | Default |
|------|-------------|---------|
| `resources` | Resource limits and requests | `{}` |
### Health Check Parameters
| Name | Description | Default |
|------|-------------|---------|
| `probes.liveness.enabled` | Enable liveness probe | `true` |
| `probes.liveness.path` | Liveness probe path | `/api/v1/status` |
| `probes.liveness.initialDelaySeconds` | Liveness initial delay | `30` |
| `probes.liveness.periodSeconds` | Liveness period | `10` |
| `probes.readiness.enabled` | Enable readiness probe | `true` |
| `probes.readiness.path` | Readiness probe path | `/api/v1/status` |
| `probes.readiness.initialDelaySeconds` | Readiness initial delay | `5` |
| `probes.readiness.periodSeconds` | Readiness period | `5` |
## Troubleshooting
- **Application not starting**: Check that persistence is enabled and the PVC is accessible
- **Timezone issues**: Set the `TZ` environment variable to your local timezone
```bash
kubectl logs deployment/jellyseerr -f
kubectl describe pod -l app.kubernetes.io/name=jellyseerr
```
## Links
- [Jellyseerr GitHub](https://github.com/fallenbagel/jellyseerr)
- [Chart Source](https://github.com/rtomik/helm-charts/tree/main/charts/jellyseerr)

View File

@ -1,266 +1,40 @@
# Joplin Server Helm Chart
A Helm chart for deploying Joplin Server on Kubernetes - Note-taking and synchronization server.
A Helm chart for deploying [Joplin Server](https://github.com/laurent22/joplin) on Kubernetes.
## Introduction
This chart deploys [Joplin Server](https://github.com/laurent22/joplin) on a Kubernetes cluster using the Helm package manager. Joplin Server is the synchronization server for Joplin, allowing you to sync your notes across devices.
This chart deploys Joplin Server, the synchronization server for the Joplin note-taking application, on a Kubernetes cluster. Joplin Server allows syncing notes across devices and supports filesystem or S3 storage, email notifications, and an optional AI transcription service.
Source code can be found here:
- https://github.com/rtomik/helm-charts/tree/main/charts/joplin-server
Source code: https://github.com/rtomik/helm-charts/tree/main/charts/joplin-server
## Prerequisites
- Kubernetes 1.19+
- Helm 3.0+
- **External PostgreSQL database** (Required - Joplin Server does not support SQLite in production)
- PV provisioner support in the underlying infrastructure (if persistence is needed for file storage)
- **External PostgreSQL database** (required Joplin Server does not support SQLite)
- PV provisioner support (if using filesystem storage)
## Installing the Chart
To install the chart with the release name `joplin-server`:
```bash
$ helm repo add joplin-chart https://rtomik.github.io/helm-charts
$ helm install joplin-server joplin-chart/joplin-server
helm repo add rtomik https://rtomik.github.io/helm-charts
helm install joplin-server rtomik/joplin-server
```
> **Important**: You must configure PostgreSQL database settings before installation.
> **Important**: Configure PostgreSQL database settings before installation.
## Uninstalling the Chart
To uninstall/delete the `joplin-server` deployment:
```bash
$ helm uninstall joplin-server
helm uninstall joplin-server
```
## Parameters
### Global parameters
| Name | Description | Value |
|------------------------|------------------------------------------------|-------|
| `nameOverride` | String to partially override the release name | `""` |
| `fullnameOverride` | String to fully override the release name | `""` |
### Image parameters
| Name | Description | Value |
|-------------------------|-----------------------------------|--------------------|
| `image.repository` | Joplin Server image repository | `joplin/server` |
| `image.tag` | Joplin Server image tag | `latest` |
| `image.pullPolicy` | Joplin Server image pull policy | `IfNotPresent` |
### Deployment parameters
| Name | Description | Value |
|--------------------------------------|-----------------------------------------------|-----------|
| `replicaCount` | Number of Joplin Server replicas | `1` |
| `revisionHistoryLimit` | Number of revisions to retain for rollback | `3` |
| `podSecurityContext.runAsNonRoot` | Run containers as non-root user | `true` |
| `podSecurityContext.runAsUser` | User ID for the container | `1001` |
| `podSecurityContext.fsGroup` | Group ID for the container filesystem | `1001` |
| `containerSecurityContext` | Security context for the container | See values.yaml |
| `nodeSelector` | Node labels for pod assignment | `{}` |
| `tolerations` | Tolerations for pod assignment | `[]` |
| `affinity` | Affinity for pod assignment | `{}` |
### Service parameters
| Name | Description | Value |
|----------------|-----------------------|-------------|
| `service.type` | Kubernetes Service type | `ClusterIP` |
| `service.port` | Service HTTP port | `22300` |
### Ingress parameters
| Name | Description | Value |
|-------------------------|-------------------------------------------|-----------------|
| `ingress.enabled` | Enable ingress record generation | `false` |
| `ingress.className` | IngressClass name | `""` |
| `ingress.annotations` | Additional annotations for the Ingress | See values.yaml |
| `ingress.hosts` | Array of host and path objects | See values.yaml |
| `ingress.tls` | TLS configuration | See values.yaml |
### Environment variables
| Name | Description | Value |
|---------------------------|-----------------------------------------------|--------------------------|
| `env.APP_PORT` | Application port | `22300` |
| `env.APP_BASE_URL` | Base URL for the application | `http://localhost:22300` |
| `env.DB_CLIENT` | Database client (always pg for PostgreSQL) | `pg` |
### PostgreSQL configuration (Required)
| Name | Description | Value |
|----------------------------------------|-----------------------------------------------|-----------|
| `postgresql.external.enabled` | Use external PostgreSQL database (required) | `true` |
| `postgresql.external.host` | PostgreSQL host | `""` |
| `postgresql.external.port` | PostgreSQL port | `5432` |
| `postgresql.external.database` | PostgreSQL database name | `joplin` |
| `postgresql.external.user` | PostgreSQL username | `joplin` |
| `postgresql.external.password` | PostgreSQL password | `""` |
| `postgresql.external.existingSecret` | Name of existing secret with PostgreSQL credentials | `""` |
| `postgresql.external.userKey` | Key in the secret for username | `username` |
| `postgresql.external.passwordKey` | Key in the secret for password | `password` |
| `postgresql.external.hostKey` | Key in the secret for host (optional) | `""` |
| `postgresql.external.portKey` | Key in the secret for port (optional) | `""` |
| `postgresql.external.databaseKey` | Key in the secret for database name (optional) | `""` |
### Joplin Server Configuration
#### Admin Settings
| Name | Description | Value |
|----------------------------------------|-----------------------------------------------|-----------|
| `joplin.admin.email` | First admin user email | `""` |
| `joplin.admin.password` | First admin user password | `""` |
| `joplin.admin.existingSecret` | Name of existing secret with admin credentials | `""` |
| `joplin.admin.emailKey` | Key in the secret for admin email | `admin-email` |
| `joplin.admin.passwordKey` | Key in the secret for admin password | `admin-password` |
#### Server Settings
| Name | Description | Value |
|-------------------------------------------|-----------------------------------------------|-----------|
| `joplin.server.maxRequestBodySize` | Maximum request body size | `200mb` |
| `joplin.server.sessionTimeout` | Session timeout in seconds | `86400` |
| `joplin.server.enableUserRegistration` | Enable/disable user registration | `false` |
| `joplin.server.enableSharing` | Enable/disable sharing | `true` |
| `joplin.server.enablePublicNotes` | Enable/disable public notes | `true` |
#### Storage Settings
| Name | Description | Value |
|-------------------------------------------|-----------------------------------------------|--------------|
| `joplin.storage.driver` | Storage driver (filesystem, s3, azure) | `filesystem` |
| `joplin.storage.filesystemPath` | Path for filesystem storage | `/var/lib/joplin` |
##### S3 Storage (Optional)
| Name | Description | Value |
|---------------------------------------------|---------------------------------------------|-----------|
| `joplin.storage.s3.bucket` | S3 bucket name | `""` |
| `joplin.storage.s3.region` | S3 region | `""` |
| `joplin.storage.s3.accessKeyId` | S3 access key ID | `""` |
| `joplin.storage.s3.secretAccessKey` | S3 secret access key | `""` |
| `joplin.storage.s3.endpoint` | S3 endpoint (for S3-compatible services) | `""` |
| `joplin.storage.s3.existingSecret` | Name of existing secret with S3 credentials | `""` |
| `joplin.storage.s3.accessKeyIdKey` | Key in the secret for access key ID | `access-key-id` |
| `joplin.storage.s3.secretAccessKeyKey` | Key in the secret for secret access key | `secret-access-key` |
#### Email Settings (Optional)
| Name | Description | Value |
|----------------------------------------|-----------------------------------------------|-----------|
| `joplin.email.enabled` | Enable email notifications | `false` |
| `joplin.email.host` | SMTP host | `""` |
| `joplin.email.port` | SMTP port | `587` |
| `joplin.email.username` | SMTP username | `""` |
| `joplin.email.password` | SMTP password | `""` |
| `joplin.email.fromEmail` | From email address | `""` |
| `joplin.email.fromName` | From name | `Joplin Server` |
| `joplin.email.secure` | Use TLS/SSL | `true` |
| `joplin.email.existingSecret` | Name of existing secret with email credentials | `""` |
| `joplin.email.usernameKey` | Key in the secret for SMTP username | `email-username` |
| `joplin.email.passwordKey` | Key in the secret for SMTP password | `email-password` |
#### Logging Settings
| Name | Description | Value |
|--------------------------------|--------------------------------------|-----------|
| `joplin.logging.level` | Log level (error, warn, info, debug) | `info` |
| `joplin.logging.target` | Log target (console, file) | `console` |
### Persistence settings (for filesystem storage)
| Name | Description | Value |
|-------------------------------|----------------------------------|-----------------|
| `persistence.enabled` | Enable persistence using PVC | `true` |
| `persistence.storageClass` | PVC Storage Class | `""` |
| `persistence.accessMode` | PVC Access Mode | `ReadWriteOnce` |
| `persistence.size` | PVC Size | `10Gi` |
| `persistence.annotations` | Annotations for PVC | `{}` |
### Transcribe Service (Optional AI Transcription)
| Name | Description | Value |
|-------------------------------------------|-----------------------------------------------|--------------|
| `transcribe.enabled` | Enable transcribe service | `false` |
| `transcribe.image.repository` | Transcribe image repository | `joplin/transcribe` |
| `transcribe.image.tag` | Transcribe image tag | `latest` |
| `transcribe.image.pullPolicy` | Transcribe image pull policy | `IfNotPresent` |
| `transcribe.api.key` | Shared secret between Joplin and Transcribe | `""` |
| `transcribe.api.existingSecret` | Name of existing secret with transcribe API key | `""` |
| `transcribe.api.keyName` | Key in the secret for transcribe API key | `transcribe-api-key` |
| `transcribe.service.type` | Transcribe service type | `ClusterIP` |
| `transcribe.service.port` | Transcribe service port | `4567` |
| `transcribe.htr.imagesFolder` | HTR images folder path | `/app/images` |
#### Transcribe Database (Separate from main database)
| Name | Description | Value |
|---------------------------------------------|---------------------------------------------|-------------|
| `transcribe.database.host` | Transcribe database host | `""` |
| `transcribe.database.port` | Transcribe database port | `5432` |
| `transcribe.database.database` | Transcribe database name | `transcribe` |
| `transcribe.database.user` | Transcribe database username | `transcribe` |
| `transcribe.database.password` | Transcribe database password | `""` |
| `transcribe.database.existingSecret` | Name of existing secret with transcribe DB credentials | `""` |
| `transcribe.database.userKey` | Key in the secret for username | `username` |
| `transcribe.database.passwordKey` | Key in the secret for password | `password` |
### Resource Configuration
| Name | Description | Value |
|-------------|--------------------------------------|-------|
| `resources` | Resource limits and requests | `{}` |
### Health Checks
| Name | Description | Value |
|-------------------------------------------|------------------------------------------|-------|
| `probes.liveness.enabled` | Enable liveness probe | `true` |
| `probes.liveness.initialDelaySeconds` | Initial delay for liveness probe | `60` |
| `probes.liveness.periodSeconds` | Period for liveness probe | `30` |
| `probes.liveness.timeoutSeconds` | Timeout for liveness probe | `10` |
| `probes.liveness.failureThreshold` | Failure threshold for liveness probe | `3` |
| `probes.liveness.successThreshold` | Success threshold for liveness probe | `1` |
| `probes.liveness.path` | Path for liveness probe | `/api/ping` |
| `probes.liveness.httpHeaders` | HTTP headers for liveness probe | `[{"name": "Host", "value": "joplin.domain.com"}]` |
| `probes.readiness.enabled` | Enable readiness probe | `true` |
| `probes.readiness.initialDelaySeconds` | Initial delay for readiness probe | `30` |
| `probes.readiness.periodSeconds` | Period for readiness probe | `10` |
| `probes.readiness.timeoutSeconds` | Timeout for readiness probe | `5` |
| `probes.readiness.failureThreshold` | Failure threshold for readiness probe | `3` |
| `probes.readiness.successThreshold` | Success threshold for readiness probe | `1` |
| `probes.readiness.path` | Path for readiness probe | `/api/ping` |
| `probes.readiness.httpHeaders` | HTTP headers for readiness probe | `[{"name": "Host", "value": "joplin.domain.com"}]` |
### Autoscaling
| Name | Description | Value |
|---------------------------------------------|------------------------------------------|---------|
| `autoscaling.enabled` | Enable horizontal pod autoscaling | `false` |
| `autoscaling.minReplicas` | Minimum number of replicas | `1` |
| `autoscaling.maxReplicas` | Maximum number of replicas | `3` |
| `autoscaling.targetCPUUtilizationPercentage`| Target CPU utilization percentage | `80` |
| `autoscaling.targetMemoryUtilizationPercentage`| Target memory utilization percentage | `80` |
### Security Settings
| Name | Description | Value |
|-----------------------------------|--------------------------------------|---------|
| `security.httpsRedirect` | Enable/disable HTTPS redirect | `false` |
| `security.tls.enabled` | Enable custom TLS certificate | `false` |
| `security.tls.existingSecret` | Name of existing secret with TLS cert| `""` |
| `security.tls.certificateKey` | Key in the secret for TLS certificate| `tls.crt` |
| `security.tls.privateKeyKey` | Key in the secret for TLS private key| `tls.key` |
## Configuration Examples
### Basic Installation with PostgreSQL
### Minimal Installation
> **Important**: Health check probes require a `Host` header matching your ingress domain. Update `probes.*.httpHeaders` accordingly.
```yaml
postgresql:
@ -272,22 +46,9 @@ postgresql:
user: "joplin"
password: "secure-password"
ingress:
enabled: true
hosts:
- host: joplin.example.com
paths:
- path: /
pathType: Prefix
tls:
- hosts:
- joplin.example.com
secretName: joplin-tls
env:
APP_BASE_URL: "https://joplin.example.com"
# IMPORTANT: Update health check host headers to match your domain
probes:
liveness:
httpHeaders:
@ -304,30 +65,22 @@ joplin:
password: "admin-password"
server:
enableUserRegistration: true
```
### Using Kubernetes Secrets
#### Full Secret Configuration
```yaml
postgresql:
external:
ingress:
enabled: true
existingSecret: "joplin-postgresql-secret"
hostKey: "host"
portKey: "port"
databaseKey: "database"
userKey: "username"
passwordKey: "password"
joplin:
admin:
existingSecret: "joplin-admin-secret"
emailKey: "email"
passwordKey: "password"
hosts:
- host: joplin.example.com
paths:
- path: /
pathType: Prefix
tls:
- hosts:
- joplin.example.com
secretName: joplin-tls
```
#### Mixed Configuration (Host in values, credentials in secret)
### Production with Existing Secrets
```yaml
postgresql:
external:
@ -338,10 +91,15 @@ postgresql:
existingSecret: "joplin-db-credentials"
userKey: "username"
passwordKey: "password"
# hostKey, portKey, databaseKey left empty - using values above
joplin:
admin:
existingSecret: "joplin-admin-secret"
emailKey: "email"
passwordKey: "password"
```
### S3 Storage Configuration
### S3 Storage
```yaml
joplin:
@ -359,7 +117,7 @@ persistence:
enabled: false
```
### Email Notifications Setup
### Email Notifications
```yaml
joplin:
@ -375,7 +133,7 @@ joplin:
passwordKey: "password"
```
### Transcribe Service (AI Features)
### Transcribe Service (AI Transcription)
```yaml
transcribe:
@ -396,32 +154,206 @@ transcribe:
size: 5Gi
```
## First-time Setup
## Parameters
1. **Configure PostgreSQL**: Ensure your PostgreSQL database is accessible and credentials are configured
2. **Admin User**: Set admin email/password or access the web interface to create the first admin user
3. **User Registration**: Configure whether users can self-register or admin approval is required
4. **Storage**: Choose between filesystem (requires persistence) or cloud storage (S3/Azure)
### Global Parameters
## Security Considerations
| Name | Description | Default |
|------|-------------|---------|
| `nameOverride` | Override the release name | `""` |
| `fullnameOverride` | Fully override the release name | `""` |
For production deployments:
### Image Parameters
1. Use external secrets for all sensitive information (database passwords, admin credentials, etc.)
2. Enable TLS/SSL for all communications
3. Configure proper RBAC and network policies
4. Use dedicated databases with proper access controls
5. Disable user registration if not needed
6. Use cloud storage for better scalability and backup
| Name | Description | Default |
|------|-------------|---------|
| `image.repository` | Joplin Server image repository | `joplin/server` |
| `image.tag` | Image tag | `3.4.2` |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
### Deployment Parameters
| Name | Description | Default |
|------|-------------|---------|
| `replicaCount` | Number of replicas | `1` |
| `revisionHistoryLimit` | Revisions to retain | `3` |
| `podSecurityContext.runAsNonRoot` | Run as non-root | `true` |
| `podSecurityContext.runAsUser` | User ID | `1001` |
| `podSecurityContext.fsGroup` | Filesystem group ID | `1001` |
| `nodeSelector` | Node selector | `{}` |
| `tolerations` | Tolerations | `[]` |
| `affinity` | Affinity rules | `{}` |
### Service Parameters
| Name | Description | Default |
|------|-------------|---------|
| `service.type` | Service type | `ClusterIP` |
| `service.port` | Service port | `22300` |
### Ingress Parameters
| Name | Description | Default |
|------|-------------|---------|
| `ingress.enabled` | Enable ingress | `false` |
| `ingress.className` | Ingress class name | `""` |
| `ingress.annotations` | Ingress annotations | See values.yaml |
| `ingress.hosts` | Ingress hosts | See values.yaml |
| `ingress.tls` | TLS configuration | See values.yaml |
### PostgreSQL Configuration (Required)
| Name | Description | Default |
|------|-------------|---------|
| `postgresql.external.enabled` | Use external PostgreSQL | `true` |
| `postgresql.external.host` | PostgreSQL host | `""` |
| `postgresql.external.port` | PostgreSQL port | `5432` |
| `postgresql.external.database` | Database name | `joplin` |
| `postgresql.external.user` | Username | `joplin` |
| `postgresql.external.password` | Password | `""` |
| `postgresql.external.existingSecret` | Existing secret name | `""` |
| `postgresql.external.userKey` | Key for username in secret | `username` |
| `postgresql.external.passwordKey` | Key for password in secret | `password` |
| `postgresql.external.hostKey` | Key for host in secret (optional) | `""` |
| `postgresql.external.portKey` | Key for port in secret (optional) | `""` |
| `postgresql.external.databaseKey` | Key for database in secret (optional) | `""` |
### Admin Settings
| Name | Description | Default |
|------|-------------|---------|
| `joplin.admin.email` | Admin user email | `""` |
| `joplin.admin.password` | Admin user password | `""` |
| `joplin.admin.existingSecret` | Existing secret for admin credentials | `""` |
| `joplin.admin.emailKey` | Key for email in secret | `admin-email` |
| `joplin.admin.passwordKey` | Key for password in secret | `admin-password` |
### Server Settings
| Name | Description | Default |
|------|-------------|---------|
| `joplin.server.maxRequestBodySize` | Max request body size | `200mb` |
| `joplin.server.sessionTimeout` | Session timeout (seconds) | `86400` |
| `joplin.server.enableUserRegistration` | Enable user registration | `false` |
| `joplin.server.enableSharing` | Enable sharing | `true` |
| `joplin.server.enablePublicNotes` | Enable public notes | `true` |
### Storage Settings
| Name | Description | Default |
|------|-------------|---------|
| `joplin.storage.driver` | Storage driver (`filesystem`, `s3`, `azure`) | `filesystem` |
| `joplin.storage.filesystemPath` | Filesystem storage path | `/var/lib/joplin` |
| `joplin.storage.s3.bucket` | S3 bucket name | `""` |
| `joplin.storage.s3.region` | S3 region | `""` |
| `joplin.storage.s3.endpoint` | S3 endpoint (for S3-compatible services) | `""` |
| `joplin.storage.s3.accessKeyId` | S3 access key ID | `""` |
| `joplin.storage.s3.secretAccessKey` | S3 secret access key | `""` |
| `joplin.storage.s3.existingSecret` | Existing secret for S3 credentials | `""` |
| `joplin.storage.s3.accessKeyIdKey` | Key for access key in secret | `access-key-id` |
| `joplin.storage.s3.secretAccessKeyKey` | Key for secret access key in secret | `secret-access-key` |
### Email Settings
| Name | Description | Default |
|------|-------------|---------|
| `joplin.email.enabled` | Enable email | `false` |
| `joplin.email.host` | SMTP host | `""` |
| `joplin.email.port` | SMTP port | `587` |
| `joplin.email.username` | SMTP username | `""` |
| `joplin.email.password` | SMTP password | `""` |
| `joplin.email.fromEmail` | From email address | `""` |
| `joplin.email.fromName` | From name | `Joplin Server` |
| `joplin.email.secure` | Use TLS/SSL | `true` |
| `joplin.email.existingSecret` | Existing secret for credentials | `""` |
| `joplin.email.usernameKey` | Key for username in secret | `email-username` |
| `joplin.email.passwordKey` | Key for password in secret | `email-password` |
### Logging Settings
| Name | Description | Default |
|------|-------------|---------|
| `joplin.logging.level` | Log level (`error`, `warn`, `info`, `debug`) | `info` |
| `joplin.logging.target` | Log target (`console`, `file`) | `console` |
### Persistence Parameters
| Name | Description | Default |
|------|-------------|---------|
| `persistence.enabled` | Enable persistence | `true` |
| `persistence.storageClass` | Storage class | `""` |
| `persistence.accessMode` | Access mode | `ReadWriteOnce` |
| `persistence.size` | PVC size | `10Gi` |
| `persistence.annotations` | PVC annotations | `{}` |
### Transcribe Service
| Name | Description | Default |
|------|-------------|---------|
| `transcribe.enabled` | Enable transcribe service | `false` |
| `transcribe.image.repository` | Transcribe image repository | `joplin/transcribe` |
| `transcribe.image.tag` | Transcribe image tag | `latest` |
| `transcribe.api.key` | Shared API key | `""` |
| `transcribe.api.existingSecret` | Existing secret for API key | `""` |
| `transcribe.api.keyName` | Key name in secret | `transcribe-api-key` |
| `transcribe.service.type` | Transcribe service type | `ClusterIP` |
| `transcribe.service.port` | Transcribe service port | `4567` |
| `transcribe.database.host` | Transcribe DB host | `""` |
| `transcribe.database.port` | Transcribe DB port | `5432` |
| `transcribe.database.database` | Transcribe DB name | `transcribe` |
| `transcribe.database.user` | Transcribe DB user | `transcribe` |
| `transcribe.database.password` | Transcribe DB password | `""` |
| `transcribe.database.existingSecret` | Existing secret for transcribe DB | `""` |
| `transcribe.database.userKey` | Key for username in secret | `username` |
| `transcribe.database.passwordKey` | Key for password in secret | `password` |
### Security Settings
| Name | Description | Default |
|------|-------------|---------|
| `security.httpsRedirect` | Enable HTTPS redirect | `false` |
| `security.tls.enabled` | Enable custom TLS certificate | `false` |
| `security.tls.existingSecret` | Secret with TLS certificate | `""` |
| `security.tls.certificateKey` | Key for TLS certificate in secret | `tls.crt` |
| `security.tls.privateKeyKey` | Key for TLS private key in secret | `tls.key` |
### Resource Parameters
| Name | Description | Default |
|------|-------------|---------|
| `resources` | Resource limits and requests | `{}` |
### Health Check Parameters
| Name | Description | Default |
|------|-------------|---------|
| `probes.liveness.enabled` | Enable liveness probe | `true` |
| `probes.liveness.path` | Liveness probe path | `/api/ping` |
| `probes.liveness.initialDelaySeconds` | Liveness initial delay | `60` |
| `probes.liveness.periodSeconds` | Liveness period | `30` |
| `probes.liveness.httpHeaders` | Liveness HTTP headers | Host matching ingress |
| `probes.readiness.enabled` | Enable readiness probe | `true` |
| `probes.readiness.path` | Readiness probe path | `/api/ping` |
| `probes.readiness.initialDelaySeconds` | Readiness initial delay | `30` |
| `probes.readiness.periodSeconds` | Readiness period | `10` |
| `probes.readiness.httpHeaders` | Readiness HTTP headers | Host matching ingress |
### Autoscaling Parameters
| Name | Description | Default |
|------|-------------|---------|
| `autoscaling.enabled` | Enable HPA | `false` |
| `autoscaling.minReplicas` | Min replicas | `1` |
| `autoscaling.maxReplicas` | Max replicas | `3` |
| `autoscaling.targetCPUUtilizationPercentage` | Target CPU | `80` |
| `autoscaling.targetMemoryUtilizationPercentage` | Target memory | `80` |
## Troubleshooting
Common issues and solutions:
### Health Check Failures / "No Available Server"
Health checks require the correct `Host` header matching your ingress domain:
1. **Health Check Issues / "No Available Server"**:
- Ensure `probes.*.httpHeaders` includes the correct Host header matching your domain
- Health checks use `/api/ping` endpoint which requires proper host validation
- Example fix:
```yaml
probes:
liveness:
@ -434,27 +366,22 @@ Common issues and solutions:
value: your-joplin-domain.com
```
2. **Database connection issues**: Verify PostgreSQL credentials and network connectivity
3. **Storage permissions**: Check filesystem permissions for persistent volumes
4. **First admin user**: If no admin configured, access the web interface to create one
5. **Transcribe issues**: Verify Docker socket access and separate database configuration
6. **Origin validation errors**: Make sure `env.APP_BASE_URL` matches your ingress host
### Database Connection Issues
For detailed troubleshooting, check the application logs:
Verify PostgreSQL credentials, network connectivity, and that `env.APP_BASE_URL` matches your ingress host.
### Origin Validation Errors
Ensure `env.APP_BASE_URL` matches your ingress hostname exactly.
### Debugging
```bash
kubectl logs -f deployment/joplin-server
```
Check pod status and events:
```bash
kubectl describe pod -l app.kubernetes.io/name=joplin-server
```
## Backing Up
## Links
- **Database**: Use PostgreSQL backup tools (pg_dump, etc.)
- **File Storage**:
- Filesystem: Backup the PVC data
- S3: Files are already stored in S3 (ensure proper S3 backup policies)
- **Configuration**: Backup your Kubernetes secrets and config
- [Joplin GitHub](https://github.com/laurent22/joplin)
- [Chart Source](https://github.com/rtomik/helm-charts/tree/main/charts/joplin-server)

View File

@ -1,14 +1,18 @@
# Karakeep Helm Chart
This Helm chart deploys [Karakeep](https://github.com/karakeep-app/karakeep), a bookmark management application, along with its required services on a Kubernetes cluster.
A Helm chart for deploying [Karakeep](https://github.com/karakeep-app/karakeep), a bookmark management application, on Kubernetes.
## Components
## Introduction
This chart deploys three containers in a single pod:
This chart deploys Karakeep as a multi-container pod with three services:
1. **Karakeep**: The main bookmark management application
2. **Chrome**: Headless Chrome browser for web scraping and preview generation
3. **MeiliSearch**: Search engine for fast bookmark search functionality
1. **Karakeep** — Main bookmark management application
2. **Chrome** Headless browser for web scraping and preview generation
3. **MeiliSearch** Search engine for fast bookmark search
All containers share the same pod network and communicate via localhost.
Source code: https://github.com/rtomik/helm-charts/tree/main/charts/karakeep
## Prerequisites
@ -18,91 +22,180 @@ This chart deploys three containers in a single pod:
## Installing the Chart
To install the chart with the release name `karakeep`:
```bash
helm repo add karakeep-chart https://rtomik.github.io/helm-charts
helm install karakeep karakeep-chart/karakeep
helm repo add rtomik https://rtomik.github.io/helm-charts
helm install karakeep rtomik/karakeep
```
## Uninstalling the Chart
To uninstall/delete the `karakeep` deployment:
```bash
helm delete karakeep
helm uninstall karakeep
```
## Configuration
## Configuration Examples
The following table lists the configurable parameters and their default values.
### Minimal Installation
### Global Settings
```yaml
ingress:
enabled: true
hosts:
- host: karakeep.example.com
paths:
- path: /
pathType: Prefix
tls:
- hosts:
- karakeep.example.com
```
| Parameter | Description | Default |
|-----------|-------------|---------|
| `nameOverride` | Override the name of the chart | `""` |
| `fullnameOverride` | Override the full name of the chart | `""` |
### Production with Secrets
For production, store `NEXTAUTH_SECRET` in a Kubernetes secret. When ingress is enabled, `NEXTAUTH_URL` is automatically set to the ingress hostname.
```yaml
secrets:
create: true
env:
NEXTAUTH_SECRET: "your-secure-32-character-string"
ingress:
enabled: true
hosts:
- host: karakeep.example.com
paths:
- path: /
pathType: Prefix
tls:
- hosts:
- karakeep.example.com
```
### With OpenAI Integration
```yaml
secrets:
create: true
env:
NEXTAUTH_SECRET: "your-secure-32-character-string"
OPENAI_API_KEY: "your-openai-api-key"
```
## Parameters
### Global Parameters
| Name | Description | Default |
|------|-------------|---------|
| `nameOverride` | Override the chart name | `""` |
| `fullnameOverride` | Override the full chart name | `""` |
| `replicaCount` | Number of replicas | `1` |
| `revisionHistoryLimit` | Revisions to retain | `3` |
### Karakeep Configuration
### Pod Security Parameters
| Parameter | Description | Default |
|-----------|-------------|---------|
| Name | Description | Default |
|------|-------------|---------|
| `podSecurityContext.runAsNonRoot` | Run as non-root | `false` |
| `podSecurityContext.runAsUser` | User ID | `0` |
| `podSecurityContext.fsGroup` | Filesystem group ID | `0` |
### Karakeep Parameters
| Name | Description | Default |
|------|-------------|---------|
| `karakeep.image.repository` | Karakeep image repository | `ghcr.io/karakeep-app/karakeep` |
| `karakeep.image.tag` | Karakeep image tag | `"release"` |
| `karakeep.image.tag` | Karakeep image tag | `0.26.0` |
| `karakeep.image.pullPolicy` | Image pull policy | `IfNotPresent` |
| `karakeep.service.port` | Karakeep service port | `3000` |
| `karakeep.env` | Karakeep environment variables | See values.yaml |
| `karakeep.extraEnv` | Additional environment variables | `[]` |
### Chrome Configuration
### Chrome Parameters
| Parameter | Description | Default |
|-----------|-------------|---------|
| Name | Description | Default |
|------|-------------|---------|
| `chrome.image.repository` | Chrome image repository | `gcr.io/zenika-hub/alpine-chrome` |
| `chrome.image.tag` | Chrome image tag | `"124"` |
| `chrome.image.tag` | Chrome image tag | `124` |
| `chrome.image.pullPolicy` | Image pull policy | `IfNotPresent` |
| `chrome.service.port` | Chrome debugging port | `9222` |
### MeiliSearch Configuration
### MeiliSearch Parameters
| Parameter | Description | Default |
|-----------|-------------|---------|
| Name | Description | Default |
|------|-------------|---------|
| `meilisearch.image.repository` | MeiliSearch image repository | `getmeili/meilisearch` |
| `meilisearch.image.tag` | MeiliSearch image tag | `"v1.13.3"` |
| `meilisearch.image.tag` | MeiliSearch image tag | `v1.13.3` |
| `meilisearch.image.pullPolicy` | Image pull policy | `IfNotPresent` |
| `meilisearch.service.port` | MeiliSearch port | `7700` |
| `meilisearch.resources.limits.cpu` | CPU limit | `500m` |
| `meilisearch.resources.limits.memory` | Memory limit | `1Gi` |
| `meilisearch.resources.requests.cpu` | CPU request | `100m` |
| `meilisearch.resources.requests.memory` | Memory request | `256Mi` |
### Persistence
### Service Parameters
| Parameter | Description | Default |
|-----------|-------------|---------|
| `persistence.enabled` | Enable persistent storage | `true` |
| `persistence.data.size` | Size of data volume | `5Gi` |
| `persistence.data.storageClass` | Storage class for data volume | `""` |
| `persistence.meilisearch.size` | Size of MeiliSearch volume | `2Gi` |
| `persistence.meilisearch.storageClass` | Storage class for MeiliSearch volume | `""` |
| Name | Description | Default |
|------|-------------|---------|
| `service.type` | Service type | `ClusterIP` |
| `service.port` | Service port | `3000` |
### Ingress
### Ingress Parameters
| Parameter | Description | Default |
|-----------|-------------|---------|
| Name | Description | Default |
|------|-------------|---------|
| `ingress.enabled` | Enable ingress | `false` |
| `ingress.hosts[0].host` | Hostname | `karakeep.domain.com` |
| `ingress.className` | Ingress class name | `""` |
| `ingress.annotations` | Ingress annotations | See values.yaml |
| `ingress.hosts` | Ingress hosts | See values.yaml |
| `ingress.tls` | TLS configuration | See values.yaml |
### Secrets
### Persistence Parameters
| Parameter | Description | Default |
|-----------|-------------|---------|
| Name | Description | Default |
|------|-------------|---------|
| `persistence.enabled` | Enable persistence | `true` |
| `persistence.data.storageClass` | Data volume storage class | `""` |
| `persistence.data.accessMode` | Data volume access mode | `ReadWriteOnce` |
| `persistence.data.size` | Data volume size | `5Gi` |
| `persistence.meilisearch.storageClass` | MeiliSearch volume storage class | `""` |
| `persistence.meilisearch.accessMode` | MeiliSearch volume access mode | `ReadWriteOnce` |
| `persistence.meilisearch.size` | MeiliSearch volume size | `2Gi` |
### Secret Parameters
| Name | Description | Default |
|------|-------------|---------|
| `secrets.create` | Create secret for environment variables | `false` |
| `secrets.existingSecret` | Use existing secret | `""` |
| `secrets.env` | Environment variables to store in secret | `{}` |
| `secrets.existingSecret` | Use an existing secret | `""` |
| `secrets.env` | Environment variables for the secret | `{}` |
**Important Configuration:**
1. The default `NEXTAUTH_SECRET` is set to a placeholder value. For production deployments, you should either:
- Override the value: `--set karakeep.env[3].value="your-secure-32-character-string"`
- Use secrets: `--set secrets.create=true --set secrets.env.NEXTAUTH_SECRET="your-secure-32-character-string"`
## Troubleshooting
2. When ingress is enabled, `NEXTAUTH_URL` is automatically set to the ingress hostname. For custom configurations:
- Override manually: `--set karakeep.env[4].value="https://your-domain.com"`
### NEXTAUTH_SECRET Not Set
## Notes
The default `NEXTAUTH_SECRET` is a placeholder. For production, override it:
- This chart creates a multi-container pod with all three services running together
- Data persistence is enabled by default with separate volumes for Karakeep data and MeiliSearch indices
- The services communicate via localhost since they share the same pod network
- Chrome runs with security flags for containerized environments
```yaml
secrets:
create: true
env:
NEXTAUTH_SECRET: "your-secure-32-character-string"
```
### Custom NEXTAUTH_URL
If not using ingress or using a custom domain, override `NEXTAUTH_URL` manually:
```yaml
karakeep:
env:
- name: NEXTAUTH_URL
value: "https://your-domain.com"
```
## Links
- [Karakeep GitHub](https://github.com/karakeep-app/karakeep)
- [Chart Source](https://github.com/rtomik/helm-charts/tree/main/charts/karakeep)

View File

@ -1,273 +1,41 @@
# Mealie Helm Chart
A Helm chart for deploying Mealie recipe management and meal planning application on Kubernetes.
A Helm chart for deploying [Mealie](https://github.com/mealie-recipes/mealie), a recipe management and meal planning application, on Kubernetes.
## Introduction
This chart deploys [Mealie](https://github.com/mealie-recipes/mealie) on a Kubernetes cluster using the Helm package manager. Mealie is a self-hosted recipe manager and meal planner with a RestAPI backend and a reactive frontend application built in Vue for a pleasant user experience for the whole family.
This chart deploys Mealie on a Kubernetes cluster using the Helm package manager. Mealie is a self-hosted recipe manager with a reactive frontend and RestAPI backend, supporting PostgreSQL or SQLite databases, LDAP/OIDC authentication, and OpenAI integration.
Source code can be found here:
- https://github.com/rtomik/helm-charts/tree/main/charts/mealie
Source code: https://github.com/rtomik/helm-charts/tree/main/charts/mealie
## Prerequisites
- Kubernetes 1.19+
- Helm 3.0+
- PV provisioner support in the underlying infrastructure (if persistence is needed)
- External Postgresql DB like https://cloudnative-pg.io/
- External PostgreSQL database (recommended, e.g. [CloudNativePG](https://cloudnative-pg.io/))
- PV provisioner support (if persistence is needed)
## Installing the Chart
To install the chart with the release name `mealie`:
```bash
$ helm repo add mealie-chart https://rtomik.github.io/helm-charts
$ helm install mealie mealie-chart/mealie
helm repo add rtomik https://rtomik.github.io/helm-charts
helm install mealie rtomik/mealie
```
> **Tip**: List all releases using `helm list`
## Uninstalling the Chart
To uninstall/delete the `mealie` deployment:
```bash
$ helm uninstall mealie
helm uninstall mealie
```
## Parameters
### Global parameters
| Name | Description | Value |
|------------------------|------------------------------------------------|-------|
| `nameOverride` | String to partially override the release name | `""` |
| `fullnameOverride` | String to fully override the release name | `""` |
### Image parameters
| Name | Description | Value |
|-------------------------|-----------------------------------|-----------------------------------|
| `image.repository` | Mealie image repository | `ghcr.io/mealie-recipes/mealie` |
| `image.tag` | Mealie image tag | `v3.1.1` |
| `image.pullPolicy` | Mealie image pull policy | `IfNotPresent` |
### Deployment parameters
| Name | Description | Value |
|--------------------------------------|-----------------------------------------------|-----------|
| `replicaCount` | Number of Mealie replicas | `1` |
| `revisionHistoryLimit` | Number of revisions to retain for rollback | `3` |
| `podSecurityContext.runAsNonRoot` | Run containers as non-root user | `false` |
| `podSecurityContext.runAsUser` | User ID for the container | `911` |
| `podSecurityContext.fsGroup` | Group ID for the container filesystem | `911` |
| `containerSecurityContext` | Security context for the container | See values.yaml |
| `nodeSelector` | Node labels for pod assignment | `{}` |
| `tolerations` | Tolerations for pod assignment | `[]` |
| `affinity` | Affinity for pod assignment | `{}` |
### Service parameters
| Name | Description | Value |
|----------------|-----------------------|-------------|
| `service.type` | Kubernetes Service type | `ClusterIP` |
| `service.port` | Service HTTP port | `9000` |
### Ingress parameters
| Name | Description | Value |
|-------------------------|-------------------------------------------|-----------------|
| `ingress.enabled` | Enable ingress record generation | `false` |
| `ingress.className` | IngressClass name | `""` |
| `ingress.annotations` | Additional annotations for the Ingress | See values.yaml |
| `ingress.hosts` | Array of host and path objects | See values.yaml |
| `ingress.tls` | TLS configuration | See values.yaml |
### Persistence parameters
| Name | Description | Value |
|-------------------------------|----------------------------------|-----------------|
| `persistence.enabled` | Enable persistence using PVC | `true` |
| `persistence.storageClass` | PVC Storage Class | `""` |
| `persistence.accessMode` | PVC Access Mode | `ReadWriteOnce` |
| `persistence.size` | PVC Size | `5Gi` |
| `persistence.annotations` | Annotations for PVC | `{}` |
### Environment variables
| Name | Description | Value |
|---------------------------------------|-----------------------------------------------|-----------------|
| `env.PUID` | UserID permissions between host OS and container | `911` |
| `env.PGID` | GroupID permissions between host OS and container | `911` |
| `env.DEFAULT_GROUP` | The default group for users | `Home` |
| `env.DEFAULT_HOUSEHOLD` | The default household for users in each group | `Family` |
| `env.BASE_URL` | Used for Notifications | `http://localhost:9000` |
| `env.TOKEN_TIME` | The time in hours that a login/auth token is valid | `48` |
| `env.API_PORT` | The port exposed by backend API | `9000` |
| `env.API_DOCS` | Turns on/off access to the API documentation | `true` |
| `env.TZ` | Must be set to get correct date/time on the server | `UTC` |
| `env.ALLOW_SIGNUP` | Allow user sign-up without token | `false` |
| `env.ALLOW_PASSWORD_LOGIN` | Whether or not to display username+password input fields | `true` |
| `env.LOG_LEVEL` | Logging level | `info` |
| `env.DAILY_SCHEDULE_TIME` | Time to run daily server tasks (HH:MM) | `23:45` |
### PostgreSQL configuration
| Name | Description | Value |
|----------------------------------------|-----------------------------------------------|-----------|
| `postgresql.enabled` | Enable PostgreSQL support | `false` |
| `postgresql.external.enabled` | Use external PostgreSQL database | `false` |
| `postgresql.external.host` | PostgreSQL host | `""` |
| `postgresql.external.port` | PostgreSQL port | `5432` |
| `postgresql.external.database` | PostgreSQL database name | `mealie` |
| `postgresql.external.user` | PostgreSQL username | `mealie` |
| `postgresql.external.password` | PostgreSQL password | `""` |
| `postgresql.external.existingSecret` | Name of existing secret with PostgreSQL credentials | `""` |
| `postgresql.external.userKey` | Key in the secret for username | `username` |
| `postgresql.external.passwordKey` | Key in the secret for password | `password` |
### Email (SMTP) configuration
| Name | Description | Value |
|--------------------------|--------------------------------------|-----------|
| `email.enabled` | Enable SMTP email support | `false` |
| `email.host` | SMTP host | `""` |
| `email.port` | SMTP port | `587` |
| `email.fromName` | From name for emails | `Mealie` |
| `email.authStrategy` | SMTP auth strategy (TLS, SSL, NONE) | `TLS` |
| `email.fromEmail` | From email address | `""` |
| `email.user` | SMTP username | `""` |
| `email.password` | SMTP password | `""` |
| `email.existingSecret` | Name of existing secret with SMTP credentials | `""` |
| `email.userKey` | Key in the secret for SMTP username | `smtp-user` |
| `email.passwordKey` | Key in the secret for SMTP password | `smtp-password` |
### LDAP Authentication
| Name | Description | Value |
|--------------------------|--------------------------------------|-----------|
| `ldap.enabled` | Enable LDAP authentication | `false` |
| `ldap.serverUrl` | LDAP server URL | `""` |
| `ldap.tlsInsecure` | Do not verify server certificate | `false` |
| `ldap.tlsCaCertFile` | Path to CA certificate file | `""` |
| `ldap.enableStartTls` | Use STARTTLS to connect to server | `false` |
| `ldap.baseDn` | Starting point for user authentication | `""` |
| `ldap.queryBind` | Optional bind user for LDAP searches | `""` |
| `ldap.queryPassword` | Password for the bind user | `""` |
| `ldap.userFilter` | LDAP filter to narrow down eligible users | `""` |
| `ldap.adminFilter` | LDAP filter for admin users | `""` |
| `ldap.idAttribute` | LDAP attribute for user ID | `uid` |
| `ldap.nameAttribute` | LDAP attribute for user name | `name` |
| `ldap.mailAttribute` | LDAP attribute for user email | `mail` |
### OpenID Connect (OIDC)
| Name | Description | Value |
|------------------------------|------------------------------------------|-----------|
| `oidc.enabled` | Enable OIDC authentication | `false` |
| `oidc.signupEnabled` | Allow new users via OIDC | `true` |
| `oidc.configurationUrl` | URL to OIDC configuration | `""` |
| `oidc.clientId` | OIDC client ID | `""` |
| `oidc.clientSecret` | OIDC client secret | `""` |
| `oidc.userGroup` | Required OIDC user group | `""` |
| `oidc.adminGroup` | OIDC admin group | `""` |
| `oidc.autoRedirect` | Bypass login page and redirect to IdP | `false` |
| `oidc.providerName` | Provider name shown in login button | `OAuth` |
| `oidc.rememberMe` | Extend session as if "Remember Me" was checked | `false` |
| `oidc.signingAlgorithm` | Algorithm used to sign the id token | `RS256` |
| `oidc.userClaim` | Claim to look up existing user by | `email` |
| `oidc.nameClaim` | Claim for user's full name | `name` |
| `oidc.groupsClaim` | Claim for user groups | `groups` |
### OpenAI Integration
| Name | Description | Value |
|------------------------------------|------------------------------------------|-----------|
| `openai.enabled` | Enable OpenAI integration | `false` |
| `openai.baseUrl` | Base URL for OpenAI API | `""` |
| `openai.apiKey` | OpenAI API key | `""` |
| `openai.model` | OpenAI model to use | `gpt-4o` |
| `openai.customHeaders` | Custom HTTP headers for OpenAI requests | `""` |
| `openai.customParams` | Custom HTTP query params for OpenAI requests | `""` |
| `openai.enableImageServices` | Enable OpenAI image services | `true` |
| `openai.workers` | Number of OpenAI workers per request | `2` |
| `openai.sendDatabaseData` | Send Mealie data to OpenAI to improve accuracy | `true` |
| `openai.requestTimeout` | Timeout for OpenAI requests in seconds | `60` |
### TLS Configuration
| Name | Description | Value |
|--------------------------|--------------------------------------|-----------|
| `tls.enabled` | Enable TLS configuration | `false` |
| `tls.certificatePath` | Path to TLS certificate file | `""` |
| `tls.privateKeyPath` | Path to TLS private key file | `""` |
| `tls.existingSecret` | Name of existing secret with TLS certificates | `""` |
| `tls.certificateKey` | Key in the secret for TLS certificate | `tls.crt` |
| `tls.privateKeyKey` | Key in the secret for TLS private key | `tls.key` |
### Theme Configuration
| Name | Description | Value |
|-------------------------------|--------------------------------|-----------|
| `theme.light.primary` | Light theme primary color | `#E58325` |
| `theme.light.accent` | Light theme accent color | `#007A99` |
| `theme.light.secondary` | Light theme secondary color | `#973542` |
| `theme.light.success` | Light theme success color | `#43A047` |
| `theme.light.info` | Light theme info color | `#1976D2` |
| `theme.light.warning` | Light theme warning color | `#FF6D00` |
| `theme.light.error` | Light theme error color | `#EF5350` |
| `theme.dark.primary` | Dark theme primary color | `#E58325` |
| `theme.dark.accent` | Dark theme accent color | `#007A99` |
| `theme.dark.secondary` | Dark theme secondary color | `#973542` |
| `theme.dark.success` | Dark theme success color | `#43A047` |
| `theme.dark.info` | Dark theme info color | `#1976D2` |
| `theme.dark.warning` | Dark theme warning color | `#FF6D00` |
| `theme.dark.error` | Dark theme error color | `#EF5350` |
### Resource Configuration
| Name | Description | Value |
|-------------|--------------------------------------|-------|
| `resources` | Resource limits and requests | `{}` |
### Health Checks
| Name | Description | Value |
|-------------------------------------------|------------------------------------------|-------|
| `probes.liveness.enabled` | Enable liveness probe | `true` |
| `probes.liveness.initialDelaySeconds` | Initial delay for liveness probe | `60` |
| `probes.liveness.periodSeconds` | Period for liveness probe | `30` |
| `probes.liveness.timeoutSeconds` | Timeout for liveness probe | `10` |
| `probes.liveness.failureThreshold` | Failure threshold for liveness probe | `3` |
| `probes.liveness.successThreshold` | Success threshold for liveness probe | `1` |
| `probes.liveness.path` | Path for liveness probe | `/` |
| `probes.readiness.enabled` | Enable readiness probe | `true` |
| `probes.readiness.initialDelaySeconds` | Initial delay for readiness probe | `30` |
| `probes.readiness.periodSeconds` | Period for readiness probe | `10` |
| `probes.readiness.timeoutSeconds` | Timeout for readiness probe | `5` |
| `probes.readiness.failureThreshold` | Failure threshold for readiness probe | `3` |
| `probes.readiness.successThreshold` | Success threshold for readiness probe | `1` |
| `probes.readiness.path` | Path for readiness probe | `/` |
### Autoscaling
| Name | Description | Value |
|---------------------------------------------|------------------------------------------|---------|
| `autoscaling.enabled` | Enable horizontal pod autoscaling | `false` |
| `autoscaling.minReplicas` | Minimum number of replicas | `1` |
| `autoscaling.maxReplicas` | Maximum number of replicas | `3` |
| `autoscaling.targetCPUUtilizationPercentage`| Target CPU utilization percentage | `80` |
| `autoscaling.targetMemoryUtilizationPercentage`| Target memory utilization percentage | `80` |
## Configuration Examples
### Basic Installation with Persistence
### Minimal Installation
```yaml
persistence:
enabled: true
size: 10Gi
storageClass: "fast-ssd"
ingress:
enabled: true
@ -282,7 +50,7 @@ ingress:
secretName: mealie-tls
```
### PostgreSQL Database Configuration
### PostgreSQL Configuration
```yaml
postgresql:
@ -300,7 +68,7 @@ env:
DB_ENGINE: "postgres"
```
### OIDC Authentication Setup
### OIDC Authentication
```yaml
oidc:
@ -326,27 +94,273 @@ openai:
enableImageServices: true
```
## Security Considerations
### LDAP Authentication
For production deployments, it's recommended to:
```yaml
ldap:
enabled: true
serverUrl: "ldap://ldap.example.com"
baseDn: "ou=users,dc=example,dc=com"
queryBind: "cn=admin,dc=example,dc=com"
queryPassword: "bind-password"
userFilter: "(objectClass=inetOrgPerson)"
idAttribute: "uid"
nameAttribute: "name"
mailAttribute: "mail"
```
1. Use external secrets for sensitive information
2. Enable TLS/SSL for all communications
3. Configure proper RBAC and network policies
4. Use a dedicated database with proper access controls
5. Enable authentication (LDAP/OIDC) and disable public signup
### Email Configuration
```yaml
email:
enabled: true
host: "smtp.example.com"
port: 587
fromName: "Mealie"
fromEmail: "mealie@example.com"
authStrategy: "TLS"
existingSecret: "mealie-smtp-secret"
userKey: "smtp-user"
passwordKey: "smtp-password"
```
## Parameters
### Global Parameters
| Name | Description | Default |
|------|-------------|---------|
| `nameOverride` | Override the release name | `""` |
| `fullnameOverride` | Fully override the release name | `""` |
### Image Parameters
| Name | Description | Default |
|------|-------------|---------|
| `image.repository` | Mealie image repository | `ghcr.io/mealie-recipes/mealie` |
| `image.tag` | Image tag | `v3.2.1` |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
### Deployment Parameters
| Name | Description | Default |
|------|-------------|---------|
| `replicaCount` | Number of replicas | `1` |
| `revisionHistoryLimit` | Revisions to retain | `3` |
| `podSecurityContext.runAsNonRoot` | Run as non-root | `false` |
| `podSecurityContext.runAsUser` | User ID | `911` |
| `podSecurityContext.fsGroup` | Filesystem group ID | `911` |
| `nodeSelector` | Node selector | `{}` |
| `tolerations` | Tolerations | `[]` |
| `affinity` | Affinity rules | `{}` |
### Service Parameters
| Name | Description | Default |
|------|-------------|---------|
| `service.type` | Service type | `ClusterIP` |
| `service.port` | Service port | `9000` |
### Ingress Parameters
| Name | Description | Default |
|------|-------------|---------|
| `ingress.enabled` | Enable ingress | `false` |
| `ingress.className` | Ingress class name | `""` |
| `ingress.annotations` | Ingress annotations | See values.yaml |
| `ingress.hosts` | Ingress hosts | See values.yaml |
| `ingress.tls` | TLS configuration | See values.yaml |
### Persistence Parameters
| Name | Description | Default |
|------|-------------|---------|
| `persistence.enabled` | Enable persistence | `false` |
| `persistence.storageClass` | Storage class | `""` |
| `persistence.accessMode` | Access mode | `ReadWriteOnce` |
| `persistence.size` | PVC size | `5Gi` |
| `persistence.annotations` | PVC annotations | `{}` |
### Environment Variables
| Name | Description | Default |
|------|-------------|---------|
| `env.PUID` | User ID for host permissions | `911` |
| `env.PGID` | Group ID for host permissions | `911` |
| `env.DEFAULT_GROUP` | Default group for users | `Home` |
| `env.DEFAULT_HOUSEHOLD` | Default household | `Family` |
| `env.BASE_URL` | Base URL for notifications | `http://localhost:9000` |
| `env.TOKEN_TIME` | Login token validity (hours) | `48` |
| `env.API_PORT` | Backend API port | `9000` |
| `env.API_DOCS` | Enable API documentation | `true` |
| `env.TZ` | Timezone | `UTC` |
| `env.ALLOW_SIGNUP` | Allow user sign-up | `false` |
| `env.ALLOW_PASSWORD_LOGIN` | Allow password login | `true` |
| `env.LOG_LEVEL` | Log level | `info` |
| `env.DAILY_SCHEDULE_TIME` | Daily task schedule (HH:MM) | `23:45` |
| `env.DB_ENGINE` | Database engine (`postgres` or `sqlite`) | `postgres` |
| `extraEnv` | Additional environment variables | `[]` |
### PostgreSQL Configuration
| Name | Description | Default |
|------|-------------|---------|
| `postgresql.enabled` | Enable PostgreSQL | `false` |
| `postgresql.external.enabled` | Use external PostgreSQL | `false` |
| `postgresql.external.host` | PostgreSQL host | `""` |
| `postgresql.external.port` | PostgreSQL port | `5432` |
| `postgresql.external.database` | Database name | `mealie` |
| `postgresql.external.user` | Username | `mealie` |
| `postgresql.external.password` | Password | `""` |
| `postgresql.external.existingSecret` | Existing secret name | `""` |
| `postgresql.external.userKey` | Key for username in secret | `username` |
| `postgresql.external.passwordKey` | Key for password in secret | `password` |
### LDAP Authentication
| Name | Description | Default |
|------|-------------|---------|
| `ldap.enabled` | Enable LDAP | `false` |
| `ldap.serverUrl` | LDAP server URL | `""` |
| `ldap.tlsInsecure` | Skip server cert verification | `false` |
| `ldap.tlsCaCertFile` | CA certificate path | `""` |
| `ldap.enableStartTls` | Use STARTTLS | `false` |
| `ldap.baseDn` | Base DN for authentication | `""` |
| `ldap.queryBind` | Bind user for searches | `""` |
| `ldap.queryPassword` | Bind user password | `""` |
| `ldap.userFilter` | User LDAP filter | `""` |
| `ldap.adminFilter` | Admin LDAP filter | `""` |
| `ldap.idAttribute` | User ID attribute | `uid` |
| `ldap.nameAttribute` | User name attribute | `name` |
| `ldap.mailAttribute` | User email attribute | `mail` |
| `ldap.existingSecret` | Existing secret for LDAP | `""` |
| `ldap.passwordKey` | Key for password in secret | `ldap-password` |
### OIDC Authentication
| Name | Description | Default |
|------|-------------|---------|
| `oidc.enabled` | Enable OIDC | `false` |
| `oidc.signupEnabled` | Allow new users via OIDC | `true` |
| `oidc.configurationUrl` | OIDC configuration URL | `""` |
| `oidc.clientId` | Client ID | `""` |
| `oidc.clientSecret` | Client secret | `""` |
| `oidc.userGroup` | Required user group | `""` |
| `oidc.adminGroup` | Admin group | `""` |
| `oidc.autoRedirect` | Redirect to IdP on login | `false` |
| `oidc.providerName` | Provider name on login button | `OAuth` |
| `oidc.rememberMe` | Extend session ("Remember Me") | `false` |
| `oidc.signingAlgorithm` | ID token signing algorithm | `RS256` |
| `oidc.userClaim` | Claim to identify user | `email` |
| `oidc.nameClaim` | Claim for user name | `name` |
| `oidc.groupsClaim` | Claim for groups | `groups` |
| `oidc.existingSecret` | Existing secret name | `""` |
| `oidc.clientIdKey` | Key for client ID in secret | `oidc-client-id` |
| `oidc.clientSecretKey` | Key for client secret in secret | `oidc-client-secret` |
### OpenAI Configuration
| Name | Description | Default |
|------|-------------|---------|
| `openai.enabled` | Enable OpenAI | `false` |
| `openai.baseUrl` | OpenAI API base URL | `""` |
| `openai.apiKey` | OpenAI API key | `""` |
| `openai.model` | Model to use | `gpt-4o` |
| `openai.enableImageServices` | Enable image services | `true` |
| `openai.workers` | Workers per request | `2` |
| `openai.sendDatabaseData` | Send DB data for accuracy | `true` |
| `openai.requestTimeout` | Request timeout (seconds) | `60` |
| `openai.existingSecret` | Existing secret name | `""` |
| `openai.apiKeyKey` | Key for API key in secret | `openai-api-key` |
### Email Configuration
| Name | Description | Default |
|------|-------------|---------|
| `email.enabled` | Enable SMTP email | `false` |
| `email.host` | SMTP host | `""` |
| `email.port` | SMTP port | `587` |
| `email.fromName` | From name | `Mealie` |
| `email.authStrategy` | Auth strategy (`TLS`, `SSL`, `NONE`) | `TLS` |
| `email.fromEmail` | From email address | `""` |
| `email.user` | SMTP username | `""` |
| `email.password` | SMTP password | `""` |
| `email.existingSecret` | Existing secret for SMTP | `""` |
| `email.userKey` | Key for username in secret | `smtp-user` |
| `email.passwordKey` | Key for password in secret | `smtp-password` |
### TLS Configuration
| Name | Description | Default |
|------|-------------|---------|
| `tls.enabled` | Enable TLS | `false` |
| `tls.certificatePath` | TLS certificate path | `""` |
| `tls.privateKeyPath` | TLS private key path | `""` |
| `tls.existingSecret` | Existing secret with TLS certs | `""` |
| `tls.certificateKey` | Key for certificate in secret | `tls.crt` |
| `tls.privateKeyKey` | Key for private key in secret | `tls.key` |
### Theme Configuration
| Name | Description | Default |
|------|-------------|---------|
| `theme.light.primary` | Light theme primary color | `#E58325` |
| `theme.light.accent` | Light theme accent color | `#007A99` |
| `theme.light.secondary` | Light theme secondary color | `#973542` |
| `theme.light.success` | Light theme success color | `#43A047` |
| `theme.light.info` | Light theme info color | `#1976D2` |
| `theme.light.warning` | Light theme warning color | `#FF6D00` |
| `theme.light.error` | Light theme error color | `#EF5350` |
| `theme.dark.primary` | Dark theme primary color | `#E58325` |
| `theme.dark.accent` | Dark theme accent color | `#007A99` |
| `theme.dark.secondary` | Dark theme secondary color | `#973542` |
| `theme.dark.success` | Dark theme success color | `#43A047` |
| `theme.dark.info` | Dark theme info color | `#1976D2` |
| `theme.dark.warning` | Dark theme warning color | `#FF6D00` |
| `theme.dark.error` | Dark theme error color | `#EF5350` |
### Resource Parameters
| Name | Description | Default |
|------|-------------|---------|
| `resources` | Resource limits and requests | `{}` |
### Health Check Parameters
| Name | Description | Default |
|------|-------------|---------|
| `probes.liveness.enabled` | Enable liveness probe | `true` |
| `probes.liveness.path` | Liveness probe path | `/` |
| `probes.liveness.initialDelaySeconds` | Liveness initial delay | `60` |
| `probes.liveness.periodSeconds` | Liveness period | `30` |
| `probes.readiness.enabled` | Enable readiness probe | `true` |
| `probes.readiness.path` | Readiness probe path | `/` |
| `probes.readiness.initialDelaySeconds` | Readiness initial delay | `30` |
| `probes.readiness.periodSeconds` | Readiness period | `10` |
### Autoscaling Parameters
| Name | Description | Default |
|------|-------------|---------|
| `autoscaling.enabled` | Enable HPA | `false` |
| `autoscaling.minReplicas` | Min replicas | `1` |
| `autoscaling.maxReplicas` | Max replicas | `3` |
| `autoscaling.targetCPUUtilizationPercentage` | Target CPU | `80` |
| `autoscaling.targetMemoryUtilizationPercentage` | Target memory | `80` |
## Troubleshooting
Common issues and solutions:
1. **Database connection issues**: Verify database credentials and network connectivity
2. **Persistence issues**: Check StorageClass and PVC configuration
3. **Authentication problems**: Verify LDAP/OIDC configuration and network access
4. **Performance issues**: Adjust resource limits and consider using external database
For more detailed troubleshooting, check the application logs:
- **Database connection issues**: Verify credentials and network connectivity
- **Persistence issues**: Check StorageClass and PVC configuration
- **Authentication problems**: Verify LDAP/OIDC configuration and network access
- **Performance issues**: Adjust resource limits and consider using an external database
```bash
kubectl logs -f deployment/mealie
kubectl describe pod -l app.kubernetes.io/name=mealie
```
## Links
- [Mealie GitHub](https://github.com/mealie-recipes/mealie)
- [Chart Source](https://github.com/rtomik/helm-charts/tree/main/charts/mealie)

View File

@ -2,8 +2,8 @@ apiVersion: v2
name: norish
description: Norish helm chart for Kubernetes - A recipe management and meal planning application
type: application
version: 0.0.4
appVersion: "v0.14.1-beta"
version: 0.0.6
appVersion: "v0.20.0-beta"
maintainers:
- name: Richard Tomik
email: no@m.com
@ -14,4 +14,4 @@ keywords:
- norish
home: https://github.com/rtomik/helm-charts
sources:
- https://github.com/norishapp/norish
- https://github.com/norish-recipes/norish

File diff suppressed because it is too large Load Diff

View File

@ -71,4 +71,4 @@ IMPORTANT CONFIGURATION NOTES:
Configure ONE provider (OIDC, GitHub, or Google) to create your admin account.
{{- end }}
For more information, visit: https://github.com/norishapp/norish
For more information, visit: https://github.com/norish-recipes/norish

View File

@ -96,6 +96,24 @@ spec:
- name: PASSWORD_AUTH_ENABLED
value: {{ .Values.config.passwordAuthEnabled | quote }}
{{- end }}
{{- if .Values.config.enableRegistration }}
- name: ENABLE_REGISTRATION
value: {{ .Values.config.enableRegistration | quote }}
{{- end }}
- name: UPLOADS_DIR
value: {{ .Values.config.uploadsDir | quote }}
{{- if .Values.config.parserApiTimeoutMs }}
- name: PARSER_API_TIMEOUT_MS
value: {{ .Values.config.parserApiTimeoutMs | quote }}
{{- end }}
{{- if .Values.config.defaultLocale }}
- name: DEFAULT_LOCALE
value: {{ .Values.config.defaultLocale | quote }}
{{- end }}
{{- if .Values.config.enabledLocales }}
- name: ENABLED_LOCALES
value: {{ .Values.config.enabledLocales | quote }}
{{- end }}
{{- if .Values.database.existingSecret }}
- name: DB_USERNAME
valueFrom:
@ -164,6 +182,20 @@ spec:
- name: OIDC_WELLKNOWN
value: {{ .Values.config.auth.oidc.wellKnown | quote }}
{{- end }}
{{- if .Values.config.auth.oidc.claimMapping.enabled }}
- name: OIDC_CLAIM_MAPPING_ENABLED
value: "true"
{{- with .Values.config.auth.oidc.claimMapping.scopes }}
- name: OIDC_SCOPES
value: {{ . | quote }}
{{- end }}
- name: OIDC_GROUPS_CLAIM
value: {{ .Values.config.auth.oidc.claimMapping.groupsClaim | quote }}
- name: OIDC_ADMIN_GROUP
value: {{ .Values.config.auth.oidc.claimMapping.adminGroup | quote }}
- name: OIDC_HOUSEHOLD_GROUP_PREFIX
value: {{ .Values.config.auth.oidc.claimMapping.householdGroupPrefix | quote }}
{{- end }}
- name: OIDC_CLIENT_ID
valueFrom:
secretKeyRef:
@ -234,7 +266,7 @@ spec:
{{- end }}
volumeMounts:
- name: uploads
mountPath: /app/uploads
mountPath: {{ .Values.config.uploadsDir }}
{{- with .Values.extraVolumeMounts }}
{{- toYaml . | nindent 12 }}
{{- end }}
@ -256,6 +288,7 @@ spec:
- "--no-sandbox"
- "--disable-gpu"
- "--disable-dev-shm-usage"
- "--disable-features=dbus"
- "--remote-debugging-address=0.0.0.0"
- "--remote-debugging-port={{ .Values.chrome.port }}"
- "--headless"

View File

@ -5,7 +5,7 @@ fullnameOverride: ""
## Image settings
image:
repository: norishapp/norish
tag: "v0.14.1-beta"
tag: "v0.20.0-beta"
pullPolicy: IfNotPresent
imagePullSecrets: []
@ -96,7 +96,7 @@ probes:
timeoutSeconds: 5
failureThreshold: 30
successThreshold: 1
path: /
path: /api/v1/health
liveness:
enabled: true
initialDelaySeconds: 30
@ -104,6 +104,11 @@ probes:
timeoutSeconds: 5
failureThreshold: 6
successThreshold: 1
# Deliberately NOT /api/v1/health: since v0.18.1 that endpoint returns 503 when the
# database is unreachable, so a transient DB outage would restart the pod. The app
# root confirms the process is alive without coupling liveness to the database.
# Readiness still uses the health endpoint, which is what pulls the pod out of the
# Service during a DB outage.
path: /
readiness:
enabled: true
@ -112,7 +117,7 @@ probes:
timeoutSeconds: 3
failureThreshold: 3
successThreshold: 1
path: /
path: /api/v1/health
## Application configuration
config:
@ -154,6 +159,26 @@ config:
# Defaults to false if OIDC or OAuth is configured, true otherwise
passwordAuthEnabled: ""
# Allow new users to register themselves (ENABLE_REGISTRATION)
# Leave empty to use the application default (false).
# The first account created always becomes the server owner/admin.
enableRegistration: ""
# Uploads directory inside the container (UPLOADS_DIR)
# Also used as the mount path for the uploads volume
uploadsDir: "/app/uploads"
# Timeout for recipe parser API calls in milliseconds (PARSER_API_TIMEOUT_MS)
# Leave empty to use the application default (15000)
parserApiTimeoutMs: ""
# Instance default locale, must match a supported locale (DEFAULT_LOCALE)
defaultLocale: ""
# Comma-separated list of enabled locales (ENABLED_LOCALES)
# Leave empty to enable all locales. Example: "en,de,nl"
enabledLocales: ""
# Authentication provider configuration
# Configure ONE provider for initial admin account creation
# After first login, manage additional providers via Settings → Admin
@ -173,6 +198,16 @@ config:
clientIdKey: "oidc-client-id"
clientSecretKey: "oidc-client-secret"
# Claim mapping: auto-assign the admin role and households from OIDC claims
# Disabled by default for security
claimMapping:
enabled: false
# Additional scopes to request, comma-separated (e.g. "groups" for Keycloak)
scopes: ""
groupsClaim: "groups" # Claim containing the user groups
adminGroup: "norish_admin" # Group that grants the server admin role
householdGroupPrefix: "norish_household_" # Prefix for household groups
# GitHub OAuth
github:
enabled: false

View File

@ -1,176 +1,59 @@
# Paperless-ngx Helm Chart
A Helm chart for deploying Paperless-ngx document management system on Kubernetes.
A Helm chart for deploying [Paperless-ngx](https://github.com/paperless-ngx/paperless-ngx), a document management system with OCR, on Kubernetes.
## Introduction
This chart deploys [Paperless-ngx](https://github.com/paperless-ngx/paperless-ngx) on a Kubernetes cluster using the Helm package manager.
This chart deploys Paperless-ngx on a Kubernetes cluster. Paperless-ngx is a community-supported document scanner: scan, index, and archive all your physical documents. It requires external PostgreSQL and Redis services.
Paperless-ngx is a community-supported supercharged version of paperless: scan, index and archive all your physical documents.
Source code can be found here:
- https://github.com/rtomik/helm-charts/tree/main/charts/paperless-ngx
Source code: https://github.com/rtomik/helm-charts/tree/main/charts/paperless-ngx
## Prerequisites
- Kubernetes 1.19+
- Helm 3.0+
- PV provisioner support in the underlying infrastructure
- **External PostgreSQL database** (required)
- **External Redis server** (required)
## External Dependencies
This chart requires external PostgreSQL and Redis services. It does not deploy these dependencies to avoid resource conflicts on centralized servers.
### PostgreSQL Setup
Paperless-ngx requires PostgreSQL 11+ as its database backend. Ensure you have:
- A PostgreSQL database created for Paperless-ngx
- Database credentials configured in values.yaml or via secrets
### Redis Setup
Redis is required for background task processing. Ensure you have:
- A Redis server accessible from the cluster
- Connection details configured in values.yaml
- Optional: Redis authentication credentials (username/password)
- Optional: Redis key prefix for sharing one Redis server among multiple Paperless instances
The chart supports all Redis authentication methods:
- No authentication: `redis://host:port/database`
- Password only (requirepass): `redis://:password@host:port/database`
- Username and password (Redis 6.0+ ACL): `redis://username:password@host:port/database`
- **External PostgreSQL database** (PostgreSQL 11+ required)
- **External Redis server**
- PV provisioner support
## Installing the Chart
To install the chart with the release name `paperless-ngx`:
```bash
$ helm repo add paperless-chart https://rtomik.github.io/helm-charts
$ helm install paperless-ngx paperless-chart/paperless-ngx
helm repo add rtomik https://rtomik.github.io/helm-charts
helm install paperless-ngx rtomik/paperless-ngx
```
Or install directly from this repository:
## Uninstalling the Chart
```bash
$ git clone https://github.com/rtomik/helm-charts.git
$ cd helm-charts/charts/paperless-ngx
$ helm install paperless-ngx .
helm uninstall paperless-ngx
```
> **Tip**: List all releases using `helm list`
## Configuration
The following table lists the configurable parameters and their default values.
### Global Parameters
| Name | Description | Value |
|------------------------|-------------------------------------------------------------------------------------|-------|
| `nameOverride` | String to partially override the release name | `""` |
| `fullnameOverride` | String to fully override the release name | `""` |
### Image Parameters
| Name | Description | Value |
|-------------------------|--------------------------------------------------------------------------------------|--------------------|
| `image.repository` | Paperless-ngx image repository | `ghcr.io/paperless-ngx/paperless-ngx` |
| `image.tag` | Paperless-ngx image tag | `latest` |
| `image.pullPolicy` | Paperless-ngx image pull policy | `IfNotPresent` |
### External Dependencies
| Name | Description | Value |
|----------------------------------------|--------------------------------------------------------------------|-------------------------------------------|
| `postgresql.external.enabled` | Enable external PostgreSQL configuration | `true` |
| `postgresql.external.host` | External PostgreSQL host | `postgresql.default.svc.cluster.local` |
| `postgresql.external.port` | External PostgreSQL port | `5432` |
| `postgresql.external.database` | External PostgreSQL database name | `paperless` |
| `postgresql.external.username` | External PostgreSQL username | `paperless` |
| `postgresql.external.existingSecret` | Existing secret with PostgreSQL credentials | `""` |
| `postgresql.external.passwordKey` | Key in existing secret for PostgreSQL password | `postgresql-password` |
| `redis.external.enabled` | Enable external Redis configuration | `true` |
| `redis.external.host` | External Redis host | `redis.default.svc.cluster.local` |
| `redis.external.port` | External Redis port | `6379` |
| `redis.external.database` | External Redis database number | `0` |
| `redis.external.username` | Redis username (Redis 6.0+ with ACL) | `""` |
| `redis.external.password` | Redis password (leave empty if no auth required) | `""` |
| `redis.external.existingSecret` | Existing secret with Redis credentials | `""` |
| `redis.external.passwordKey` | Key in existing secret for Redis password | `redis-password` |
| `redis.external.prefix` | Prefix for Redis keys/channels (for multi-instance) | `""` |
### Security Configuration
| Name | Description | Value |
|----------------------------------------|--------------------------------------------------------------------|---------------------|
| `config.secretKey.existingSecret` | Name of existing secret for Django secret key | `""` |
| `config.secretKey.secretKey` | Key in the existing secret for Django secret key | `secret-key` |
| `config.admin.user` | Admin username to create on startup | `""` |
| `config.admin.password` | Admin password (use existingSecret for production) | `""` |
| `config.admin.email` | Admin email address | `root@localhost` |
| `config.admin.existingSecret` | Name of existing secret for admin credentials | `""` |
### Application Configuration
| Name | Description | Value |
|----------------------------------------|--------------------------------------------------------------------|---------------------|
| `config.url` | External URL for Paperless-ngx (e.g., https://paperless.domain.com) | `""` |
| `config.allowedHosts` | Comma-separated list of allowed hosts | `*` |
| `config.timeZone` | Application timezone | `UTC` |
| `config.ocr.language` | OCR language (3-letter code) | `eng` |
| `config.ocr.mode` | OCR mode (skip, redo, force) | `skip` |
| `config.consumer.recursive` | Enable recursive consumption directory watching | `false` |
| `config.consumer.subdirsAsTags` | Use subdirectory names as tags | `false` |
### Persistence Parameters
| Name | Description | Value |
|----------------------------------------|--------------------------------------------------------------------|---------------------|
| `persistence.data.enabled` | Enable persistence for data directory | `true` |
| `persistence.data.existingClaim` | Use an existing PVC for data directory | `""` |
| `persistence.data.size` | Size of data PVC | `1Gi` |
| `persistence.media.enabled` | Enable persistence for media directory | `true` |
| `persistence.media.existingClaim` | Use an existing PVC for media directory | `""` |
| `persistence.media.size` | Size of media PVC | `10Gi` |
| `persistence.consume.enabled` | Enable persistence for consume directory | `true` |
| `persistence.consume.existingClaim` | Use an existing PVC for consume directory | `""` |
| `persistence.consume.size` | Size of consume PVC | `5Gi` |
| `persistence.export.enabled` | Enable persistence for export directory | `true` |
| `persistence.export.existingClaim` | Use an existing PVC for export directory | `""` |
| `persistence.export.size` | Size of export PVC | `1Gi` |
### Service Parameters
| Name | Description | Value |
|----------------------------|------------------------------------------------------|-------------|
| `service.type` | Kubernetes Service type | `ClusterIP` |
| `service.port` | Service HTTP port | `8000` |
### Ingress Parameters
| Name | Description | Value |
|----------------------------|------------------------------------------------------|----------------------|
| `ingress.enabled` | Enable ingress record generation | `false` |
| `ingress.className` | IngressClass name | `""` |
| `ingress.annotations` | Additional annotations for the Ingress resource | See values.yaml |
| `ingress.hosts` | Array of host and path objects | See values.yaml |
| `ingress.tls` | TLS configuration | See values.yaml |
## Usage Examples
### Basic Installation
**Note**: PVCs are not deleted automatically. To remove them:
```bash
helm install paperless-ngx . \
--set postgresql.external.host=my-postgres.example.com \
--set postgresql.external.password=secretpassword \
--set redis.external.host=my-redis.example.com
kubectl delete pvc -l app.kubernetes.io/instance=paperless-ngx
```
### Production Installation with External Secrets
## Configuration Examples
### Minimal Installation
```yaml
postgresql:
external:
enabled: true
host: "my-postgres.example.com"
password: "secretpassword"
redis:
external:
host: "my-redis.example.com"
```
### Production with Existing Secrets
```yaml
# values-production.yaml
config:
url: "https://paperless.example.com"
allowedHosts: "paperless.example.com"
@ -182,14 +65,12 @@ config:
existingSecret: "paperless-admin-secrets"
postgresql:
# External PostgreSQL connection details
external:
enabled: true
host: "postgres-cluster-pooler.dbs.svc.cluster.local"
port: 5432
database: "paperless"
username: "paperless"
# Use existingSecret for credentials
existingSecret: "paperless-db-credentials"
passwordKey: "password"
@ -198,10 +79,8 @@ redis:
host: "redis.cache.svc.cluster.local"
port: 6379
database: 0
# Use existingSecret for Redis credentials
existingSecret: "paperless-redis-credentials"
passwordKey: "password"
# Optional: Use prefix to share Redis among multiple instances
prefix: "paperless-prod"
ingress:
@ -218,40 +97,17 @@ ingress:
- paperless.example.com
```
```bash
helm install paperless-ngx . -f values-production.yaml
```
### Redis Authentication Examples
#### Redis with Password Only (requirepass)
```bash
helm install paperless-ngx . \
--set redis.external.host=redis.example.com \
--set redis.external.password=myredispassword
```
Or with existing secret:
### Redis with Username and Password (ACL)
```yaml
redis:
external:
host: "redis.example.com"
existingSecret: "redis-auth-secret"
passwordKey: "redis-password"
username: "paperless-user"
password: "myredispassword"
```
#### Redis with Username and Password (Redis 6.0+ ACL)
```bash
helm install paperless-ngx . \
--set redis.external.host=redis.example.com \
--set redis.external.username=paperless-user \
--set redis.external.password=myredispassword
```
#### Multiple Paperless Instances on One Redis Server
### Sharing Redis Among Multiple Instances
Use the `prefix` parameter to avoid key collisions:
@ -271,35 +127,8 @@ redis:
prefix: "paperless-staging"
```
## Security Considerations
1. **Use external secrets** for production deployments to store sensitive data like database passwords, Redis passwords, and the Django secret key.
2. **Set a proper PAPERLESS_URL** when exposing the application externally.
3. **Configure ALLOWED_HOSTS** to restrict which hosts can access the application.
4. **Use HTTPS** when exposing the application to the internet.
5. **Secure Redis**: Always use authentication (password or username/password) for Redis in production environments. Use `existingSecret` instead of plain text passwords.
6. **Container Security**: The container runs as root initially to allow s6-overlay to set up the runtime environment, then drops privileges to UID 1000. This is required for the Paperless-ngx Docker image to function properly.
## Volumes and Data
Paperless-ngx uses several directories:
- **Data directory**: Contains the search index, classification model, and SQLite database (if used)
- **Media directory**: Contains all uploaded documents and thumbnails
- **Consume directory**: Drop documents here for automatic processing
- **Export directory**: Used for document exports
All directories can be configured with separate PVCs and storage classes.
### Using Existing PVCs
The chart supports using existing PersistentVolumeClaims instead of creating new ones. This is useful for:
- Migrating from an existing Paperless-ngx deployment
- Using pre-provisioned storage with specific settings
- Sharing volumes across deployments
To use an existing PVC, specify the `existingClaim` parameter for the relevant volume:
```yaml
persistence:
data:
@ -310,39 +139,171 @@ persistence:
existingClaim: "my-existing-media-pvc"
export:
enabled: true
existingClaim: "" # Will create new PVC
consume:
enabled: true
existingClaim: "" # Will create new PVC
```
When `existingClaim` is specified:
- The chart will **NOT** create a new PVC
- The specified PVC must already exist in the same namespace
- `storageClass`, `size`, and `accessMode` parameters are ignored for that volume
- You can mix existing and new PVCs (some volumes with `existingClaim`, others without)
When `existingClaim` is set, the chart skips PVC creation and `storageClass`/`size` are ignored for that volume.
## Uninstalling the Chart
## Parameters
To uninstall/delete the `paperless-ngx` deployment:
### Global Parameters
| Name | Description | Default |
|------|-------------|---------|
| `nameOverride` | Override the release name | `""` |
| `fullnameOverride` | Fully override the release name | `""` |
### Image Parameters
| Name | Description | Default |
|------|-------------|---------|
| `image.repository` | Paperless-ngx image repository | `ghcr.io/paperless-ngx/paperless-ngx` |
| `image.tag` | Image tag | `2.20.3` |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
### Deployment Parameters
| Name | Description | Default |
|------|-------------|---------|
| `replicaCount` | Number of replicas | `1` |
| `revisionHistoryLimit` | Revisions to retain | `3` |
| `podSecurityContext.runAsNonRoot` | Run as non-root | `false` |
| `podSecurityContext.runAsUser` | User ID | `0` |
| `podSecurityContext.fsGroup` | Filesystem group ID | `1000` |
| `nodeSelector` | Node selector | `{}` |
| `tolerations` | Tolerations | `[]` |
| `affinity` | Affinity rules | `{}` |
### Service Parameters
| Name | Description | Default |
|------|-------------|---------|
| `service.type` | Service type | `ClusterIP` |
| `service.port` | Service port | `8000` |
### Ingress Parameters
| Name | Description | Default |
|------|-------------|---------|
| `ingress.enabled` | Enable ingress | `false` |
| `ingress.className` | Ingress class name | `""` |
| `ingress.annotations` | Ingress annotations | See values.yaml |
| `ingress.hosts` | Ingress hosts | See values.yaml |
| `ingress.tls` | TLS configuration | See values.yaml |
### PostgreSQL Configuration (Required)
| Name | Description | Default |
|------|-------------|---------|
| `postgresql.external.enabled` | Enable external PostgreSQL | `true` |
| `postgresql.external.host` | PostgreSQL host | `postgresql.default.svc.cluster.local` |
| `postgresql.external.port` | PostgreSQL port | `5432` |
| `postgresql.external.database` | Database name | `paperless` |
| `postgresql.external.username` | Username | `paperless` |
| `postgresql.external.password` | Password | `""` |
| `postgresql.external.existingSecret` | Existing secret name | `""` |
| `postgresql.external.passwordKey` | Key for password in secret | `postgresql-password` |
### Redis Configuration (Required)
| Name | Description | Default |
|------|-------------|---------|
| `redis.external.enabled` | Enable external Redis | `true` |
| `redis.external.host` | Redis host | `redis.default.svc.cluster.local` |
| `redis.external.port` | Redis port | `6379` |
| `redis.external.database` | Redis database number | `0` |
| `redis.external.username` | Redis username (6.0+ ACL) | `""` |
| `redis.external.password` | Redis password | `""` |
| `redis.external.existingSecret` | Existing secret name | `""` |
| `redis.external.urlKey` | Key for full Redis URL in secret | `redis-url` |
| `redis.external.passwordKey` | Key for password in secret | `redis-password` |
| `redis.external.prefix` | Key prefix for multi-instance | `""` |
### Application Configuration
| Name | Description | Default |
|------|-------------|---------|
| `config.url` | External URL | `""` |
| `config.allowedHosts` | Allowed hosts (comma-separated) | `*` |
| `config.csrfTrustedOrigins` | CSRF trusted origins | `""` |
| `config.timeZone` | Timezone | `UTC` |
| `config.ocr.language` | OCR language (3-letter code) | `eng` |
| `config.ocr.mode` | OCR mode (`skip`, `redo`, `force`) | `skip` |
| `config.consumer.recursive` | Recursive consume directory | `false` |
| `config.consumer.subdirsAsTags` | Use subdirectory names as tags | `false` |
### Security Configuration
| Name | Description | Default |
|------|-------------|---------|
| `config.secretKey.existingSecret` | Existing secret for Django secret key | `""` |
| `config.secretKey.secretKey` | Key in secret | `secret-key` |
| `config.admin.user` | Admin username to create on startup | `""` |
| `config.admin.password` | Admin password | `""` |
| `config.admin.email` | Admin email | `root@localhost` |
| `config.admin.existingSecret` | Existing secret for admin credentials | `""` |
### Persistence Parameters
| Name | Description | Default |
|------|-------------|---------|
| `persistence.data.enabled` | Enable data PVC | `true` |
| `persistence.data.existingClaim` | Existing data PVC | `""` |
| `persistence.data.size` | Data PVC size | `1Gi` |
| `persistence.media.enabled` | Enable media PVC | `true` |
| `persistence.media.existingClaim` | Existing media PVC | `""` |
| `persistence.media.size` | Media PVC size | `10Gi` |
| `persistence.consume.enabled` | Enable consume PVC | `true` |
| `persistence.consume.existingClaim` | Existing consume PVC | `""` |
| `persistence.consume.size` | Consume PVC size | `5Gi` |
| `persistence.export.enabled` | Enable export PVC | `true` |
| `persistence.export.existingClaim` | Existing export PVC | `""` |
| `persistence.export.size` | Export PVC size | `1Gi` |
### Resource Parameters
| Name | Description | Default |
|------|-------------|---------|
| `resources` | Resource limits and requests | `{}` |
### Health Check Parameters
| Name | Description | Default |
|------|-------------|---------|
| `probes.liveness.enabled` | Enable liveness probe | `true` |
| `probes.liveness.path` | Liveness probe path | `/` |
| `probes.liveness.initialDelaySeconds` | Liveness initial delay | `60` |
| `probes.liveness.periodSeconds` | Liveness period | `10` |
| `probes.readiness.enabled` | Enable readiness probe | `true` |
| `probes.readiness.path` | Readiness probe path | `/` |
| `probes.readiness.initialDelaySeconds` | Readiness initial delay | `30` |
| `probes.readiness.periodSeconds` | Readiness period | `5` |
### Autoscaling Parameters
| Name | Description | Default |
|------|-------------|---------|
| `autoscaling.enabled` | Enable HPA | `false` |
| `autoscaling.minReplicas` | Min replicas | `1` |
| `autoscaling.maxReplicas` | Max replicas | `3` |
| `autoscaling.targetCPUUtilizationPercentage` | Target CPU | `80` |
| `autoscaling.targetMemoryUtilizationPercentage` | Target memory | `80` |
## Troubleshooting
- **Database Connection**: Verify PostgreSQL credentials and that the database exists
- **Redis Connection**: Ensure Redis is accessible; use `prefix` if sharing Redis between instances
- **Allowed Hosts Error**: Set `config.allowedHosts` to your domain when exposed externally
- **Container Security**: The container runs as root initially for s6-overlay setup, then drops to UID 1000. This is required by the Paperless-ngx image.
```bash
helm uninstall paperless-ngx
kubectl logs -f deployment/paperless-ngx
kubectl describe pod -l app.kubernetes.io/name=paperless-ngx
```
The command removes all the Kubernetes components associated with the chart and deletes the release.
## Contributing
Please feel free to contribute by opening issues or pull requests at:
https://github.com/rtomik/helm-charts
## License
This Helm chart is licensed under the MIT License.
## Links
- [Paperless-ngx GitHub](https://github.com/paperless-ngx/paperless-ngx)
- [Paperless-ngx Documentation](https://docs.paperless-ngx.com/)
- [Paperless-ngx GitHub Repository](https://github.com/paperless-ngx/paperless-ngx)
- [Docker Hub](https://hub.docker.com/r/ghcr.io/paperless-ngx/paperless-ngx)
- [Chart Source](https://github.com/rtomik/helm-charts/tree/main/charts/paperless-ngx)

View File

@ -1,117 +1,52 @@
# qBittorrent with Gluetun VPN
# qBittorrent VPN Helm Chart
A Helm chart for deploying qBittorrent with a Gluetun VPN sidecar container on Kubernetes.
A Helm chart for deploying [qBittorrent](https://www.qbittorrent.org/) with a [Gluetun](https://github.com/qdm12/gluetun) VPN sidecar on Kubernetes.
## Introduction
This chart deploys [qBittorrent](https://www.qbittorrent.org/) alongside [Gluetun](https://github.com/qdm12/gluetun), a VPN client/tunnel in a container, to ensure all BitTorrent traffic is routed through the VPN. The chart supports all major VPN providers and protocols through Gluetun's comprehensive compatibility.
This chart deploys qBittorrent alongside Gluetun, ensuring all BitTorrent traffic is routed through a VPN. It supports 30+ VPN providers via OpenVPN or WireGuard, includes a kill-switch firewall, and exposes HTTP/Socks proxy services.
Source code can be found here:
- https://github.com/rtomik/helm-charts/tree/main/charts/qbittorrent-vpn
Source code: https://github.com/rtomik/helm-charts/tree/main/charts/qbittorrent-vpn
Note: Currently only tested with NordVPN an OpenVPN configuration.
## Features
- **Multiple VPN Providers**: Support for 30+ VPN providers including NordVPN, ProtonVPN, Private Internet Access, ExpressVPN, Surfshark, Mullvad, and more
- **Protocol Support**: Use OpenVPN or WireGuard based on your provider's capabilities
- **Server Selection**: Choose servers by country, city, or specific hostnames with optional randomization
- **Security**: Proper container security settings to ensure traffic only flows through the VPN
- **Health Monitoring**: Integrated health checks for both qBittorrent and the VPN connection
- **Persistence**: Separate volume storage for configuration and downloads
- **Web UI**: Access qBittorrent via web interface with optional ingress support
- **Proxy Services**: HTTP and Shadowsocks proxies for additional devices to use the VPN tunnel
**Note**: Currently only tested with NordVPN and OpenVPN configuration.
## Prerequisites
- Kubernetes 1.19+
- Helm 3.2.0+
- PV provisioner support in the cluster
- A valid subscription to a VPN service
- A valid VPN subscription
## Installation
### Add the Repository
## Installing the Chart
```bash
helm repo add rtomik-charts https://rtomik.github.io/helm-charts
helm repo update
helm repo add rtomik https://rtomik.github.io/helm-charts
helm install qbittorrent-vpn rtomik/qbittorrent-vpn
```
### Create a Secret for VPN Credentials
For better security, store your VPN credentials in a Kubernetes secret:
## Uninstalling the Chart
```bash
# For OpenVPN authentication
kubectl create secret generic vpn-credentials \
--namespace default \
--from-literal=username='your-vpn-username' \
--from-literal=password='your-vpn-password'
# For WireGuard authentication (if using WireGuard)
kubectl create secret generic wireguard-keys \
--namespace default \
--from-literal=private_key='your-wireguard-private-key'
helm uninstall qbittorrent-vpn
```
Then reference this secret in your values:
```yaml
gluetun:
credentials:
create: false
existingSecret: "vpn-credentials"
usernameKey: "username"
passwordKey: "password"
```
### Install the Chart
```bash
# Option 1: Installation with custom values file (recommended)
helm install qbittorrent-vpn rtomik-charts/qbittorrent-vpn -f values.yaml -n media
# Option 2: Installation with inline parameter overrides
helm install qbittorrent-vpn rtomik-charts/qbittorrent-vpn -n media \
--set gluetun.vpn.provider=nordvpn \
--set gluetun.vpn.serverCountries=Germany \
--set-string gluetun.credentials.existingSecret=vpn-credentials
```
## Uninstallation
```bash
helm uninstall qbittorrent-vpn -n media
```
Note: This will not delete Persistent Volume Claims. To delete them:
**Note**: PVCs are not deleted automatically. To remove them:
```bash
kubectl delete pvc -l app.kubernetes.io/instance=qbittorrent-vpn
```
## Configuration
## Configuration Examples
### Key Parameters
### NordVPN with Existing Secret
| Parameter | Description | Default |
|---------------------------------------|-------------------------------------------------------|----------------------------|
| `qbittorrent.image.repository` | qBittorrent image repository | `linuxserver/qbittorrent` |
| `qbittorrent.image.tag` | qBittorrent image tag | `latest` |
| `gluetun.image.repository` | Gluetun image repository | `qmcgaw/gluetun` |
| `gluetun.image.tag` | Gluetun image tag | `v3.40.0` |
| `gluetun.vpn.provider` | VPN provider name | `nordvpn` |
| `gluetun.vpn.type` | VPN protocol (`openvpn` or `wireguard`) | `openvpn` |
| `gluetun.vpn.serverCountries` | Countries to connect to (comma-separated) | `Germany` |
| `persistence.config.size` | Size of PVC for qBittorrent config | `2Gi` |
| `persistence.downloads.size` | Size of PVC for downloads | `100Gi` |
| `ingress.enabled` | Enable ingress controller resource | `true` |
| `ingress.hosts[0].host` | Hostname for the ingress | `qbittorrent.domain.com` |
First, create a secret with your VPN credentials:
For a complete list of parameters, see the [values.yaml](values.yaml) file.
### Example: Using with NordVPN
```bash
kubectl create secret generic vpn-credentials \
--from-literal=username='your-vpn-username' \
--from-literal=password='your-vpn-password'
```
```yaml
gluetun:
@ -120,14 +55,26 @@ gluetun:
type: "openvpn"
serverCountries: "United States"
openvpn:
NORDVPN_CATEGORY: "P2P" # For torrent-optimized servers
NORDVPN_CATEGORY: "P2P"
credentials:
create: true
username: "your-nordvpn-username"
password: "your-nordvpn-password"
create: false
existingSecret: "vpn-credentials"
usernameKey: "username"
passwordKey: "password"
ingress:
enabled: true
hosts:
- host: qbittorrent.example.com
paths:
- path: /
pathType: Prefix
tls:
- hosts:
- qbittorrent.example.com
```
### Example: Using with ProtonVPN
### ProtonVPN
```yaml
gluetun:
@ -136,15 +83,15 @@ gluetun:
type: "openvpn"
serverCountries: "Switzerland"
openvpn:
PROTONVPN_TIER: "2" # 0 is free, 2 is paid (Plus/Visionary)
SERVER_FEATURES: "p2p" # For torrent support
PROTONVPN_TIER: "2"
SERVER_FEATURES: "p2p"
credentials:
create: true
username: "protonvpn-username"
password: "protonvpn-password"
```
### Example: Using with Private Internet Access
### Private Internet Access with Port Forwarding
```yaml
gluetun:
@ -157,40 +104,11 @@ gluetun:
username: "pia-username"
password: "pia-password"
settings:
VPN_PORT_FORWARDING: "on" # PIA supports port forwarding
VPN_PORT_FORWARDING: "on"
STATUS_FILE: "/tmp/gluetun-status.json"
```
## VPN Provider Support
This chart supports all VPN providers compatible with Gluetun, including:
- AirVPN
- Cyberghost
- ExpressVPN
- FastestVPN
- HideMyAss
- IPVanish
- IVPN
- Mullvad
- NordVPN
- Perfect Privacy
- Private Internet Access (PIA)
- PrivateVPN
- ProtonVPN
- PureVPN
- Surfshark
- TorGuard
- VyprVPN
- WeVPN
- Windscribe
For the complete list and provider-specific options, see the [Gluetun Providers Documentation](https://github.com/qdm12/gluetun-wiki/tree/main/setup/providers).
## Additional Features
### Accessing the HTTP Proxy
Gluetun provides an HTTP proxy on port 8888 that can be used by other applications to route traffic through the VPN. To expose this proxy:
### Proxy Services
```yaml
service:
@ -200,33 +118,9 @@ service:
socksPort: 8388
```
### Firewall Settings
### Custom Sidecar (NATMap)
By default, the chart enables the Gluetun firewall to prevent leaks if the VPN connection drops. You can customize this:
```yaml
gluetun:
settings:
FIREWALL: "on"
FIREWALL_OUTBOUND_SUBNETS: "10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
```
### Port Forwarding
For VPN providers that support port forwarding (like PIA):
```yaml
gluetun:
settings:
VPN_PORT_FORWARDING: "on"
STATUS_FILE: "/tmp/gluetun-status.json"
```
### Custom Sidecar Containers
The chart supports adding custom sidecar containers to the pod. This is useful for adding additional functionality like port forwarding management (NATMap), monitoring, or other helper containers.
Sidecars are specified using the standard Kubernetes container specification:
Sidecars can access shared volumes: `config`, `downloads`, and `gluetun-config`.
```yaml
sidecars:
@ -246,38 +140,146 @@ sidecars:
subPath: natmap
```
**Common Use Cases:**
## Parameters
1. **NATMap**: Automatically update port forwarding configurations
2. **Monitoring**: Add monitoring agents or exporters
3. **Custom Scripts**: Run periodic maintenance or update tasks
### qBittorrent Parameters
**Sharing Volumes:**
| Name | Description | Default |
|------|-------------|---------|
| `qbittorrent.image.repository` | qBittorrent image repository | `linuxserver/qbittorrent` |
| `qbittorrent.image.tag` | qBittorrent image tag | `5.1.0` |
| `qbittorrent.image.pullPolicy` | Image pull policy | `IfNotPresent` |
| `qbittorrent.bittorrentPort` | BitTorrent traffic port | `6881` |
| `qbittorrent.service.port` | Web UI port | `8080` |
Sidecars can access the same volumes as the main containers:
- `config`: qBittorrent configuration volume
- `downloads`: Downloads volume
- `gluetun-config`: Gluetun configuration volume (if enabled)
### Gluetun VPN Parameters
For the full Kubernetes container specification reference, see the [Kubernetes documentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.28/#container-v1-core).
| Name | Description | Default |
|------|-------------|---------|
| `gluetun.enabled` | Enable Gluetun VPN sidecar | `true` |
| `gluetun.image.repository` | Gluetun image repository | `qmcgaw/gluetun` |
| `gluetun.image.tag` | Gluetun image tag | `v3.40.0` |
| `gluetun.vpn.provider` | VPN provider name | `nordvpn` |
| `gluetun.vpn.type` | VPN protocol (`openvpn` or `wireguard`) | `openvpn` |
| `gluetun.vpn.serverCountries` | Countries to connect (comma-separated) | `Netherlands` |
| `gluetun.vpn.serverCities` | Cities to connect (optional) | `""` |
| `gluetun.vpn.serverNames` | Specific server names (optional) | `""` |
| `gluetun.vpn.randomize` | Randomize server selection | `true` |
### VPN Credentials
| Name | Description | Default |
|------|-------------|---------|
| `gluetun.credentials.create` | Create credentials secret | `true` |
| `gluetun.credentials.username` | VPN username (if creating secret) | `""` |
| `gluetun.credentials.password` | VPN password (if creating secret) | `""` |
| `gluetun.credentials.existingSecret` | Existing secret name | `""` |
| `gluetun.credentials.usernameKey` | Key for username in secret | `username` |
| `gluetun.credentials.passwordKey` | Key for password in secret | `password` |
### Gluetun Settings
| Name | Description | Default |
|------|-------------|---------|
| `gluetun.settings.FIREWALL` | Enable firewall | `on` |
| `gluetun.settings.FIREWALL_OUTBOUND_SUBNETS` | Allowed outbound subnets | `10.0.0.0/8,172.16.0.0/12,192.168.0.0/16` |
| `gluetun.settings.FIREWALL_INPUT_PORTS` | Ports allowed through firewall | `8080` |
| `gluetun.settings.FIREWALL_DEBUG` | Enable firewall debug | `on` |
| `gluetun.settings.VPN_PORT_FORWARDING` | Enable port forwarding | `off` |
| `gluetun.settings.DNS_ADDRESS` | DNS server address | `1.1.1.1` |
| `gluetun.resources.limits.cpu` | CPU limit | `300m` |
| `gluetun.resources.limits.memory` | Memory limit | `256Mi` |
### WireGuard Configuration (when using WireGuard)
| Name | Description | Default |
|------|-------------|---------|
| `gluetun.vpn.wireguard.privateKey` | WireGuard private key | `""` |
| `gluetun.vpn.wireguard.privateKeyExistingSecret` | Existing secret with private key | `""` |
| `gluetun.vpn.wireguard.addresses` | WireGuard addresses | `""` |
| `gluetun.vpn.wireguard.endpointIP` | Server endpoint IP (optional) | `""` |
| `gluetun.vpn.wireguard.endpointPort` | Server endpoint port (optional) | `""` |
### Deployment Parameters
| Name | Description | Default |
|------|-------------|---------|
| `replicaCount` | Number of replicas | `1` |
| `revisionHistoryLimit` | Revisions to retain | `3` |
| `podSecurityContext.runAsNonRoot` | Run as non-root | `false` |
| `podSecurityContext.runAsUser` | User ID | `0` |
| `podSecurityContext.fsGroup` | Filesystem group ID | `0` |
| `nodeSelector` | Node selector | `{}` |
| `tolerations` | Tolerations | `[]` |
| `affinity` | Affinity rules | `{}` |
### Service Parameters
| Name | Description | Default |
|------|-------------|---------|
| `service.type` | Service type | `ClusterIP` |
| `service.port` | Service port | `8080` |
| `service.proxies.enabled` | Enable HTTP/Socks proxy services | `false` |
| `service.proxies.httpPort` | HTTP proxy port | `8888` |
| `service.proxies.socksPort` | Socks proxy port | `8388` |
### Ingress Parameters
| Name | Description | Default |
|------|-------------|---------|
| `ingress.enabled` | Enable ingress | `false` |
| `ingress.className` | Ingress class name | `""` |
| `ingress.annotations` | Ingress annotations | `[]` |
| `ingress.hosts` | Ingress hosts | See values.yaml |
| `ingress.tls` | TLS configuration | See values.yaml |
### Persistence Parameters
| Name | Description | Default |
|------|-------------|---------|
| `qbittorrent.persistence.config.enabled` | Enable config PVC | `true` |
| `qbittorrent.persistence.config.existingClaim` | Existing config PVC | `""` |
| `qbittorrent.persistence.config.size` | Config PVC size | `2Gi` |
| `qbittorrent.persistence.downloads.enabled` | Enable downloads PVC | `true` |
| `qbittorrent.persistence.downloads.existingClaim` | Existing downloads PVC | `""` |
| `qbittorrent.persistence.downloads.size` | Downloads PVC size | `2Gi` |
| `gluetun.persistence.enabled` | Enable Gluetun config PVC | `true` |
| `gluetun.persistence.size` | Gluetun config PVC size | `100Mi` |
### Sidecar Parameters
| Name | Description | Default |
|------|-------------|---------|
| `sidecars` | Additional sidecar containers | `[]` |
### Health Check Parameters
| Name | Description | Default |
|------|-------------|---------|
| `probes.liveness.enabled` | Enable liveness probe | `true` |
| `probes.liveness.path` | Liveness probe path | `/` |
| `probes.liveness.periodSeconds` | Liveness period | `30` |
| `probes.readiness.enabled` | Enable readiness probe | `true` |
| `probes.readiness.path` | Readiness probe path | `/` |
| `probes.readiness.periodSeconds` | Readiness period | `10` |
## Supported VPN Providers
AirVPN, Cyberghost, ExpressVPN, FastestVPN, HideMyAss, IPVanish, IVPN, Mullvad, NordVPN, Perfect Privacy, Private Internet Access, PrivateVPN, ProtonVPN, PureVPN, Surfshark, TorGuard, VyprVPN, WeVPN, Windscribe, and more.
See the [Gluetun Providers Documentation](https://github.com/qdm12/gluetun-wiki/tree/main/setup/providers) for the full list and provider-specific options.
## Troubleshooting
### VPN Connection Issues
### VPN Not Connecting
If the VPN isn't connecting properly:
1. Check the Gluetun logs:
```bash
kubectl logs deployment/qbittorrent-vpn -c gluetun
```
2. Verify your credentials are correct:
```bash
kubectl describe secret vpn-credentials
```
3. Try setting the log level to debug for more detailed information:
Enable debug logging for more detail:
```yaml
gluetun:
extraEnv:
@ -285,28 +287,17 @@ If the VPN isn't connecting properly:
value: "debug"
```
### qBittorrent Can't Create Directories
### Directory Creation Errors
If you see errors like "Could not create required directory":
Ensure the init container is enabled and `fsGroup` is set in `podSecurityContext`.
1. Make sure the init container is enabled and properly configured
2. Ensure proper `fsGroup` is set in the `podSecurityContext`
3. Check that the persistence volume allows the correct permissions
### Firewall / Network Issues
### Firewall/Security Issues
Gluetun requires `privileged: true` and `NET_ADMIN` capability. Verify `/dev/net/tun` is mounted correctly.
If you encounter iptables or network issues:
## Links
1. Ensure the Gluetun container has `privileged: true`
2. Verify the `NET_ADMIN` capability is added
3. Check that the `/dev/net/tun` device is correctly mounted
## License
This chart is licensed under the MIT License.
## Acknowledgements
- [Gluetun](https://github.com/qdm12/gluetun) by [qdm12](https://github.com/qdm12)
- [LinuxServer.io](https://linuxserver.io/) for the qBittorrent container
- The qBittorrent team for the excellent torrent client
- [qBittorrent](https://www.qbittorrent.org/)
- [Gluetun GitHub](https://github.com/qdm12/gluetun)
- [Gluetun Provider Setup](https://github.com/qdm12/gluetun-wiki/tree/main/setup/providers)
- [Chart Source](https://github.com/rtomik/helm-charts/tree/main/charts/qbittorrent-vpn)

View File

@ -1,140 +1,43 @@
# Recipya Helm Chart
A Helm chart for deploying [Recipya](https://github.com/reaper47/recipya) on Kubernetes.
[Source Code](https://github.com/rtomik/helm-charts/tree/main/charts%2Frecipya)
A Helm chart for deploying [Recipya](https://github.com/reaper47/recipya), a recipe management application, on Kubernetes.
## Introduction
This chart deploys Recipya recipe manager on a Kubernetes cluster using the Helm package manager.
This chart deploys Recipya on a Kubernetes cluster using the Helm package manager. Recipya includes optimized Traefik ingress configuration with Content Security Policy support and sticky session handling for authentication.
Source code can be found here:
- https://github.com/rtomik/helm-charts/tree/main/charts/recipya
Source code: https://github.com/rtomik/helm-charts/tree/main/charts/recipya
## Prerequisites
- Kubernetes 1.19+
- Helm 3.2.0+
- PV provisioner support in the underlying infrastructure (if persistence is needed)
- PV provisioner support (if persistence is needed)
## Installing the Chart
To install the chart with the release name `recipya`:
```bash
helm repo add recipya-chart https://rtomik.github.io/helm-charts
helm install recipya recipya-chart/recipya -n recipya
helm repo add rtomik https://rtomik.github.io/helm-charts
helm install recipya rtomik/recipya
```
The command deploys Recipya on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation.
## Uninstalling the Chart
To uninstall/delete the `recipya` deployment:
```bash
helm uninstall recipya -n recipya
helm uninstall recipya
```
## Important Configuration Notes
## Configuration Examples
### Server URL
### Minimal Installation
When deploying with an ingress, it's **critical** to set `config.server.url` to match your ingress URL (including https if you're using TLS). This ensures that redirects after login work correctly:
> **Important**: Set `config.server.url` to match your ingress URL including the scheme. This is required for post-login redirects to work correctly.
```yaml
config:
server:
url: "https://your-recipya-domain.com"
```
url: "https://recipya.example.com"
### Ingress Configuration
This chart includes optimized ingress configurations for Traefik, with support for WebSockets and proper security headers. If you're using a different ingress controller, you may need to adjust annotations accordingly.
## Parameters
### Global parameters
| Name | Description | Value |
|--------------------------|--------------------------------------|-----------------|
| `image.repository` | Recipya image repository | `reaper99/recipya` |
| `image.tag` | Recipya image tag | `v1.2.2` |
| `image.pullPolicy` | Recipya image pull policy | `IfNotPresent` |
| `replicaCount` | Number of Recipya replicas | `1` |
| `revisionHistoryLimit` | Number of revisions to keep | `3` |
### Security parameters
| Name | Description | Value |
|-----------------------------------------|--------------------------------------------------|-----------|
| `podSecurityContext.fsGroup` | Group ID for the Recipya container | `1000` |
| `containerSecurityContext` | Security context for the container | `{}` |
### Recipya configuration parameters
| Name | Description | Value |
|-----------------------------------------|-------------------------------------------------------|---------------------|
| `config.server.port` | Server port | `8078` |
| `config.server.autologin` | Whether to login automatically | `false` |
| `config.server.is_demo` | Whether the app is a demo version | `false` |
| `config.server.is_prod` | Whether the app is in production | `false` |
| `config.server.no_signups` | Whether to disable user account registrations | `false` |
| `config.server.url` | Base URL for the application | `http://0.0.0.0` |
| `config.email.address` | The email address for SendGrid | `""` |
| `config.email.sendgrid` | SendGrid API key | `""` |
| `config.documentIntelligence.endpoint` | Azure Document Intelligence endpoint | `""` |
| `config.documentIntelligence.key` | Azure Document Intelligence key | `""` |
### Service parameters
| Name | Description | Value |
|--------------------------|--------------------------------------------------|-------------|
| `service.type` | Recipya service type | `ClusterIP` |
| `service.port` | Recipya service port | `8078` |
### Ingress parameters
| Name | Description | Value |
|-------------------------------|--------------------------------------------------|------------------------|
| `ingress.enabled` | Enable ingress controller resource | `false` |
| `ingress.className` | IngressClass that will be used | `"traefik"` |
| `ingress.annotations` | Additional ingress annotations | See values.yaml |
| `ingress.hosts[0].host` | Default host for the ingress resource | `chart-example.local` |
| `ingress.tls` | TLS configuration | `[]` |
### Persistence parameters
| Name | Description | Value |
|--------------------------------------|------------------------------------------|------------------|
| `persistence.enabled` | Enable persistence using PVC | `true` |
| `persistence.accessMode` | PVC Access Mode | `ReadWriteOnce` |
| `persistence.size` | PVC Storage Request | `1Gi` |
| `persistence.storageClass` | Storage class of backing PVC | `""` |
### Resource parameters
| Name | Description | Value |
|-------------------------------|------------------------------------------|-----------|
| `resources.limits.cpu` | CPU limit | `500m` |
| `resources.limits.memory` | Memory limit | `512Mi` |
| `resources.requests.cpu` | CPU request | `100m` |
| `resources.requests.memory` | Memory request | `128Mi` |
### Probe parameters
| Name | Description | Value |
|--------------------------------------|--------------------------------------------|-----------|
| `probes.liveness.enabled` | Enable liveness probe | `true` |
| `probes.liveness.path` | Path for liveness probe | `/` |
| `probes.readiness.enabled` | Enable readiness probe | `true` |
| `probes.readiness.path` | Path for readiness probe | `/` |
## Traefik Ingress Configuration
The chart includes specially configured middlewares for Traefik to ensure proper functioning of Recipya:
```yaml
ingress:
enabled: true
className: "traefik"
@ -153,34 +56,16 @@ ingress:
- recipya.example.com
```
This configuration includes:
1. Custom Content Security Policy allowing essential scripts from unpkg.com
2. Sticky sessions for maintaining authentication
3. Proper headers for proxy operation
## Content Security Policy Configuration
The chart includes a custom middleware that configures the proper Content Security Policy for Recipya. This is particularly important as the application requires access to external scripts from unpkg.com:
### With SendGrid Email
```yaml
contentSecurityPolicy: >-
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: data: https://unpkg.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob:;
font-src 'self' data:;
connect-src 'self' ws: wss: *;
worker-src 'self' blob:;
frame-src 'self';
media-src 'self' blob:;
object-src 'none';
form-action 'self';
config:
email:
address: "your-email@example.com"
sendgrid: "SG.your-sendgrid-api-key"
```
## Using Existing Secrets
If you want to use existing secrets for sensitive data:
### With SendGrid and Azure Document Intelligence via Existing Secrets
```yaml
config:
@ -194,6 +79,127 @@ config:
keyKey: "di_key"
```
## Configuration
## Parameters
See the [Recipya documentation](https://recipes.musicavis.ca/docs/installation/docker/#environment-variables) for details on all available configuration options.
### Global Parameters
| Name | Description | Default |
|------|-------------|---------|
| `nameOverride` | Override the release name | `""` |
| `fullnameOverride` | Fully override the release name | `""` |
| `replicaCount` | Number of replicas | `1` |
| `revisionHistoryLimit` | Revisions to retain | `3` |
### Image Parameters
| Name | Description | Default |
|------|-------------|---------|
| `image.repository` | Recipya image repository | `reaper99/recipya` |
| `image.tag` | Image tag | `v1.2.2` |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
| `imagePullSecrets` | Image pull secrets | `[]` |
### Pod Security Parameters
| Name | Description | Default |
|------|-------------|---------|
| `podSecurityContext.fsGroup` | Filesystem group ID | `1000` |
| `containerSecurityContext` | Container security context | `{}` |
### Application Configuration
| Name | Description | Default |
|------|-------------|---------|
| `config.server.port` | Server port | `8078` |
| `config.server.url` | Base URL (must match ingress) | `http://0.0.0.0` |
| `config.server.autologin` | Auto-login | `false` |
| `config.server.is_demo` | Demo mode | `false` |
| `config.server.is_prod` | Production mode | `true` |
| `config.server.no_signups` | Disable user registration | `false` |
| `config.email.address` | SendGrid email address | `""` |
| `config.email.sendgrid` | SendGrid API key | `""` |
| `config.email.existingSecret` | Existing secret for email | `""` |
| `config.email.addressKey` | Key for email address in secret | `email` |
| `config.email.sendgridKey` | Key for SendGrid key in secret | `sendgrid` |
| `config.documentIntelligence.endpoint` | Azure Document Intelligence endpoint | `""` |
| `config.documentIntelligence.key` | Azure Document Intelligence key | `""` |
| `config.documentIntelligence.existingSecret` | Existing secret for Azure DI | `""` |
| `config.documentIntelligence.endpointKey` | Key for endpoint in secret | `di_endpoint` |
| `config.documentIntelligence.keyKey` | Key for API key in secret | `di_key` |
### Service Parameters
| Name | Description | Default |
|------|-------------|---------|
| `service.type` | Service type | `ClusterIP` |
| `service.port` | Service port | `8078` |
### Ingress Parameters
| Name | Description | Default |
|------|-------------|---------|
| `ingress.enabled` | Enable ingress | `false` |
| `ingress.className` | Ingress class name | `""` |
| `ingress.annotations` | Ingress annotations | See values.yaml |
| `ingress.hosts` | Ingress hosts | See values.yaml |
| `ingress.tls` | TLS configuration | `[]` |
### Persistence Parameters
| Name | Description | Default |
|------|-------------|---------|
| `persistence.enabled` | Enable persistence | `false` |
| `persistence.storageClass` | Storage class | `""` |
| `persistence.accessMode` | Access mode | `ReadWriteOnce` |
| `persistence.size` | PVC size | `5Gi` |
| `persistence.annotations` | PVC annotations | `{}` |
### Resource Parameters
| Name | Description | Default |
|------|-------------|---------|
| `resources` | Resource limits and requests | `{}` |
### Health Check Parameters
| Name | Description | Default |
|------|-------------|---------|
| `probes.liveness.enabled` | Enable liveness probe | `true` |
| `probes.liveness.path` | Liveness probe path | `/` |
| `probes.liveness.initialDelaySeconds` | Liveness initial delay | `30` |
| `probes.liveness.periodSeconds` | Liveness period | `10` |
| `probes.readiness.enabled` | Enable readiness probe | `true` |
| `probes.readiness.path` | Readiness probe path | `/` |
| `probes.readiness.initialDelaySeconds` | Readiness initial delay | `30` |
| `probes.readiness.periodSeconds` | Readiness period | `10` |
## Troubleshooting
### Post-Login Redirect Fails
Ensure `config.server.url` matches your ingress URL exactly, including the scheme (`https://`).
### Content Security Policy Errors
The chart includes a Traefik middleware with a CSP policy allowing scripts from `unpkg.com`. If using a different ingress controller, configure an equivalent CSP policy:
```
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' blob: data: https://unpkg.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: blob:;
connect-src 'self' ws: wss: *;
```
### Debugging
```bash
kubectl logs deployment/recipya -f
kubectl describe pod -l app.kubernetes.io/name=recipya
```
## Links
- [Recipya GitHub](https://github.com/reaper47/recipya)
- [Recipya Documentation](https://recipes.musicavis.ca/docs/installation/docker/#environment-variables)
- [Chart Source](https://github.com/rtomik/helm-charts/tree/main/charts/recipya)

View File

@ -1,30 +1,42 @@
# Tandoor Recipes Helm Chart
A Helm chart for deploying [Tandoor Recipes](https://github.com/TandoorRecipes/recipes) on Kubernetes.
A Helm chart for deploying [Tandoor Recipes](https://github.com/TandoorRecipes/recipes), a recipe management application, on Kubernetes.
Tandoor is a recipe management application that allows you to manage your recipes, plan meals, and create shopping lists.
## Introduction
Source code can be found here:
- https://github.com/rtomik/helm-charts/tree/main/charts/tandoor
This chart deploys Tandoor Recipes on a Kubernetes cluster. Tandoor supports PostgreSQL databases, LDAP/OIDC authentication, S3 object storage, email notifications, AI features, and Food Data Central API integration for nutrition data.
Source code: https://github.com/rtomik/helm-charts/tree/main/charts/tandoor
## Prerequisites
- Kubernetes 1.19+
- Helm 3.0+
- PV provisioner support in the underlying infrastructure
- **External PostgreSQL database** (required - this chart does NOT include PostgreSQL)
- **External PostgreSQL database** (required — this chart does not include PostgreSQL)
- PV provisioner support
## Installing the Chart
```bash
helm repo add rtomik https://rtomik.github.io/helm-charts
helm repo update
helm install tandoor rtomik/tandoor -f values.yaml
helm install tandoor rtomik/tandoor
```
## Usage Examples
## Uninstalling the Chart
### Minimal Configuration
```bash
helm uninstall tandoor
```
**Note**: PVCs are not deleted automatically. To remove them:
```bash
kubectl delete pvc -l app.kubernetes.io/name=tandoor
```
## Configuration Examples
### Minimal Installation
```yaml
postgresql:
@ -38,7 +50,7 @@ config:
value: "your-secret-key-at-least-50-characters-long-for-security-purposes"
```
### Production Configuration
### Production with Existing Secrets
```yaml
postgresql:
@ -52,20 +64,10 @@ config:
secretKey:
existingSecret: "tandoor-app-secret"
secretKey: "secret-key"
allowedHosts: "tandoor.example.com"
csrfTrustedOrigins: "https://tandoor.example.com"
timezone: "Europe/Berlin"
# Optional: OpenID Connect with Authentik
# oidc:
# enabled: true
# providerId: "authentik"
# providerName: "Authentik"
# clientId: "your-client-id"
# clientSecret: "your-client-secret"
# serverUrl: "https://authentik.company/application/o/tandoor/.well-known/openid-configuration"
ingress:
enabled: true
className: "nginx"
@ -84,7 +86,6 @@ ingress:
persistence:
staticfiles:
enabled: true
# existingClaim: "my-existing-pvc"
storageClass: "longhorn"
size: 2Gi
mediafiles:
@ -101,323 +102,349 @@ resources:
memory: 256Mi
```
### OIDC with Authentik
## Configuration
```yaml
config:
oidc:
enabled: true
providerId: "authentik"
providerName: "Authentik"
clientId: "your-client-id"
clientSecret: "your-client-secret"
serverUrl: "https://authentik.company/application/o/tandoor/.well-known/openid-configuration"
```
All configuration options are based on the official Tandoor documentation:
https://docs.tandoor.dev/system/configuration/
### S3 Object Storage
The following table lists the configurable parameters and their default values.
### Global Parameters
| Name | Description | Value |
|------|-------------|-------|
| `nameOverride` | String to partially override the release name | `""` |
| `fullnameOverride` | String to fully override the release name | `""` |
### Image Parameters
| Name | Description | Value |
|------|-------------|-------|
| `image.repository` | Tandoor image repository | `vabene1111/recipes` |
| `image.tag` | Tandoor image tag | `2.3.5` |
| `image.pullPolicy` | Tandoor image pull policy | `IfNotPresent` |
### Deployment Parameters
| Name | Description | Value |
|------|-------------|-------|
| `replicaCount` | Number of Tandoor replicas | `1` |
| `revisionHistoryLimit` | Number of old ReplicaSets to retain | `3` |
### PostgreSQL Parameters
| Name | Description | Value |
|------|-------------|-------|
| `postgresql.host` | PostgreSQL host | `postgresql.default.svc.cluster.local` |
| `postgresql.port` | PostgreSQL port | `5432` |
| `postgresql.database` | PostgreSQL database name | `tandoor` |
| `postgresql.username` | PostgreSQL username | `tandoor` |
| `postgresql.password` | PostgreSQL password (not recommended for production) | `""` |
| `postgresql.existingSecret` | Existing secret with PostgreSQL credentials | `""` |
| `postgresql.passwordKey` | Key in existing secret for PostgreSQL password | `postgresql-password` |
### Security Configuration
| Name | Description | Value |
|------|-------------|-------|
| `config.secretKey.value` | Django secret key (at least 50 characters) | `""` |
| `config.secretKey.existingSecret` | Existing secret for Django secret key | `""` |
| `config.secretKey.secretKey` | Key in existing secret for Django secret key | `secret-key` |
| `config.allowedHosts` | Allowed hosts for HTTP Host Header validation | `*` |
| `config.csrfTrustedOrigins` | CSRF trusted origins | `""` |
| `config.corsAllowOrigins` | Enable CORS allow all origins | `false` |
### Server Configuration
| Name | Description | Value |
|------|-------------|-------|
| `config.tandoorPort` | Port where Tandoor exposes its web server | `8080` |
| `config.gunicornWorkers` | Number of Gunicorn worker processes | `3` |
| `config.gunicornThreads` | Number of Gunicorn threads per worker | `2` |
| `config.gunicornTimeout` | Gunicorn request timeout in seconds | `30` |
| `config.gunicornMedia` | Enable media serving via Gunicorn | `0` |
| `config.timezone` | Application timezone | `UTC` |
| `config.scriptName` | URL path base for subfolder deployments | `""` |
| `config.sessionCookieDomain` | Session cookie domain | `""` |
| `config.sessionCookieName` | Session cookie identifier | `sessionid` |
### Feature Configuration
| Name | Description | Value |
|------|-------------|-------|
| `config.enableSignup` | Allow user registration | `false` |
| `config.enableMetrics` | Enable Prometheus metrics at /metrics | `false` |
| `config.enablePdfExport` | Enable recipe PDF export | `false` |
| `config.sortTreeByName` | Sort keywords/foods alphabetically | `false` |
### Social Authentication
| Name | Description | Value |
|------|-------------|-------|
| `config.socialDefaultAccess` | Space ID for auto-joining new social auth users | `0` |
| `config.socialDefaultGroup` | Default group for new users (guest/user/admin) | `guest` |
| `config.socialProviders` | Comma-separated OAuth provider list | `""` |
| `config.socialAccountProviders` | SOCIALACCOUNT_PROVIDERS JSON (for complex setups) | `""` |
### OpenID Connect (OIDC) Configuration
| Name | Description | Value |
|------|-------------|-------|
| `config.oidc.enabled` | Enable OpenID Connect authentication | `false` |
| `config.oidc.providerId` | Provider ID (e.g., authentik, keycloak) | `authentik` |
| `config.oidc.providerName` | Display name on login page | `Authentik` |
| `config.oidc.clientId` | Client ID from OIDC provider | `""` |
| `config.oidc.clientSecret` | Client Secret from OIDC provider | `""` |
| `config.oidc.serverUrl` | OpenID Connect well-known configuration URL | `""` |
### LDAP Configuration
| Name | Description | Value |
|------|-------------|-------|
| `config.ldap.enabled` | Enable LDAP authentication | `false` |
| `config.ldap.serverUri` | LDAP server URI | `""` |
| `config.ldap.bindDn` | LDAP bind distinguished name | `""` |
| `config.ldap.bindPassword` | LDAP bind password | `""` |
| `config.ldap.userSearchBaseDn` | LDAP user search base | `""` |
| `config.ldap.tlsCacertFile` | LDAP TLS CA certificate file | `""` |
| `config.ldap.startTls` | Enable LDAP StartTLS | `false` |
| `config.ldap.existingSecret` | Existing secret for LDAP credentials | `""` |
| `config.ldap.bindPasswordKey` | Key in existing secret for LDAP password | `ldap-bind-password` |
### Remote User Authentication
| Name | Description | Value |
|------|-------------|-------|
| `config.remoteUserAuth` | Enable REMOTE-USER header authentication | `false` |
```yaml
config:
s3:
enabled: true
bucketName: "tandoor-media"
regionName: "us-east-1"
endpointUrl: "https://minio.example.com"
existingSecret: "tandoor-s3-secret"
```
### Email Configuration
| Name | Description | Value |
|------|-------------|-------|
| `config.email.host` | SMTP server hostname | `""` |
| `config.email.port` | SMTP server port | `25` |
| `config.email.user` | SMTP authentication username | `""` |
| `config.email.password` | SMTP authentication password | `""` |
| `config.email.useTls` | Enable TLS for email | `false` |
| `config.email.useSsl` | Enable SSL for email | `false` |
| `config.email.defaultFrom` | Default from email address | `webmaster@localhost` |
```yaml
config:
email:
host: "smtp.example.com"
port: 587
useTls: true
defaultFrom: "tandoor@example.com"
existingSecret: "tandoor-email-secret"
passwordKey: "email-password"
```
### LDAP Authentication
```yaml
config:
ldap:
enabled: true
serverUri: "ldap://ldap.example.com"
bindDn: "cn=admin,dc=example,dc=com"
bindPassword: "bind-password"
userSearchBaseDn: "ou=users,dc=example,dc=com"
existingSecret: "tandoor-ldap-secret"
bindPasswordKey: "ldap-bind-password"
```
## Parameters
### Global Parameters
| Name | Description | Default |
|------|-------------|---------|
| `nameOverride` | Override the release name | `""` |
| `fullnameOverride` | Fully override the release name | `""` |
### Image Parameters
| Name | Description | Default |
|------|-------------|---------|
| `image.repository` | Tandoor image repository | `vabene1111/recipes` |
| `image.tag` | Image tag | `2.3.5` |
| `image.pullPolicy` | Image pull policy | `IfNotPresent` |
### Deployment Parameters
| Name | Description | Default |
|------|-------------|---------|
| `replicaCount` | Number of replicas | `1` |
| `revisionHistoryLimit` | Revisions to retain | `3` |
| `podSecurityContext.fsGroup` | Filesystem group ID | `0` |
| `containerSecurityContext.runAsUser` | User ID | `0` |
| `containerSecurityContext.runAsGroup` | Group ID | `0` |
| `containerSecurityContext.allowPrivilegeEscalation` | Allow privilege escalation | `false` |
| `nodeSelector` | Node selector | `{}` |
| `tolerations` | Tolerations | `[]` |
| `affinity` | Affinity rules | `{}` |
### Service Parameters
| Name | Description | Default |
|------|-------------|---------|
| `service.type` | Service type | `ClusterIP` |
| `service.port` | Service port | `8080` |
### Ingress Parameters
| Name | Description | Default |
|------|-------------|---------|
| `ingress.enabled` | Enable ingress | `false` |
| `ingress.className` | Ingress class name | `""` |
| `ingress.annotations` | Ingress annotations | See values.yaml |
| `ingress.hosts` | Ingress hosts | See values.yaml |
| `ingress.tls` | TLS configuration | See values.yaml |
### PostgreSQL Configuration (Required)
| Name | Description | Default |
|------|-------------|---------|
| `postgresql.host` | PostgreSQL host | `postgresql.default.svc.cluster.local` |
| `postgresql.port` | PostgreSQL port | `5432` |
| `postgresql.database` | Database name | `tandoor` |
| `postgresql.username` | Username | `tandoor` |
| `postgresql.password` | Password | `""` |
| `postgresql.existingSecret` | Existing secret name | `""` |
| `postgresql.passwordKey` | Key for password in secret | `postgresql-password` |
### Security Configuration
| Name | Description | Default |
|------|-------------|---------|
| `config.secretKey.value` | Django secret key (min 50 chars) | `""` |
| `config.secretKey.existingSecret` | Existing secret for secret key | `""` |
| `config.secretKey.secretKey` | Key in secret | `secret-key` |
| `config.allowedHosts` | Allowed HTTP hosts | `*` |
| `config.csrfTrustedOrigins` | CSRF trusted origins | `""` |
| `config.corsAllowOrigins` | Allow all CORS origins | `false` |
### Server Configuration
| Name | Description | Default |
|------|-------------|---------|
| `config.tandoorPort` | Web server port | `8080` |
| `config.gunicornWorkers` | Gunicorn workers | `3` |
| `config.gunicornThreads` | Gunicorn threads per worker | `2` |
| `config.gunicornTimeout` | Gunicorn timeout (seconds) | `30` |
| `config.gunicornMedia` | Serve media via Gunicorn | `0` |
| `config.timezone` | Timezone | `UTC` |
| `config.scriptName` | URL path base for subfolder | `""` |
| `config.sessionCookieDomain` | Session cookie domain | `""` |
| `config.sessionCookieName` | Session cookie name | `sessionid` |
### Feature Configuration
| Name | Description | Default |
|------|-------------|---------|
| `config.enableSignup` | Allow user registration | `false` |
| `config.enableMetrics` | Enable Prometheus metrics | `false` |
| `config.enablePdfExport` | Enable PDF export | `false` |
| `config.sortTreeByName` | Sort keywords alphabetically | `false` |
### OIDC Configuration
| Name | Description | Default |
|------|-------------|---------|
| `config.oidc.enabled` | Enable OIDC | `false` |
| `config.oidc.providerId` | Provider ID | `authentik` |
| `config.oidc.providerName` | Provider display name | `Authentik` |
| `config.oidc.clientId` | Client ID | `""` |
| `config.oidc.clientSecret` | Client secret | `""` |
| `config.oidc.serverUrl` | Well-known configuration URL | `""` |
### LDAP Configuration
| Name | Description | Default |
|------|-------------|---------|
| `config.ldap.enabled` | Enable LDAP | `false` |
| `config.ldap.serverUri` | LDAP server URI | `""` |
| `config.ldap.bindDn` | Bind DN | `""` |
| `config.ldap.bindPassword` | Bind password | `""` |
| `config.ldap.userSearchBaseDn` | User search base DN | `""` |
| `config.ldap.tlsCacertFile` | TLS CA cert file | `""` |
| `config.ldap.startTls` | Enable StartTLS | `false` |
| `config.ldap.existingSecret` | Existing secret for LDAP | `""` |
| `config.ldap.bindPasswordKey` | Key for bind password in secret | `ldap-bind-password` |
### Email Configuration
| Name | Description | Default |
|------|-------------|---------|
| `config.email.host` | SMTP host | `""` |
| `config.email.port` | SMTP port | `25` |
| `config.email.user` | SMTP username | `""` |
| `config.email.password` | SMTP password | `""` |
| `config.email.useTls` | Enable TLS | `false` |
| `config.email.useSsl` | Enable SSL | `false` |
| `config.email.defaultFrom` | Default from address | `webmaster@localhost` |
| `config.email.accountEmailSubjectPrefix` | Email subject prefix | `[Tandoor Recipes]` |
| `config.email.existingSecret` | Existing secret for email credentials | `""` |
| `config.email.passwordKey` | Key in existing secret for email password | `email-password` |
| `config.email.existingSecret` | Existing secret for email | `""` |
| `config.email.passwordKey` | Key for password in secret | `email-password` |
### S3/Object Storage Configuration
### S3 Storage Configuration
| Name | Description | Value |
|------|-------------|-------|
| `config.s3.enabled` | Enable S3 storage for media files | `false` |
| Name | Description | Default |
|------|-------------|---------|
| `config.s3.enabled` | Enable S3 storage | `false` |
| `config.s3.accessKey` | S3 access key | `""` |
| `config.s3.secretAccessKey` | S3 secret access key | `""` |
| `config.s3.bucketName` | S3 bucket name | `""` |
| `config.s3.regionName` | S3 region name | `""` |
| `config.s3.endpointUrl` | Custom S3 endpoint URL (for MinIO) | `""` |
| `config.s3.customDomain` | CDN/proxy domain for S3 | `""` |
| `config.s3.querystringAuth` | Use signed URLs for S3 objects | `true` |
| `config.s3.regionName` | S3 region | `""` |
| `config.s3.endpointUrl` | Custom S3 endpoint (MinIO) | `""` |
| `config.s3.customDomain` | CDN/proxy domain | `""` |
| `config.s3.querystringAuth` | Use signed URLs | `true` |
| `config.s3.querystringExpire` | Signed URL expiration (seconds) | `3600` |
| `config.s3.existingSecret` | Existing secret for S3 credentials | `""` |
| `config.s3.existingSecret` | Existing secret for S3 | `""` |
### AI Features
### Social Authentication
| Name | Description | Value |
|------|-------------|-------|
| Name | Description | Default |
|------|-------------|---------|
| `config.socialDefaultAccess` | Space ID for auto-join | `0` |
| `config.socialDefaultGroup` | Default group (`guest`/`user`/`admin`) | `guest` |
| `config.socialProviders` | OAuth providers (comma-separated) | `""` |
| `config.remoteUserAuth` | Enable REMOTE-USER header auth | `false` |
### AI Configuration
| Name | Description | Default |
|------|-------------|---------|
| `config.ai.enabled` | Enable AI features | `false` |
| `config.ai.creditsMonthly` | Monthly AI credits per space | `100` |
| `config.ai.rateLimit` | AI API rate limit | `60/hour` |
| `config.ai.creditsMonthly` | Monthly credits per space | `100` |
| `config.ai.rateLimit` | AI rate limit | `60/hour` |
### External Services
| Name | Description | Value |
|------|-------------|-------|
| Name | Description | Default |
|------|-------------|---------|
| `config.fdcApiKey` | Food Data Central API key | `DEMO_KEY` |
| `config.disableExternalConnectors` | Disable all external connectors | `false` |
| `config.disableExternalConnectors` | Disable external connectors | `false` |
| `config.externalConnectorsQueueSize` | External connectors queue size | `100` |
### Rate Limiting
| Name | Description | Value |
|------|-------------|-------|
| Name | Description | Default |
|------|-------------|---------|
| `config.ratelimitUrlImportRequests` | Rate limit for URL imports | `""` |
| `config.drfThrottleRecipeUrlImport` | DRF throttle for recipe URL import | `60/hour` |
### Space Defaults
### Space & User Defaults
| Name | Description | Value |
|------|-------------|-------|
| Name | Description | Default |
|------|-------------|---------|
| `config.spaceDefaultMaxRecipes` | Max recipes per space (0=unlimited) | `0` |
| `config.spaceDefaultMaxUsers` | Max users per space (0=unlimited) | `0` |
| `config.spaceDefaultMaxFiles` | Max file storage in MB (0=unlimited) | `0` |
| `config.spaceDefaultAllowSharing` | Allow public recipe sharing | `true` |
### User Preference Defaults
| Name | Description | Value |
|------|-------------|-------|
| `config.spaceDefaultAllowSharing` | Allow public sharing | `true` |
| `config.fractionPrefDefault` | Default fraction display | `false` |
| `config.commentPrefDefault` | Enable comments by default | `true` |
| `config.commentPrefDefault` | Comments enabled by default | `true` |
| `config.stickyNavPrefDefault` | Sticky navbar by default | `true` |
| `config.maxOwnedSpacesPrefDefault` | Max spaces per user | `100` |
### Cosmetic Configuration
### Performance & Cosmetic
| Name | Description | Value |
|------|-------------|-------|
| Name | Description | Default |
|------|-------------|---------|
| `config.shoppingMinAutosyncInterval` | Min auto-sync interval (minutes) | `5` |
| `config.exportFileCacheDuration` | Export cache duration (seconds) | `600` |
| `config.unauthenticatedThemeFromSpace` | Space ID for unauthenticated theme | `0` |
| `config.forceThemeFromSpace` | Space ID to enforce theme globally | `0` |
### Performance Configuration
| Name | Description | Value |
|------|-------------|-------|
| `config.shoppingMinAutosyncInterval` | Min auto-sync interval (minutes) | `5` |
| `config.exportFileCacheDuration` | Export cache duration (seconds) | `600` |
### Legal URLs
| Name | Description | Value |
|------|-------------|-------|
| Name | Description | Default |
|------|-------------|---------|
| `config.termsUrl` | Terms of service URL | `""` |
| `config.privacyUrl` | Privacy policy URL | `""` |
| `config.imprintUrl` | Legal imprint URL | `""` |
### hCaptcha Configuration
| Name | Description | Value |
|------|-------------|-------|
| Name | Description | Default |
|------|-------------|---------|
| `config.hcaptcha.siteKey` | hCaptcha site key | `""` |
| `config.hcaptcha.secret` | hCaptcha secret key | `""` |
| `config.hcaptcha.secret` | hCaptcha secret | `""` |
| `config.hcaptcha.existingSecret` | Existing secret for hCaptcha | `""` |
### Debugging
| Name | Description | Value |
|------|-------------|-------|
| `config.debug` | Enable Django debug mode | `false` |
| `config.debugToolbar` | Enable Django Debug Toolbar | `false` |
| `config.sqlDebug` | Enable SQL debug output | `false` |
| `config.logLevel` | Application log level | `WARNING` |
| `config.gunicornLogLevel` | Gunicorn log level | `info` |
### Service Parameters
| Name | Description | Value |
|------|-------------|-------|
| `service.type` | Kubernetes Service type | `ClusterIP` |
| `service.port` | Service HTTP port | `8080` |
### Ingress Parameters
| Name | Description | Value |
|------|-------------|-------|
| `ingress.enabled` | Enable ingress | `false` |
| `ingress.className` | Ingress class name | `""` |
| `ingress.annotations` | Ingress annotations | See values.yaml |
| `ingress.hosts` | Ingress hosts configuration | See values.yaml |
| `ingress.tls` | Ingress TLS configuration | See values.yaml |
### Persistence Parameters
| Name | Description | Value |
|------|-------------|-------|
| `persistence.staticfiles.enabled` | Enable static files persistence | `true` |
| `persistence.staticfiles.existingClaim` | Use existing PVC for static files | `""` |
| `persistence.staticfiles.storageClass` | Storage class for static files | `""` |
| `persistence.staticfiles.accessMode` | Access mode for static files PVC | `ReadWriteOnce` |
| `persistence.staticfiles.size` | Size of static files PVC | `1Gi` |
| `persistence.mediafiles.enabled` | Enable media files persistence | `true` |
| `persistence.mediafiles.existingClaim` | Use existing PVC for media files | `""` |
| `persistence.mediafiles.storageClass` | Storage class for media files | `""` |
| `persistence.mediafiles.accessMode` | Access mode for media files PVC | `ReadWriteOnce` |
| `persistence.mediafiles.size` | Size of media files PVC | `5Gi` |
| Name | Description | Default |
|------|-------------|---------|
| `persistence.staticfiles.enabled` | Enable static files PVC | `true` |
| `persistence.staticfiles.existingClaim` | Existing PVC for static files | `""` |
| `persistence.staticfiles.storageClass` | Storage class | `""` |
| `persistence.staticfiles.accessMode` | Access mode | `ReadWriteOnce` |
| `persistence.staticfiles.size` | PVC size | `1Gi` |
| `persistence.mediafiles.enabled` | Enable media files PVC | `true` |
| `persistence.mediafiles.existingClaim` | Existing PVC for media files | `""` |
| `persistence.mediafiles.storageClass` | Storage class | `""` |
| `persistence.mediafiles.accessMode` | Access mode | `ReadWriteOnce` |
| `persistence.mediafiles.size` | PVC size | `5Gi` |
### Pod Security Context
### Resource Parameters
| Name | Description | Value |
|------|-------------|-------|
| `podSecurityContext.runAsNonRoot` | Run as non-root user | `true` |
| `podSecurityContext.runAsUser` | User ID to run as | `1000` |
| `podSecurityContext.fsGroup` | Group ID for filesystem | `1000` |
| Name | Description | Default |
|------|-------------|---------|
| `resources` | Resource limits and requests | `{}` |
### Container Security Context
### Health Check Parameters
| Name | Description | Value |
|------|-------------|-------|
| `containerSecurityContext.allowPrivilegeEscalation` | Allow privilege escalation | `false` |
| `containerSecurityContext.readOnlyRootFilesystem` | Read-only root filesystem | `false` |
| Name | Description | Default |
|------|-------------|---------|
| `probes.liveness.enabled` | Enable liveness probe | `true` |
| `probes.liveness.path` | Liveness probe path | `/` |
| `probes.liveness.initialDelaySeconds` | Liveness initial delay | `30` |
| `probes.liveness.periodSeconds` | Liveness period | `10` |
| `probes.readiness.enabled` | Enable readiness probe | `true` |
| `probes.readiness.path` | Readiness probe path | `/` |
| `probes.readiness.initialDelaySeconds` | Readiness initial delay | `15` |
| `probes.readiness.periodSeconds` | Readiness period | `5` |
### Autoscaling Parameters
| Name | Description | Value |
|------|-------------|-------|
| `autoscaling.enabled` | Enable autoscaling | `false` |
| `autoscaling.minReplicas` | Minimum replicas | `1` |
| `autoscaling.maxReplicas` | Maximum replicas | `3` |
| `autoscaling.targetCPUUtilizationPercentage` | Target CPU utilization | `80` |
| `autoscaling.targetMemoryUtilizationPercentage` | Target memory utilization | `80` |
| Name | Description | Default |
|------|-------------|---------|
| `autoscaling.enabled` | Enable HPA | `false` |
| `autoscaling.minReplicas` | Min replicas | `1` |
| `autoscaling.maxReplicas` | Max replicas | `3` |
| `autoscaling.targetCPUUtilizationPercentage` | Target CPU | `80` |
| `autoscaling.targetMemoryUtilizationPercentage` | Target memory | `80` |
### Probes Configuration
### Debugging
| Name | Description | Value |
|------|-------------|-------|
| `probes.liveness.enabled` | Enable liveness probe | `true` |
| `probes.liveness.initialDelaySeconds` | Initial delay for liveness probe | `30` |
| `probes.liveness.periodSeconds` | Period for liveness probe | `10` |
| `probes.readiness.enabled` | Enable readiness probe | `true` |
| `probes.readiness.initialDelaySeconds` | Initial delay for readiness probe | `15` |
| `probes.readiness.periodSeconds` | Period for readiness probe | `5` |
| Name | Description | Default |
|------|-------------|---------|
| `config.debug` | Enable Django debug mode | `false` |
| `config.debugToolbar` | Enable Debug Toolbar | `false` |
| `config.sqlDebug` | Enable SQL debug | `false` |
| `config.logLevel` | Application log level | `WARNING` |
| `config.gunicornLogLevel` | Gunicorn log level | `info` |
### Additional Configuration
| Name | Description | Value |
|------|-------------|-------|
| `env` | Additional environment variables | `[]` |
| `extraEnvFrom` | Additional environment variables from secrets | `[]` |
| `extraVolumes` | Additional volumes | `[]` |
| `extraVolumeMounts` | Additional volume mounts | `[]` |
| `nodeSelector` | Node selector | `{}` |
| `tolerations` | Tolerations | `[]` |
| `affinity` | Affinity rules | `{}` |
| Name | Description | Default |
|------|-------------|---------|
| `env` | Extra environment variables | `[]` |
| `extraEnvFrom` | Extra env from secrets/configmaps | `[]` |
| `extraVolumes` | Extra volumes | `[]` |
| `extraVolumeMounts` | Extra volume mounts | `[]` |
## Uninstalling the Chart
## Troubleshooting
- **CSRF Errors**: Set `config.csrfTrustedOrigins` to your domain URL including the scheme
- **Login Issues**: Verify OIDC/LDAP configuration and callback URLs
- **Missing Media**: Check persistence configuration and S3 connectivity if enabled
```bash
helm uninstall tandoor
```
**Note:** PVCs are not automatically deleted. To remove them:
```bash
kubectl delete pvc -l app.kubernetes.io/name=tandoor
kubectl logs -f deployment/tandoor
kubectl describe pod -l app.kubernetes.io/name=tandoor
```
## Links
@ -425,3 +452,4 @@ kubectl delete pvc -l app.kubernetes.io/name=tandoor
- [Tandoor Recipes GitHub](https://github.com/TandoorRecipes/recipes)
- [Tandoor Documentation](https://docs.tandoor.dev/)
- [Configuration Reference](https://docs.tandoor.dev/system/configuration/)
- [Chart Source](https://github.com/rtomik/helm-charts/tree/main/charts/tandoor)