From e809d6067de250076807978b7bf1c43ae2990faf Mon Sep 17 00:00:00 2001 From: Richard Tomik Date: Sun, 24 Aug 2025 21:47:31 +0200 Subject: [PATCH] joplin-server helmchart v0.0.1 --- charts/joplin-server/Chart.yaml | 17 + charts/joplin-server/NOTES.txt | 106 +++++ charts/joplin-server/readme.md | 445 ++++++++++++++++++ charts/joplin-server/templates/_helpers.tpl | 45 ++ .../joplin-server/templates/deployment.yaml | 377 +++++++++++++++ charts/joplin-server/templates/ingress.yaml | 43 ++ charts/joplin-server/templates/pvc.yaml | 44 ++ charts/joplin-server/templates/service.yaml | 34 ++ charts/joplin-server/values.yaml | 267 +++++++++++ values.yaml | 57 --- 10 files changed, 1378 insertions(+), 57 deletions(-) create mode 100644 charts/joplin-server/Chart.yaml create mode 100644 charts/joplin-server/NOTES.txt create mode 100644 charts/joplin-server/readme.md create mode 100644 charts/joplin-server/templates/_helpers.tpl create mode 100644 charts/joplin-server/templates/deployment.yaml create mode 100644 charts/joplin-server/templates/ingress.yaml create mode 100644 charts/joplin-server/templates/pvc.yaml create mode 100644 charts/joplin-server/templates/service.yaml create mode 100644 charts/joplin-server/values.yaml delete mode 100644 values.yaml diff --git a/charts/joplin-server/Chart.yaml b/charts/joplin-server/Chart.yaml new file mode 100644 index 0000000..63a4353 --- /dev/null +++ b/charts/joplin-server/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +name: joplin-server +description: Joplin Server helm chart for Kubernetes - Note-taking and synchronization server +type: application +version: 0.0.1 +appVersion: "3.4.2" +maintainers: + - name: Richard Tomik + email: no@m.com +keywords: + - notes + - synchronization + - joplin + - productivity +home: https://github.com/rtomik/helm-charts +sources: + - https://github.com/laurent22/joplin \ No newline at end of file diff --git a/charts/joplin-server/NOTES.txt b/charts/joplin-server/NOTES.txt new file mode 100644 index 0000000..80affb9 --- /dev/null +++ b/charts/joplin-server/NOTES.txt @@ -0,0 +1,106 @@ +1. Get the application 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[0].nodePort}" services {{ include "joplin-server.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 "joplin-server.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "joplin-server.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "joplin-server.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} + +2. Joplin Server is configured with: + - Database: PostgreSQL ({{ .Values.postgresql.external.host }}:{{ .Values.postgresql.external.port }}/{{ .Values.postgresql.external.database }}) + - Storage: {{ .Values.joplin.storage.driver }} + {{- if eq .Values.joplin.storage.driver "filesystem" }} + - Data path: {{ .Values.joplin.storage.filesystemPath }} + {{- end }} + - User registration: {{ if .Values.joplin.server.enableUserRegistration }}enabled{{ else }}disabled{{ end }} + - Sharing: {{ if .Values.joplin.server.enableSharing }}enabled{{ else }}disabled{{ end }} + +{{- if and (eq .Values.joplin.storage.driver "filesystem") .Values.persistence.enabled }} +3. Data is persisted using PVC: {{ include "joplin-server.fullname" . }}-data +{{- else if eq .Values.joplin.storage.driver "filesystem" }} +3. WARNING: No persistence enabled. Data will be lost when pods are restarted. +{{- else }} +3. Using {{ .Values.joplin.storage.driver }} storage - no local persistence needed. +{{- end }} + +{{- if .Values.transcribe.enabled }} +4. AI Transcription service is enabled: + - Transcribe service accessible internally at: {{ include "joplin-server.fullname" . }}-transcribe:{{ .Values.transcribe.service.port }} + {{- if .Values.transcribe.persistence.enabled }} + - Transcribe images stored in PVC: {{ include "joplin-server.fullname" . }}-transcribe-images + {{- end }} +{{- end }} + +{{- if .Values.joplin.email.enabled }} +5. Email notifications configured: + - SMTP host: {{ .Values.joplin.email.host }}:{{ .Values.joplin.email.port }} + - From: {{ .Values.joplin.email.fromName }} <{{ .Values.joplin.email.fromEmail }}> +{{- end }} + +{{- if not .Values.joplin.admin.email }} + +6. IMPORTANT: First-time setup required! + After accessing the application, you'll need to create your first admin user. + Consider setting joplin.admin.email and joplin.admin.password for automated setup. +{{- else }} + +6. Admin user configured: + - Email: {{ .Values.joplin.admin.email }} + {{- if .Values.joplin.admin.existingSecret }} + - Password: Retrieved from secret {{ .Values.joplin.admin.existingSecret }} + {{- else }} + - Password: Set in values (consider using existingSecret for production) + {{- end }} +{{- end }} + +{{- if not .Values.postgresql.external.enabled }} + +7. WARNING: PostgreSQL database not configured! + Please configure postgresql.external settings to connect to your PostgreSQL database. + Joplin Server requires a PostgreSQL database to function. +{{- else if and .Values.postgresql.external.enabled (not .Values.postgresql.external.existingSecret) }} + +7. SECURITY NOTE: Database credentials in plain text. + For production use, consider using postgresql.external.existingSecret to store database credentials securely. +{{- end }} + +{{- $defaultHost := "joplin.domain.com" }} +{{- $actualHost := "" }} +{{- if .Values.ingress.enabled }} +{{- range .Values.ingress.hosts }} +{{- $actualHost = .host }} +{{- end }} +{{- end }} +{{- if and $actualHost (ne $actualHost $defaultHost) }} +{{- if and .Values.probes.liveness.httpHeaders (len .Values.probes.liveness.httpHeaders) }} +{{- $probeHost := "" }} +{{- range .Values.probes.liveness.httpHeaders }} +{{- if eq .name "Host" }} +{{- $probeHost = .value }} +{{- end }} +{{- end }} +{{- if eq $probeHost $defaultHost }} + +8. IMPORTANT: Health check configuration needs updating! + Your ingress host is "{{ $actualHost }}" but health checks are configured for "{{ $defaultHost }}". + Update probes.*.httpHeaders.Host value to match your domain for proper health checks. +{{- end }} +{{- end }} +{{- end }} + +For more information about using this Helm chart, please refer to the readme.md file. \ No newline at end of file diff --git a/charts/joplin-server/readme.md b/charts/joplin-server/readme.md new file mode 100644 index 0000000..aaf9751 --- /dev/null +++ b/charts/joplin-server/readme.md @@ -0,0 +1,445 @@ +# Joplin Server Helm Chart + +A Helm chart for deploying Joplin Server on Kubernetes - Note-taking and synchronization server. + +## 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. + +Source code can be found here: +- 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) + +## 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 +``` + +> **Important**: You must configure PostgreSQL database settings before installation. + +## Uninstalling the Chart + +To uninstall/delete the `joplin-server` deployment: + +```bash +$ 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 | `host` | +| `postgresql.external.portKey` | Key in the secret for port | `port` | +| `postgresql.external.databaseKey` | Key in the secret for database name | `database` | + +### 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 + +```yaml +postgresql: + external: + enabled: true + host: "postgresql.example.com" + port: 5432 + database: "joplin" + 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: + - name: Host + value: joplin.example.com + readiness: + httpHeaders: + - name: Host + value: joplin.example.com + +joplin: + admin: + email: "admin@example.com" + password: "admin-password" + server: + enableUserRegistration: true +``` + +### Using Kubernetes Secrets + +```yaml +postgresql: + external: + 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" +``` + +### S3 Storage Configuration + +```yaml +joplin: + storage: + driver: "s3" + s3: + bucket: "joplin-notes" + region: "us-east-1" + existingSecret: "joplin-s3-secret" + accessKeyIdKey: "access-key-id" + secretAccessKeyKey: "secret-access-key" + +# No persistence needed when using S3 +persistence: + enabled: false +``` + +### Email Notifications Setup + +```yaml +joplin: + email: + enabled: true + host: "smtp.example.com" + port: 587 + fromEmail: "joplin@example.com" + fromName: "Joplin Server" + secure: true + existingSecret: "joplin-email-secret" + usernameKey: "username" + passwordKey: "password" +``` + +### Transcribe Service (AI Features) + +```yaml +transcribe: + enabled: true + api: + existingSecret: "joplin-transcribe-secret" + keyName: "api-key" + database: + host: "postgresql.example.com" + port: 5432 + database: "transcribe" + user: "transcribe" + existingSecret: "transcribe-db-secret" + userKey: "username" + passwordKey: "password" + persistence: + enabled: true + size: 5Gi +``` + +## First-time Setup + +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) + +## Security Considerations + +For production deployments: + +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 + +## Troubleshooting + +Common issues and solutions: + +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: + httpHeaders: + - name: Host + value: your-joplin-domain.com + readiness: + httpHeaders: + - name: Host + 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 + +For detailed troubleshooting, check the application logs: + +```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 + +- **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 \ No newline at end of file diff --git a/charts/joplin-server/templates/_helpers.tpl b/charts/joplin-server/templates/_helpers.tpl new file mode 100644 index 0000000..2e71a81 --- /dev/null +++ b/charts/joplin-server/templates/_helpers.tpl @@ -0,0 +1,45 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "joplin-server.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "joplin-server.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 "joplin-server.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "joplin-server.labels" -}} +helm.sh/chart: {{ include "joplin-server.chart" . }} +{{ include "joplin-server.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "joplin-server.selectorLabels" -}} +app.kubernetes.io/name: {{ include "joplin-server.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} \ No newline at end of file diff --git a/charts/joplin-server/templates/deployment.yaml b/charts/joplin-server/templates/deployment.yaml new file mode 100644 index 0000000..12e8d6c --- /dev/null +++ b/charts/joplin-server/templates/deployment.yaml @@ -0,0 +1,377 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "joplin-server.fullname" . }} + labels: + {{- include "joplin-server.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + revisionHistoryLimit: {{ .Values.revisionHistoryLimit }} + selector: + matchLabels: + {{- include "joplin-server.selectorLabels" . | nindent 6 }} + strategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 1 + maxSurge: 1 + template: + metadata: + labels: + {{- include "joplin-server.selectorLabels" . | nindent 8 }} + annotations: + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.podSecurityContext | nindent 8 }} + 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: http + containerPort: 22300 + protocol: TCP + {{- if .Values.probes.liveness.enabled }} + livenessProbe: + httpGet: + path: {{ .Values.probes.liveness.path }} + port: http + {{- if .Values.probes.liveness.httpHeaders }} + httpHeaders: + {{- toYaml .Values.probes.liveness.httpHeaders | nindent 16 }} + {{- end }} + 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: + httpGet: + path: {{ .Values.probes.readiness.path }} + port: http + {{- if .Values.probes.readiness.httpHeaders }} + httpHeaders: + {{- toYaml .Values.probes.readiness.httpHeaders | nindent 16 }} + {{- end }} + 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 }} + env: + {{- range $key, $value := .Values.env }} + - name: {{ $key }} + value: {{ $value | quote }} + {{- end }} + {{- if .Values.postgresql.external.enabled }} + - name: POSTGRES_HOST + {{- if .Values.postgresql.external.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.postgresql.external.existingSecret }} + key: {{ .Values.postgresql.external.hostKey }} + {{- else }} + value: {{ .Values.postgresql.external.host | quote }} + {{- end }} + - name: POSTGRES_PORT + {{- if .Values.postgresql.external.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.postgresql.external.existingSecret }} + key: {{ .Values.postgresql.external.portKey }} + {{- else }} + value: {{ .Values.postgresql.external.port | quote }} + {{- end }} + - name: POSTGRES_DATABASE + {{- if .Values.postgresql.external.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.postgresql.external.existingSecret }} + key: {{ .Values.postgresql.external.databaseKey }} + {{- else }} + value: {{ .Values.postgresql.external.database | quote }} + {{- end }} + - name: POSTGRES_USER + {{- if .Values.postgresql.external.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.postgresql.external.existingSecret }} + key: {{ .Values.postgresql.external.userKey }} + {{- else }} + value: {{ .Values.postgresql.external.user | quote }} + {{- end }} + - name: POSTGRES_PASSWORD + {{- if .Values.postgresql.external.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.postgresql.external.existingSecret }} + key: {{ .Values.postgresql.external.passwordKey }} + {{- else }} + value: {{ .Values.postgresql.external.password | quote }} + {{- end }} + {{- end }} + {{- if .Values.joplin.admin.email }} + - name: ADMIN_EMAIL + {{- if .Values.joplin.admin.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.joplin.admin.existingSecret }} + key: {{ .Values.joplin.admin.emailKey }} + {{- else }} + value: {{ .Values.joplin.admin.email | quote }} + {{- end }} + {{- end }} + {{- if .Values.joplin.admin.password }} + - name: ADMIN_PASSWORD + {{- if .Values.joplin.admin.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.joplin.admin.existingSecret }} + key: {{ .Values.joplin.admin.passwordKey }} + {{- else }} + value: {{ .Values.joplin.admin.password | quote }} + {{- end }} + {{- end }} + {{- if .Values.joplin.server.maxRequestBodySize }} + - name: MAX_REQUEST_BODY_SIZE + value: {{ .Values.joplin.server.maxRequestBodySize | quote }} + {{- end }} + {{- if .Values.joplin.server.sessionTimeout }} + - name: SESSION_TIMEOUT + value: {{ .Values.joplin.server.sessionTimeout | quote }} + {{- end }} + - name: ENABLE_USER_REGISTRATION + value: {{ .Values.joplin.server.enableUserRegistration | quote }} + - name: ENABLE_SHARING + value: {{ .Values.joplin.server.enableSharing | quote }} + - name: ENABLE_PUBLIC_NOTES + value: {{ .Values.joplin.server.enablePublicNotes | quote }} + {{- if eq .Values.joplin.storage.driver "filesystem" }} + - name: STORAGE_CONNECTION_STRING + value: "Type=Filesystem; Path={{ .Values.joplin.storage.filesystemPath }}" + {{- else if eq .Values.joplin.storage.driver "s3" }} + - name: STORAGE_CONNECTION_STRING + value: "Type=S3; Bucket={{ .Values.joplin.storage.s3.bucket }}; Region={{ .Values.joplin.storage.s3.region }}{{- if .Values.joplin.storage.s3.endpoint }}; Endpoint={{ .Values.joplin.storage.s3.endpoint }}{{- end }}" + {{- else }} + - name: STORAGE_CONNECTION_STRING + value: "Type=Database" + {{- end }} + {{- if and (eq .Values.joplin.storage.driver "s3") .Values.joplin.storage.s3.bucket }} + - name: AWS_ACCESS_KEY_ID + {{- if .Values.joplin.storage.s3.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.joplin.storage.s3.existingSecret }} + key: {{ .Values.joplin.storage.s3.accessKeyIdKey }} + {{- else }} + value: {{ .Values.joplin.storage.s3.accessKeyId | quote }} + {{- end }} + - name: AWS_SECRET_ACCESS_KEY + {{- if .Values.joplin.storage.s3.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.joplin.storage.s3.existingSecret }} + key: {{ .Values.joplin.storage.s3.secretAccessKeyKey }} + {{- else }} + value: {{ .Values.joplin.storage.s3.secretAccessKey | quote }} + {{- end }} + {{- end }} + {{- if .Values.joplin.email.enabled }} + - name: SMTP_HOST + value: {{ .Values.joplin.email.host | quote }} + - name: SMTP_PORT + value: {{ .Values.joplin.email.port | quote }} + - name: SMTP_USERNAME + {{- if .Values.joplin.email.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.joplin.email.existingSecret }} + key: {{ .Values.joplin.email.usernameKey }} + {{- else }} + value: {{ .Values.joplin.email.username | quote }} + {{- end }} + - name: SMTP_PASSWORD + {{- if .Values.joplin.email.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.joplin.email.existingSecret }} + key: {{ .Values.joplin.email.passwordKey }} + {{- else }} + value: {{ .Values.joplin.email.password | quote }} + {{- end }} + - name: SMTP_FROM_EMAIL + value: {{ .Values.joplin.email.fromEmail | quote }} + - name: SMTP_FROM_NAME + value: {{ .Values.joplin.email.fromName | quote }} + - name: SMTP_SECURE + value: {{ .Values.joplin.email.secure | quote }} + {{- end }} + {{- if .Values.transcribe.enabled }} + - name: TRANSCRIBE_ENABLED + value: "true" + - name: TRANSCRIBE_API_KEY + {{- if .Values.transcribe.api.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.transcribe.api.existingSecret }} + key: {{ .Values.transcribe.api.keyName }} + {{- else }} + value: {{ .Values.transcribe.api.key | quote }} + {{- end }} + - name: TRANSCRIBE_BASE_URL + value: "http://{{ include "joplin-server.fullname" . }}-transcribe:{{ .Values.transcribe.service.port }}" + {{- end }} + - name: LOG_LEVEL + value: {{ .Values.joplin.logging.level | quote }} + - name: LOG_TARGET + value: {{ .Values.joplin.logging.target | quote }} + {{- with .Values.extraEnv }} + {{- toYaml . | nindent 12 }} + {{- end }} + volumeMounts: + {{- if eq .Values.joplin.storage.driver "filesystem" }} + - name: data + mountPath: {{ .Values.joplin.storage.filesystemPath }} + {{- end }} + {{- if and .Values.security.tls.enabled .Values.security.tls.existingSecret }} + - name: tls-certs + mountPath: /app/certs + readOnly: true + {{- end }} + {{- with .Values.extraVolumeMounts }} + {{- toYaml . | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- if .Values.transcribe.enabled }} + - name: transcribe + image: "{{ .Values.transcribe.image.repository }}:{{ .Values.transcribe.image.tag }}" + imagePullPolicy: {{ .Values.transcribe.image.pullPolicy }} + ports: + - name: transcribe + containerPort: 4567 + protocol: TCP + env: + - name: APP_PORT + value: "4567" + - name: DB_CLIENT + value: "pg" + - name: API_KEY + {{- if .Values.transcribe.api.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.transcribe.api.existingSecret }} + key: {{ .Values.transcribe.api.keyName }} + {{- else }} + value: {{ .Values.transcribe.api.key | quote }} + {{- end }} + - name: HTR_CLI_IMAGES_FOLDER + value: {{ .Values.transcribe.htr.imagesFolder | quote }} + {{- if .Values.transcribe.database.host }} + - name: QUEUE_DATABASE_HOST + {{- if .Values.transcribe.database.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.transcribe.database.existingSecret }} + key: {{ .Values.transcribe.database.hostKey }} + {{- else }} + value: {{ .Values.transcribe.database.host | quote }} + {{- end }} + - name: QUEUE_DATABASE_PORT + {{- if .Values.transcribe.database.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.transcribe.database.existingSecret }} + key: {{ .Values.transcribe.database.portKey }} + {{- else }} + value: {{ .Values.transcribe.database.port | quote }} + {{- end }} + - name: QUEUE_DATABASE_NAME + {{- if .Values.transcribe.database.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.transcribe.database.existingSecret }} + key: {{ .Values.transcribe.database.databaseKey }} + {{- else }} + value: {{ .Values.transcribe.database.database | quote }} + {{- end }} + - name: QUEUE_DATABASE_USER + {{- if .Values.transcribe.database.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.transcribe.database.existingSecret }} + key: {{ .Values.transcribe.database.userKey }} + {{- else }} + value: {{ .Values.transcribe.database.user | quote }} + {{- end }} + - name: QUEUE_DATABASE_PASSWORD + {{- if .Values.transcribe.database.existingSecret }} + valueFrom: + secretKeyRef: + name: {{ .Values.transcribe.database.existingSecret }} + key: {{ .Values.transcribe.database.passwordKey }} + {{- else }} + value: {{ .Values.transcribe.database.password | quote }} + {{- end }} + {{- end }} + volumeMounts: + - name: transcribe-images + mountPath: {{ .Values.transcribe.htr.imagesFolder }} + - name: docker-socket + mountPath: /var/run/docker.sock + {{- end }} + volumes: + {{- if eq .Values.joplin.storage.driver "filesystem" }} + - name: data + {{- if .Values.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ include "joplin-server.fullname" . }}-data + {{- else }} + emptyDir: {} + {{- end }} + {{- end }} + {{- if .Values.transcribe.enabled }} + - name: transcribe-images + {{- if .Values.transcribe.persistence.enabled }} + persistentVolumeClaim: + claimName: {{ include "joplin-server.fullname" . }}-transcribe-images + {{- else }} + emptyDir: {} + {{- end }} + - name: docker-socket + hostPath: + path: /var/run/docker.sock + type: Socket + {{- end }} + {{- if and .Values.security.tls.enabled .Values.security.tls.existingSecret }} + - name: tls-certs + secret: + secretName: {{ .Values.security.tls.existingSecret }} + {{- 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 }} \ No newline at end of file diff --git a/charts/joplin-server/templates/ingress.yaml b/charts/joplin-server/templates/ingress.yaml new file mode 100644 index 0000000..1747444 --- /dev/null +++ b/charts/joplin-server/templates/ingress.yaml @@ -0,0 +1,43 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "joplin-server.fullname" . }} + labels: + {{- include "joplin-server.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 "joplin-server.fullname" $ }} + port: + number: {{ $.Values.service.port }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/joplin-server/templates/pvc.yaml b/charts/joplin-server/templates/pvc.yaml new file mode 100644 index 0000000..8253247 --- /dev/null +++ b/charts/joplin-server/templates/pvc.yaml @@ -0,0 +1,44 @@ +{{- if and (eq .Values.joplin.storage.driver "filesystem") .Values.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "joplin-server.fullname" . }}-data + labels: + {{- include "joplin-server.labels" . | nindent 4 }} + {{- with .Values.persistence.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + accessModes: + - {{ .Values.persistence.accessMode | quote }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} +{{- end }} +--- +{{- if and .Values.transcribe.enabled .Values.transcribe.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "joplin-server.fullname" . }}-transcribe-images + labels: + {{- include "joplin-server.labels" . | nindent 4 }} + app.kubernetes.io/component: transcribe + {{- with .Values.transcribe.persistence.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + accessModes: + - {{ .Values.transcribe.persistence.accessMode | quote }} + {{- if .Values.transcribe.persistence.storageClass }} + storageClassName: {{ .Values.transcribe.persistence.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.transcribe.persistence.size | quote }} +{{- end }} \ No newline at end of file diff --git a/charts/joplin-server/templates/service.yaml b/charts/joplin-server/templates/service.yaml new file mode 100644 index 0000000..65d9fdd --- /dev/null +++ b/charts/joplin-server/templates/service.yaml @@ -0,0 +1,34 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "joplin-server.fullname" . }} + labels: + {{- include "joplin-server.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "joplin-server.selectorLabels" . | nindent 4 }} +--- +{{- if .Values.transcribe.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "joplin-server.fullname" . }}-transcribe + labels: + {{- include "joplin-server.labels" . | nindent 4 }} + app.kubernetes.io/component: transcribe +spec: + type: {{ .Values.transcribe.service.type }} + ports: + - port: {{ .Values.transcribe.service.port }} + targetPort: transcribe + protocol: TCP + name: transcribe + selector: + {{- include "joplin-server.selectorLabels" . | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/charts/joplin-server/values.yaml b/charts/joplin-server/values.yaml new file mode 100644 index 0000000..665ec95 --- /dev/null +++ b/charts/joplin-server/values.yaml @@ -0,0 +1,267 @@ +## Global settings +nameOverride: "" +fullnameOverride: "" + +## Image settings +image: + repository: joplin/server + tag: "3.4.2" + pullPolicy: IfNotPresent + +## Deployment settings +replicaCount: 1 +revisionHistoryLimit: 3 + +# Pod security settings +podSecurityContext: + runAsNonRoot: true + runAsUser: 1001 + fsGroup: 1001 + +containerSecurityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: false + capabilities: + drop: + - ALL + +## Pod scheduling +nodeSelector: {} +tolerations: [] +affinity: {} + +## Service settings +service: + type: ClusterIP + port: 22300 + +## Ingress settings +ingress: + enabled: false + className: "" + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: websecure + hosts: + - host: joplin.domain.com + paths: + - path: / + pathType: Prefix + tls: + - hosts: + - joplin.domain.com + +## Resource limits and requests +# resources: +# limits: +# cpu: 500m +# memory: 512Mi +# requests: +# cpu: 100m +# memory: 256Mi + +## Application health checks +probes: + liveness: + enabled: true + initialDelaySeconds: 60 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + successThreshold: 1 + path: /api/ping + # Host header for health checks to bypass origin validation + # Update this to match your actual domain + httpHeaders: + - name: Host + value: joplin.domain.com + readiness: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + successThreshold: 1 + path: /api/ping + # Host header for health checks to bypass origin validation + # Update this to match your actual domain + httpHeaders: + - name: Host + value: joplin.domain.com + +## Autoscaling configuration +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 3 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + +## Environment variables +env: + # Application Settings + APP_PORT: "22300" + APP_BASE_URL: "http://localhost:22300" + + # Database Settings (PostgreSQL required) + DB_CLIENT: "pg" + +# Extra environment variables (for advanced use cases) +extraEnv: [] + +# Extra volume mounts +extraVolumeMounts: [] + +# Extra volumes +extraVolumes: [] + +## PostgreSQL configuration (External database required) +postgresql: + # External PostgreSQL settings (required) + external: + enabled: false + host: "" + port: 5432 + database: "joplin" + user: "joplin" + password: "" + # Use existing secret for database credentials + existingSecret: "" + userKey: "username" + passwordKey: "password" + hostKey: "host" + portKey: "port" + databaseKey: "database" + +## Joplin Server Configuration +joplin: + # Admin settings + admin: + # First admin user email (set during first setup) + email: "" + # First admin user password (set during first setup) + password: "" + # Use existing secret for admin credentials + existingSecret: "" + emailKey: "admin-email" + passwordKey: "admin-password" + + # Server settings + server: + # Maximum request body size (in bytes) + maxRequestBodySize: "200mb" + # Session timeout in seconds + sessionTimeout: 86400 + # Enable/disable user registration + enableUserRegistration: false + # Enable/disable sharing + enableSharing: true + # Enable/disable public notes + enablePublicNotes: true + + # Storage settings + storage: + # Storage driver: database, filesystem, s3, or azure + driver: "database" + # For filesystem storage (requires persistence) + filesystemPath: "/var/lib/joplin" + # For S3 storage (optional) + s3: + bucket: "" + region: "" + accessKeyId: "" + secretAccessKey: "" + endpoint: "" + # Use existing secret for S3 credentials + existingSecret: "" + accessKeyIdKey: "access-key-id" + secretAccessKeyKey: "secret-access-key" + + # Email settings (for user registration and notifications) + email: + enabled: false + host: "" + port: 587 + username: "" + password: "" + fromEmail: "" + fromName: "Joplin Server" + # Use TLS/SSL + secure: true + # Use existing secret for email credentials + existingSecret: "" + usernameKey: "email-username" + passwordKey: "email-password" + + # Logging settings + logging: + level: "info" # error, warn, info, debug + target: "console" # console, file + +## Persistence settings (for filesystem storage) +persistence: + enabled: false + storageClass: "" + accessMode: ReadWriteOnce + size: 3Gi + annotations: {} + +## Transcribe service (optional AI transcription) +transcribe: + enabled: false + image: + repository: joplin/transcribe + tag: "latest" + pullPolicy: IfNotPresent + + # Transcribe API settings + api: + # Shared secret between Joplin Server and Transcribe service + key: "" + # Use existing secret for transcribe API key + existingSecret: "" + keyName: "transcribe-api-key" + + # Transcribe service settings + service: + type: ClusterIP + port: 4567 + + # HTR CLI settings + htr: + # Images folder path + imagesFolder: "/app/images" + + # Transcribe persistence (for image storage) + persistence: + enabled: false + storageClass: "" + accessMode: ReadWriteOnce + size: 5Gi + annotations: {} + + # Transcribe database (separate from main Joplin database) + database: + host: "" + port: 5432 + database: "transcribe" + user: "transcribe" + password: "" + # Use existing secret for transcribe database credentials + existingSecret: "" + userKey: "username" + passwordKey: "password" + hostKey: "host" + portKey: "port" + databaseKey: "database" + +## Security settings +security: + # Enable/disable HTTPS redirect + httpsRedirect: false + # Custom TLS certificate + tls: + enabled: false + # Use existing secret for TLS certificate + existingSecret: "" + certificateKey: "tls.crt" + privateKeyKey: "tls.key" \ No newline at end of file diff --git a/values.yaml b/values.yaml deleted file mode 100644 index d607763..0000000 --- a/values.yaml +++ /dev/null @@ -1,57 +0,0 @@ -ingress: - enabled: true - className: "traefik" - annotations: - traefik.ingress.kubernetes.io/router.entrypoints: websecure - hosts: - - host: mealie.tomik.lat - paths: - - path: / - pathType: Prefix - tls: - - hosts: - - mealie.tomik.lat - -persistence: - enabled: true - storageClass: "longhorn" - accessMode: ReadWriteOnce - size: 3Gi - -postgresql: - enabled: true - # External PostgreSQL settings - external: - enabled: true - host: "postgres-cluster-pooler.dbs.svc.cluster.local" - port: 5432 - database: "mealie" - user: "mealie_user" - password: "7OemzeEtwYF1y7FyqRi6" - -## Environment variables -env: - # General Settings - PUID: "911" - PGID: "911" - DEFAULT_GROUP: "Home" - DEFAULT_HOUSEHOLD: "Family" - BASE_URL: "http://localhost:9000" - TOKEN_TIME: "48" - API_PORT: "9000" - API_DOCS: "true" - TZ: "UTC" - ALLOW_SIGNUP: "false" - ALLOW_PASSWORD_LOGIN: "true" - LOG_LEVEL: "info" - DAILY_SCHEDULE_TIME: "23:45" - - # Security - SECURITY_MAX_LOGIN_ATTEMPTS: "5" - SECURITY_USER_LOCKOUT_TIME: "24" - - # Database - DB_ENGINE: "postgres" - - # Webworker - UVICORN_WORKERS: "1" \ No newline at end of file