Files
2026-07-13 13:32:57 +08:00

740 lines
17 KiB
Markdown

# Trigger.dev v4 Helm Chart
This Helm chart deploys Trigger.dev v4 self-hosting stack to Kubernetes.
## Quick Start
### Prerequisites
```bash
# Build Helm dependencies (required for Bitnami charts)
helm dependency build
# Extract dependency charts for local template testing
for file in ./charts/*.tgz; do echo "Extracting $file"; tar -xzf "$file" -C ./charts; done
# Alternative: Use --dependency-update flag for template testing
helm template trigger . --dependency-update
```
### Installation
```bash
# Deploy with default values (testing/development only)
helm install trigger .
# Deploy to specific namespace
helm install trigger . -n trigger --create-namespace
# Deploy with custom values for production
helm install trigger . -f values-production.yaml -n trigger --create-namespace
```
### Upgrading
```bash
# Upgrade existing release
helm upgrade trigger .
# Upgrade with new values
helm upgrade trigger . -f values-production.yaml
```
### Access the dashboard
```bash
kubectl port-forward svc/trigger-webapp 3040:3030 --address 0.0.0.0
```
Dashboard: http://localhost:3040/
### Deploying your tasks
```bash
# The --push arg is required when testing locally
npx trigger.dev@latest deploy --push
```
## ⚠️ Security Requirements
### Secrets Configuration
**IMPORTANT**: The default secrets are for **TESTING ONLY** and must be changed for production.
#### Required Secrets
All secrets must be exactly **32 hexadecimal characters** (16 bytes):
- `sessionSecret` - User authentication sessions
- `magicLinkSecret` - Passwordless login tokens
- `encryptionKey` - Sensitive data encryption
- `managedWorkerSecret` - Worker authentication
#### Generate Production Secrets
```bash
for i in {1..4}; do openssl rand -hex 16; done
```
#### Configure Production Secrets
```yaml
# values-production.yaml
secrets:
sessionSecret: "your-generated-secret-1"
magicLinkSecret: "your-generated-secret-2"
encryptionKey: "your-generated-secret-3"
managedWorkerSecret: "your-generated-secret-4"
objectStore:
accessKeyId: "your-s3-access-key"
secretAccessKey: "your-s3-secret-key"
```
## Architecture
This chart deploys the following components:
### Core Services
- **Webapp** - Main Trigger.dev application (port 3030)
- **PostgreSQL** - Primary database with logical replication
- **Redis** - Cache and job queue
- **Electric** - Real-time sync service (ElectricSQL)
### Worker Services
- **Supervisor** - Kubernetes worker orchestrator for executing runs
### Supporting Services
- **ClickHouse** - Analytics database
- **MinIO** - S3-compatible object storage
- **Registry** - Private Docker registry for deployed code (EXPERIMENTAL - disabled by default)
## Configuration
### Basic Configuration
```yaml
webapp:
# Application URLs
appOrigin: "https://trigger.example.com"
loginOrigin: "https://trigger.example.com"
apiOrigin: "https://trigger.example.com"
# Bootstrap mode (auto-creates worker group)
bootstrap:
enabled: true # Enable for combined setups
workerGroupName: "bootstrap"
```
### External Services
Use external managed services instead of bundled components:
```yaml
# External PostgreSQL
postgres:
deploy: false
external:
host: "your-postgres.rds.amazonaws.com"
port: 5432
database: "trigger"
username: "trigger_user"
password: "your-password"
# External Redis
redis:
deploy: false
external:
host: "your-redis.cache.amazonaws.com"
port: 6379
password: "your-password"
# External Docker Registry (e.g., Kind local registry)
registry:
deploy: true
external:
host: "localhost"
port: 5001
username: ""
password: ""
```
### Ingress Configuration
```yaml
# Webapp ingress
webapp:
ingress:
enabled: true
className: "nginx"
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
hosts:
- host: trigger.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: trigger-tls
hosts:
- trigger.example.com
# Registry ingress
registry:
ingress:
enabled: true
className: "nginx"
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
hosts:
- host: registry.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: registry-tls
hosts:
- registry.example.com
```
### Resource Configuration
```yaml
resources:
webapp:
limits:
cpu: 2000m
memory: 4Gi
requests:
cpu: 1000m
memory: 2Gi
postgres:
primary:
resources:
limits:
cpu: 1000m
memory: 2Gi
```
## Deployment Modes
### Testing/Development
- Use default values
- Single replica
- Lower resource limits
- Bootstrap mode enabled
### Production
- Custom secrets (required)
- Multiple replicas with anti-affinity
- Production resource limits
- External services recommended
- Ingress with TLS
- Persistent storage
## Persistence
All services support persistent storage and allow you to control the storage class globally or per service. Our internal services (Registry) now support the full Bitnami persistence configuration pattern:
### Basic Persistence Configuration
```yaml
global:
storageClass: "fast-ssd" # Default for all services
# Bitnami chart services (simplified configuration)
postgres:
primary:
persistence:
enabled: true
size: 10Gi
storageClass: "postgres-nvme" # Optional: override for PostgreSQL
redis:
master:
persistence:
enabled: true
size: 5Gi
storageClass: "redis-ssd" # Optional: override for Redis
clickhouse:
persistence:
enabled: true
size: 10Gi
storageClass: "analytics-hdd" # Optional: override for ClickHouse
s3:
persistence:
enabled: true
size: 10Gi
storageClass: "objectstore-ssd" # Optional: override for S3
```
### Internal Services - Full Bitnami-Style Configuration
Our internal services (Registry) support the complete Bitnami persistence configuration pattern:
```yaml
# Registry - Full persistence configuration options
registry:
persistence:
enabled: true
# Name to assign the volume
volumeName: "data"
# Name of an existing PVC to use
existingClaim: ""
# The path the volume will be mounted at
mountPath: "/var/lib/registry"
# The subdirectory of the volume to mount to
subPath: ""
# PVC Storage Class for Registry data volume
storageClass: "registry-ssd"
# PVC Access Mode for Registry volume
accessModes:
- "ReadWriteOnce"
# PVC Storage Request for Registry volume
size: 10Gi
# Annotations for the PVC
annotations:
backup.velero.io/backup-volumes: "data"
# Labels for the PVC
labels:
app.kubernetes.io/component: "storage"
# Selector to match an existing Persistent Volume
selector:
matchLabels:
tier: "registry"
# Custom PVC data source
dataSource:
name: "registry-snapshot"
kind: "VolumeSnapshot"
apiGroup: "snapshot.storage.k8s.io"
# Shared persistent volume for worker token file
persistence:
shared:
enabled: true
size: 5Mi
accessMode: ReadWriteOnce
# accessMode: ReadWriteMany # Use for cross-node deployment
storageClass: ""
retain: true # Prevents deletion on uninstall
```
### Persistence Configuration Rules
- **Service-level storageClass** overrides the global value for that service only
- **Global storageClass** applies to all services that don't specify their own
- **Cluster default** is used if neither global nor service-level storageClass is set
- **Internal services** (Registry) support full Bitnami-style configuration
- **Bitnami chart services** use their respective chart's configuration patterns
## Monitoring
### Health Checks
Health checks are configured for all services:
- HTTP endpoints for web services
- Database connection tests
- Readiness and liveness probes
### Health Probe Configuration
All non-Bitnami services support configurable health probes:
```yaml
# Webapp health probes
webapp:
livenessProbe:
enabled: true
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 5
failureThreshold: 5
successThreshold: 1
readinessProbe:
enabled: true
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 1
failureThreshold: 5
successThreshold: 1
startupProbe:
enabled: false
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 60
successThreshold: 1
# Supervisor health probes
supervisor:
livenessProbe:
enabled: true
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 5
failureThreshold: 5
successThreshold: 1
readinessProbe:
enabled: true
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 1
failureThreshold: 5
successThreshold: 1
startupProbe:
enabled: false
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 60
successThreshold: 1
# Electric health probes
electric:
livenessProbe:
enabled: true
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 5
failureThreshold: 5
successThreshold: 1
readinessProbe:
enabled: true
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 1
failureThreshold: 5
successThreshold: 1
startupProbe:
enabled: false
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 60
successThreshold: 1
# Registry health probes
registry:
livenessProbe:
enabled: true
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 5
failureThreshold: 5
successThreshold: 1
readinessProbe:
enabled: true
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 1
failureThreshold: 5
successThreshold: 1
startupProbe:
enabled: false
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 60
successThreshold: 1
```
### Prometheus Integration
ServiceMonitors are available for webapp and supervisor services:
```yaml
webapp:
serviceMonitor:
enabled: true
interval: "30s"
path: "/metrics"
labels:
release: prometheus-stack
supervisor:
serviceMonitor:
enabled: true
interval: "30s"
path: "/metrics"
labels:
release: prometheus-stack
```
## Operations
### Force Pod Restart
When you need to force all pods to restart (e.g., to pick up updated secrets or config):
```bash
# Force restart using timestamp annotation (Helm-native approach)
helm upgrade <release-name> . --set-string podAnnotations.restartedAt="$(date +%s)"
# Example
helm upgrade trigger . --set-string podAnnotations.restartedAt="$(date +%s)"
```
This approach:
- ✅ Uses Helm's built-in annotation mechanism
- ✅ Safe - doesn't recreate immutable resources like PVCs
- ✅ Targeted - only restarts pods that need updates
- ✅ Trackable - increments Helm revision number
### Configuration Updates
After changing secrets or ConfigMaps in your values file:
```bash
# 1. Upgrade with new values
helm upgrade trigger . -f values-production.yaml
# 2. Force pod restart to pick up changes
helm upgrade trigger . -f values-production.yaml \
--set-string podAnnotations.restartedAt="$(date +%s)"
```
## Troubleshooting
### Check Pod Status
```bash
kubectl get pods -l app.kubernetes.io/name=trigger.dev
```
### View Logs
```bash
# Webapp logs
kubectl logs -l app.kubernetes.io/component=webapp
# Database logs
kubectl logs -l app.kubernetes.io/component=postgres
```
### Run Tests
```bash
helm test trigger.dev
```
## Testing
### Validate Deployment
```bash
# Check Helm template syntax
helm template trigger.dev . --dry-run > /dev/null && echo "Template validation successful"
# Test webapp health endpoint (requires port forwarding)
curl -s -o /dev/null -w "%{http_code}" http://localhost:3040/healthcheck || echo "Connection failed"
# Port forward to access webapp locally
kubectl port-forward svc/trigger.dev-webapp 3040:3030 --address 0.0.0.0
```
### Common Issues
1. **Secrets errors**: Ensure all secrets are exactly 32 hex characters
2. **Database connection**: Check PostgreSQL is ready before webapp starts
3. **Resource limits**: Increase limits for ClickHouse in constrained environments
4. **Config not applying**: Use the pod restart technique above to force config reload
5. **Image pull errors**: When testing locally, deploy with `npx trigger.dev@latest deploy --push`
## Examples
See `values-production-example.yaml` for a complete production configuration example.
## Version Management
### Understanding Versions
The Helm chart uses three types of versions:
1. **Chart Version** (`Chart.yaml:version`) - Helm chart packaging version
2. **App Version** (`Chart.yaml:appVersion`) - Trigger.dev application version
3. **Component Versions** (`values.yaml`) - Individual service versions (Electric, ClickHouse, etc.)
### Release Process
#### For Chart Maintainers
1. **Update Chart Version** for chart changes:
```bash
# Edit Chart.yaml
version: 4.1.0 # Increment for chart changes (semver)
```
2. **Update App Version** when Trigger.dev releases new version:
```bash
# Edit Chart.yaml
appVersion: "v4.1.0" # Match Trigger.dev release (v-prefixed image tag)
```
3. **Release via GitHub**:
```bash
# Tag and push
git tag helm-v4.1.0
git push origin helm-v4.1.0
# GitHub Actions will automatically build and publish to GHCR
```
#### For Users
```bash
# Install specific chart version
helm upgrade --install trigger \
oci://ghcr.io/triggerdotdev/charts/trigger.dev \
--version 4.1.0
# Install latest chart version
helm upgrade --install trigger \
oci://ghcr.io/triggerdotdev/charts/trigger.dev
# Override app version (advanced)
helm upgrade --install trigger . \
--set webapp.image.tag=v4.0.1
```
## Production Readiness Checklist
### 🔒 Security (REQUIRED)
- [ ] **Generate unique secrets** (never use defaults):
```bash
# Generate 4 secrets
for i in {1..4}; do openssl rand -hex 16; done
```
- [ ] **Configure security contexts**:
```yaml
webapp:
podSecurityContext:
fsGroup: 1000
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
```
- [ ] **Enable network policies** (if supported by cluster)
- [ ] **Configure proper RBAC** for supervisor
- [ ] **Use TLS ingress** with cert-manager
### 📊 Resource Management (REQUIRED)
- [ ] **Set resource limits and requests** - for example:
```yaml
webapp:
resources:
limits:
cpu: 2000m
memory: 4Gi
requests:
cpu: 1000m
memory: 2Gi
postgres:
primary:
resources:
limits:
cpu: 1000m
memory: 2Gi
requests:
cpu: 500m
memory: 1Gi
redis:
master:
resources:
limits:
cpu: 500m
memory: 1Gi
requests:
cpu: 250m
memory: 512Mi
# ClickHouse can be very resource intensive, so we recommend setting limits and requests accordingly
# Note: not doing this can cause OOM crashes which will cause issues across many different features
clickhouse:
resources:
limits:
cpu: 4000m
memory: 16Gi
requests:
cpu: 2000m
memory: 8Gi
supervisor:
resources:
limits:
cpu: 500m
memory: 1Gi
requests:
cpu: 250m
memory: 512Mi
```
- [ ] **Configure persistent storage for all services** - for example:
```yaml
global:
storageClass: "fast-nvme" # Default for all services
postgres:
primary:
persistence:
size: 500Gi
redis:
master:
persistence:
size: 20Gi
clickhouse:
persistence:
size: 100Gi
s3:
persistence:
size: 200Gi
# Internal services support full Bitnami-style configuration
registry:
persistence:
enabled: true
size: 100Gi
storageClass: "registry-ssd"
annotations:
backup.velero.io/backup-volumes: "data"
```
### 🏗️ High Availability (RECOMMENDED)
- [ ] **Multiple replicas** with pod anti-affinity
- [ ] **Pod disruption budgets**
- [ ] **External managed services** (RDS, ElastiCache, etc.)
- [ ] **Multi-AZ storage classes**
- [ ] **Backup strategies** for databases
### 📈 Monitoring (RECOMMENDED)
- [ ] **Enable ServiceMonitors** for Prometheus
- [ ] **Configure alerting** for critical services
- [ ] **Set up log aggregation**
- [ ] **Monitor resource usage** and adjust limits
### 🚀 Performance (OPTIONAL)
- [ ] **Horizontal Pod Autoscaler** for webapp
- [ ] **Vertical Pod Autoscaler** for data services
- [ ] **Node affinity** for data services
- [ ] **Separate storage classes** for different workloads
## Support
- Documentation: https://trigger.dev/docs/self-hosting
- GitHub Issues: https://github.com/triggerdotdev/trigger.dev/issues
- Discord: https://discord.gg/untWVke9aH