{{- /* Barcode settings, defended against a null / empty "barcodes:" override: an empty block falls back to the chart defaults (issue #61). */ -}} {{- $barcodes := fromYaml (include "paperless-ngx.barcodes" .Values.paperless.files.barcodes) -}} apiVersion: apps/v1 kind: StatefulSet metadata: name: {{ .Release.Name }}-{{ .Chart.Name }}-sfs labels: app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs spec: replicas: {{ include "paperless-ngx.replicas" (dict "scaleDown" .Values.scaleDown "replicas" .Values.paperless.replicas) }} serviceName: {{ .Release.Name }}-{{ .Chart.Name }}-service selector: matchLabels: app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs template: metadata: name: {{ .Release.Name }}-{{ .Chart.Name }}-sfs labels: app: {{ .Release.Name }}-{{ .Chart.Name }}-sfs spec: automountServiceAccountToken: false securityContext: {{- include "paperless-ngx.podSecurityContext" .Values.paperless.securityContext | nindent 8 }} initContainers: # s6-overlay refuses to start unless /run is owned by the runtime uid, # but emptyDirs are always created root-owned. This minimal init # container hands /run over to the paperless uid (see # paperless.securityContext in values.yaml) so the main container can # run fully hardened (non-root, no privilege escalation). - name: init-run-ownership image: "{{ .Values.paperless.busybox.image.repository }}:{{ .Values.paperless.busybox.image.tag }}" imagePullPolicy: IfNotPresent command: - sh - -c {{- $podCtx := fromYaml (include "paperless-ngx.podSecurityContext" .Values.paperless.securityContext) }} - chown {{ $podCtx.runAsUser }}:{{ $podCtx.runAsGroup }} /run securityContext: runAsNonRoot: false runAsUser: 0 allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: - ALL add: - CHOWN volumeMounts: - mountPath: /run name: run resources: requests: cpu: 10m memory: 16Mi ephemeral-storage: 1Mi limits: cpu: 100m memory: 32Mi ephemeral-storage: 10Mi containers: - name: {{ .Release.Name }}-{{ .Chart.Name }}-sfs image: "ghcr.io/paperless-ngx/paperless-ngx:{{ .Chart.AppVersion }}" imagePullPolicy: IfNotPresent securityContext: {{- include "paperless-ngx.containerSecurityContext" .Values.paperless.securityContext | nindent 12 }} ports: - containerPort: 8000 protocol: TCP name: paperless-port env: - name: PAPERLESS_REDIS valueFrom: secretKeyRef: name: {{ .Release.Name }}-{{ .Chart.Name }}-redis-secret key: redis-connection-url - name: PAPERLESS_REDIS_PREFIX valueFrom: secretKeyRef: name: {{ .Release.Name }}-{{ .Chart.Name }}-redis-secret key: key-prefix - name: PAPERLESS_DBENGINE value: "postgresql" - name: PAPERLESS_DBHOST valueFrom: secretKeyRef: name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret key: server - name: PAPERLESS_DBPORT valueFrom: secretKeyRef: name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret key: port - name: PAPERLESS_DBNAME valueFrom: secretKeyRef: name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret key: database - name: PAPERLESS_DBUSER valueFrom: secretKeyRef: name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret key: username - name: PAPERLESS_DBPASS valueFrom: secretKeyRef: name: {{ .Release.Name }}-{{ .Chart.Name }}-db-secret key: password - name: PAPERLESS_ADMIN_USER valueFrom: secretKeyRef: name: {{ .Release.Name }}-{{ .Chart.Name }}-secret key: username - name: PAPERLESS_ADMIN_MAIL valueFrom: secretKeyRef: name: {{ .Release.Name }}-{{ .Chart.Name }}-secret key: username - name: PAPERLESS_ADMIN_PASSWORD valueFrom: secretKeyRef: name: {{ .Release.Name }}-{{ .Chart.Name }}-secret key: password - name: PAPERLESS_SECRET_KEY valueFrom: secretKeyRef: name: {{ .Release.Name }}-{{ .Chart.Name }}-secret key: paperless-secret-key - name: PAPERLESS_OCR_LANGUAGE value: "{{ .Values.paperless.ocr.languages }}" - name: PAPERLESS_OCR_MODE value: "{{ .Values.paperless.ocr.mode }}" - name: PAPERLESS_ARCHIVE_FILE_GENERATION value: "{{ .Values.paperless.ocr.archiveFileGeneration }}" - name: PAPERLESS_OCR_ROTATE_PAGES value: "{{ .Values.paperless.ocr.rotate_pages }}" - name: PAPERLESS_OCR_OUTPUT_TYPE value: "{{ .Values.paperless.ocr.output_type }}" - name: PAPERLESS_FILENAME_FORMAT value: "{{ .Values.paperless.files.filenameFormat }}" {{- /* ingress.domain is not a pure Ingress value: it is also PAPERLESS_URL, the ONLY origin this release declares to Django, so it stays required when ingress.enabled is false and in Gateway mode (#114). The required() is here and not only in the Ingress template because this is the read that cannot be switched off - an empty value would render "https://" and every login POST would be refused as a CSRF failure, with nothing in the log naming a chart value. A template comment and not a YAML one on purpose: a "#" line here is part of the rendered manifest, and this change has to leave `helm template` byte-identical for unchanged values. */}} - name: PAPERLESS_URL value: "https://{{ required "A Domain/Host must be provided (ingress.domain) - it is also PAPERLESS_URL, the only origin this release declares" .Values.ingress.domain }}" - name: PAPERLESS_CONSUMER_ENABLE_ASN_BARCODE value: "{{ $barcodes.asn.enabled }}" - name: PAPERLESS_CONSUMER_ENABLE_BARCODES value: "{{ $barcodes.enabled }}" - name: PAPERLESS_CONSUMER_BARCODE_STRING value: "{{ $barcodes.barcodeString }}" - name: PAPERLESS_CONSUMER_BARCODE_RETAIN_SPLIT_PAGES value: "{{ $barcodes.retainBarcodePage }}" - name: PAPERLESS_CONSUMER_DELETE_DUPLICATES value: "{{ .Values.paperless.files.deleteDuplicates }}" {{- if .Values.paperless.files.tikaGotenberg.enabled }} - name: PAPERLESS_TIKA_ENABLED value: "{{ .Values.paperless.files.tikaGotenberg.enabled }}" - name: PAPERLESS_TIKA_ENDPOINT value: "http://{{ required "If Tika is enabled, a Tika-Endpoint must be provided" .Values.paperless.files.tikaGotenberg.tikaEndpoint }}" - name: PAPERLESS_TIKA_GOTENBERG_ENDPOINT value: "http://{{ required "If Tika is enabled, a Gotenberg-Endpoint must be provided" .Values.paperless.files.tikaGotenberg.gotenbergEndpoint }}" {{- end }} {{- if .Values.oidc.enabled }} - name: PAPERLESS_APPS value: "allauth.socialaccount.providers.openid_connect" - name: PAPERLESS_SOCIALACCOUNT_PROVIDERS value: |- { "openid_connect": { "OAUTH_PKCE_ENABLED": true, "APPS": [ { "provider_id": "{{ required "A OIDC-provider-id must be provided" .Values.oidc.provider.id }}", "name": "{{ required "A OIDC-provider-name must be provided" .Values.oidc.provider.name }}", "client_id": "{{ required "A OIDC-client-id must be provided" .Values.oidc.provider.clientId }}", "settings": { "fetch_userinfo": true, "oauth_pkce_enabled": true, "server_url": "{{ required "A OIDC-discovery-document-url must be provided" .Values.oidc.provider.discoveryUrl }}", {{- if .Values.oidc.provider.tokenAuthMethod }} "token_auth_method": "{{ .Values.oidc.provider.tokenAuthMethod }}", {{- end }} "uid_field": "sub" } } ] } } - name: PAPERLESS_REDIRECT_LOGIN_TO_SSO value: "{{ .Values.oidc.autoLoginWithProvider }}" - name: PAPERLESS_DISABLE_REGULAR_LOGIN value: "{{ .Values.oidc.disableRegularLogin }}" {{- end }} {{- /* QUOTED SINCE 13.0.0, and it is a fix rather than a tidy-up (Gitea #112, same genus as #11). This block is the chart's additive env interface, and until 13.0.0 it rendered its operand bare. Two measured consequences, both of them the operator's value arriving as something else: - a value beginning with "*" aborted the render, with the error pointing at THIS template rather than at the value; - "true" and "0755" rendered as a YAML bool and an integer, and the API server rejects those with "cannot unmarshal number into Go struct field EnvVar.value of type string" - the chart renders, the install fails. The quoted form is the repository's documented one (template-chart/templates/xxx.deployment.yaml carries the rule); this chart was one of seven that had not been brought to it. */}} {{- range $value := .Values.paperless.env }} {{- if $value.value }} - name: {{ $value.name | quote }} value: {{ tpl $value.value $ | quote }} {{- else if $value.valueFrom }} {{- if $value.valueFrom.secretKeyRef }} - name: {{ $value.name }} valueFrom: secretKeyRef: name: {{ tpl $value.valueFrom.secretKeyRef.name $ | quote }} key: {{ tpl $value.valueFrom.secretKeyRef.key $ | quote }} {{- else if $value.valueFrom.configMapKeyRef }} - name: {{ $value.name }} valueFrom: configMapKeyRef: name: {{ tpl $value.valueFrom.configMapKeyRef.name $ | quote }} key: {{ tpl $value.valueFrom.configMapKeyRef.key $ | quote }} {{- end }} {{- end }} {{- end }} volumeMounts: - mountPath: "/usr/src/paperless/data" name: "paperless-volume" subPath: data/ - mountPath: "/usr/src/paperless/media" name: "paperless-volume" subPath: media/ - mountPath: "/usr/src/paperless/export" name: "paperless-volume" subPath: export/ - mountPath: "/usr/src/paperless/consume" name: "paperless-consume-volume" # Writable emptyDirs so the root filesystem can stay read-only: # s6-overlay needs /run, OCR/consumption uses /tmp for scratch files. - mountPath: /tmp name: tmp - mountPath: /run name: run resources: {{- include "paperless-ngx.effectiveResources" .Values.paperless.resources | nindent 12 }} readinessProbe: {{- include "paperless-ngx.readinessProbe" .Values.paperless.readinessProbe | nindent 12 }} livenessProbe: {{- include "paperless-ngx.livenessProbe" .Values.paperless.livenessProbe | nindent 12 }} startupProbe: {{- include "paperless-ngx.startupProbe" .Values.paperless.startupProbe | nindent 12 }} restartPolicy: Always volumes: - name: tmp emptyDir: {} - name: run emptyDir: {} - name: paperless-volume persistentVolumeClaim: claimName: {{ .Release.Name }}-{{ .Chart.Name }}-volume-claim - name: paperless-consume-volume persistentVolumeClaim: claimName: {{ .Release.Name }}-{{ .Chart.Name }}-consume-volume-claim