version bump norish

This commit is contained in:
Richard Tomik
2026-04-13 16:15:48 +02:00
parent 839d5ce530
commit 256f7e6807
11 changed files with 2058 additions and 2421 deletions

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 |
|-------------------------|--------------------------------------------------------------------------------------|--------------------|
| `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 | `[]` |
| Name | Description | Default |
|------|-------------|---------|
| `image.repository` | Donetick image repository | `donetick/donetick` |
| `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 | `{}` |
| 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 |
### Pod Configuration
### Persistence Parameters
| 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 | `""` |
| `ingress.tls` | TLS configuration | See values.yaml |
| `ingress.tls[].secretName` | Host-specific TLS secret name (overrides global) | `""` |
### 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 |
|----------------------------------------|------------------------------------------------------|---------------|
| `config.server.port` | Server port | `2021` |
| `config.server.read_timeout` | Server read timeout | `10s` |
| `config.server.write_timeout` | Server write timeout | `10s` |
| `config.server.rate_period` | Rate limiting period | `60s` |
| `config.server.rate_limit` | Rate limiting requests per period | `300` |
| `config.server.serve_frontend` | Serve frontend files | `true` |
| Name | Description | Default |
|------|-------------|---------|
| `config.server.port` | Server port | `2021` |
| `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 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 |
|----------------------------------------|------------------------------------------------------|---------------|
| `config.features.notifications` | Enable notifications | `true` |
| `config.features.realtime` | Enable real-time features | `true` |
| `config.features.oauth` | Enable OAuth authentication | `false` |
| Name | Description | Default |
|------|-------------|---------|
| `config.features.notifications` | Enable notifications | `true` |
| `config.features.realtime` | Enable real-time features | `true` |
| `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)