Kubernetes / Helm
Deploy Morgenruf to any Kubernetes cluster using the official Helm chart.
Prerequisites
- A running Kubernetes cluster (any cloud or on-prem)
- Helm 3 installed and configured against your cluster
- A Slack app with Client ID, Client Secret, and Signing Secret ready
- A PostgreSQL instance (recommended) β the bundled PostgreSQL is available but defaults to
enabled: false; use an external DB in production
Add the Helm Repository
helm repo add morgenruf https://charts.morgenruf.dev
helm repo update
bash
Confirm the chart is available:
helm search repo morgenruf
bash
The current chart version is 0.3.0. To pin to a specific version, add --version 0.3.0 to the install command.
Install
Install with all required values passed via --set flags:
helm install morgenruf morgenruf/morgenruf \
--namespace morgenruf \
--create-namespace \
--set slack.clientId="YOUR_CLIENT_ID" \
--set slack.clientSecret="YOUR_CLIENT_SECRET" \
--set slack.signingSecret="YOUR_SIGNING_SECRET" \
--set app.url="https://api.your-domain.com" \
--set flaskSecretKey="$(openssl rand -hex 32)" \
--set externalDatabase.url="postgresql://user:pass@db-host:5432/morgenruf" \
--set postgresql.enabled=false
bash
clientSecret β signingSecret. These are two different values from your Slack app's Basic Information page. Do not mix them up β the app will fail to verify Slack requests if signingSecret is wrong.
For production deployments, store your secrets in Kubernetes Secrets or a tool like Vault rather than passing them as --set values. Use existingSecret values to reference pre-created secrets.
Install with a values file
For a more maintainable setup, create a values.yaml file:
slack:
clientId: "YOUR_CLIENT_ID"
clientSecret: "YOUR_CLIENT_SECRET" # from Basic Information
signingSecret: "YOUR_SIGNING_SECRET" # DIFFERENT from clientSecret!
app:
url: "https://api.your-domain.com" # public HTTPS URL, no trailing slash
flaskSecretKey: "replace_with_openssl_rand_hex_32"
# Recommended: use an external database
postgresql:
enabled: false
externalDatabase:
url: "postgresql://user:password@your-db-host:5432/morgenruf"
ingress:
enabled: true
host: api.your-domain.com
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
tls: true
replicaCount: 2
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 100m
memory: 128Mi
values.yaml
Then install with:
helm install morgenruf morgenruf/morgenruf \
--namespace morgenruf \
--create-namespace \
-f values.yaml
bash
Values Reference
All configurable values for the Helm chart:
| Key | Default | Description |
|---|---|---|
replicaCount |
1 |
Number of pod replicas. Use 2+ for high availability. |
image.repository |
ghcr.io/morgenruf/morgenruf |
Container image repository. |
image.tag |
latest |
Image tag. Pin to a specific version for production (e.g. v1.2.0). |
image.pullPolicy |
IfNotPresent |
Kubernetes image pull policy. |
slack.clientId |
"" |
Slack app Client ID. |
slack.clientSecret |
"" |
Slack app Client Secret. |
slack.signingSecret |
"" |
Slack Signing Secret for request verification. Not the same as clientSecret. |
app.url |
"" |
Public HTTPS URL of this instance (no trailing slash). Used for OAuth redirect URIs. |
flaskSecretKey |
"" |
Flask session signing key. Generate with openssl rand -hex 32. |
postgresql.enabled |
false |
Deploy a bundled PostgreSQL instance. Defaults to false β use an external DB in production. |
postgresql.auth.database |
morgenruf |
PostgreSQL database name. |
postgresql.auth.username |
morgenruf |
PostgreSQL username. |
postgresql.auth.password |
"" |
PostgreSQL password. Always set this. |
externalDatabase.url |
"" |
Full PostgreSQL connection string when postgresql.enabled=false. |
ingress.enabled |
false |
Enable Kubernetes Ingress resource. |
ingress.host |
"" |
Hostname for the ingress rule (e.g. morgenruf.example.com). |
ingress.tls |
false |
Enable TLS for the ingress. Requires a cert-manager or pre-created secret. |
resources.limits.cpu |
500m |
CPU resource limit. |
resources.limits.memory |
512Mi |
Memory resource limit. |
resources.requests.cpu |
100m |
CPU resource request. |
resources.requests.memory |
128Mi |
Memory resource request. |
serviceAccount.create |
true |
Create a Kubernetes ServiceAccount for the pod. |
podAnnotations |
{} |
Annotations to add to the bot pod(s). |
Cloudflare Tunnel (no Ingress)
If you use Cloudflare Tunnel instead of a traditional ingress controller, disable the built-in Ingress resource and let the tunnel handle inbound traffic:
ingress:
enabled: false # Cloudflare Tunnel routes traffic directly to the Service
service:
type: ClusterIP
port: 3000
values.yaml
Point your cloudflared tunnel config at the Morgenruf Service name
(e.g. http://morgenruf.morgenruf.svc.cluster.local:3000).
Set app.url to the public hostname exposed by Cloudflare.
Upgrade
To upgrade to a newer chart or image version:
helm repo update
helm upgrade morgenruf morgenruf/morgenruf \
--namespace morgenruf \
-f values.yaml
bash
Helm will perform a rolling update, keeping at least one pod available during the transition.
Uninstall
To remove Morgenruf from your cluster:
helm uninstall morgenruf --namespace morgenruf
bash
This will delete all Morgenruf resources but will not delete the namespace or PersistentVolumeClaims (to protect your data). Delete those manually if needed.
Using an External PostgreSQL
To connect to an existing PostgreSQL instance instead of the bundled one:
postgresql:
enabled: false
externalDatabase:
url: "postgresql://user:password@your-db-host:5432/morgenruf"
values.yaml
Make sure the database exists and the user has CREATE privileges (needed for migrations).