Joplin Server Helm Chart
A Helm chart for deploying Joplin Server on Kubernetes - Note-taking and synchronization server.
Introduction
This chart deploys Joplin Server 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:
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:
Uninstalling the Chart
To uninstall/delete the joplin-server deployment:
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
Using Kubernetes Secrets
S3 Storage Configuration
Email Notifications Setup
Transcribe Service (AI Features)
First-time Setup
- Configure PostgreSQL: Ensure your PostgreSQL database is accessible and credentials are configured
- Admin User: Set admin email/password or access the web interface to create the first admin user
- User Registration: Configure whether users can self-register or admin approval is required
- Storage: Choose between filesystem (requires persistence) or cloud storage (S3/Azure)
Security Considerations
For production deployments:
- Use external secrets for all sensitive information (database passwords, admin credentials, etc.)
- Enable TLS/SSL for all communications
- Configure proper RBAC and network policies
- Use dedicated databases with proper access controls
- Disable user registration if not needed
- Use cloud storage for better scalability and backup
Troubleshooting
Common issues and solutions:
-
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:
-
Database connection issues: Verify PostgreSQL credentials and network connectivity
-
Storage permissions: Check filesystem permissions for persistent volumes
-
First admin user: If no admin configured, access the web interface to create one
-
Transcribe issues: Verify Docker socket access and separate database configuration
-
Origin validation errors: Make sure env.APP_BASE_URL matches your ingress host
For detailed troubleshooting, check the application logs:
Check pod status and events:
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