mirror of
https://github.com/rtomik/helm-charts.git
synced 2026-07-16 08:14:48 +00:00
388 lines
12 KiB
Markdown
388 lines
12 KiB
Markdown
# Joplin Server Helm Chart
|
|
|
|
A Helm chart for deploying [Joplin Server](https://github.com/laurent22/joplin) on Kubernetes.
|
|
|
|
## Introduction
|
|
|
|
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: 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)
|
|
- PV provisioner support (if using filesystem storage)
|
|
|
|
## Installing the Chart
|
|
|
|
```bash
|
|
helm repo add rtomik https://rtomik.github.io/helm-charts
|
|
helm install joplin-server rtomik/joplin-server
|
|
```
|
|
|
|
> **Important**: Configure PostgreSQL database settings before installation.
|
|
|
|
## Uninstalling the Chart
|
|
|
|
```bash
|
|
helm uninstall joplin-server
|
|
```
|
|
|
|
## Configuration Examples
|
|
|
|
### Minimal Installation
|
|
|
|
> **Important**: Health check probes require a `Host` header matching your ingress domain. Update `probes.*.httpHeaders` accordingly.
|
|
|
|
```yaml
|
|
postgresql:
|
|
external:
|
|
enabled: true
|
|
host: "postgresql.example.com"
|
|
port: 5432
|
|
database: "joplin"
|
|
user: "joplin"
|
|
password: "secure-password"
|
|
|
|
env:
|
|
APP_BASE_URL: "https://joplin.example.com"
|
|
|
|
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
|
|
|
|
ingress:
|
|
enabled: true
|
|
hosts:
|
|
- host: joplin.example.com
|
|
paths:
|
|
- path: /
|
|
pathType: Prefix
|
|
tls:
|
|
- hosts:
|
|
- joplin.example.com
|
|
secretName: joplin-tls
|
|
```
|
|
|
|
### Production with Existing Secrets
|
|
|
|
```yaml
|
|
postgresql:
|
|
external:
|
|
enabled: true
|
|
host: "postgres-cluster-pooler.dbs.svc.cluster.local"
|
|
port: 5432
|
|
database: "joplin-server"
|
|
existingSecret: "joplin-db-credentials"
|
|
userKey: "username"
|
|
passwordKey: "password"
|
|
|
|
joplin:
|
|
admin:
|
|
existingSecret: "joplin-admin-secret"
|
|
emailKey: "email"
|
|
passwordKey: "password"
|
|
```
|
|
|
|
### S3 Storage
|
|
|
|
```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
|
|
|
|
```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 Transcription)
|
|
|
|
```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
|
|
```
|
|
|
|
## Parameters
|
|
|
|
### Global Parameters
|
|
|
|
| Name | Description | Default |
|
|
|------|-------------|---------|
|
|
| `nameOverride` | Override the release name | `""` |
|
|
| `fullnameOverride` | Fully override the release name | `""` |
|
|
|
|
### Image Parameters
|
|
|
|
| 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
|
|
|
|
### Health Check Failures / "No Available Server"
|
|
|
|
Health checks require the correct `Host` header matching your ingress domain:
|
|
|
|
```yaml
|
|
probes:
|
|
liveness:
|
|
httpHeaders:
|
|
- name: Host
|
|
value: your-joplin-domain.com
|
|
readiness:
|
|
httpHeaders:
|
|
- name: Host
|
|
value: your-joplin-domain.com
|
|
```
|
|
|
|
### Database Connection Issues
|
|
|
|
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
|
|
kubectl describe pod -l app.kubernetes.io/name=joplin-server
|
|
```
|
|
|
|
## Links
|
|
|
|
- [Joplin GitHub](https://github.com/laurent22/joplin)
|
|
- [Chart Source](https://github.com/rtomik/helm-charts/tree/main/charts/joplin-server)
|