morgenruf.dev

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).