Self Hosting
Run Morgenruf on your own servers with Docker Compose. Full control, no vendor lock-in.
Prefer Kubernetes? See the Helm chart guide for a production-ready cluster deployment.
The Docker image ghcr.io/morgenruf/morgenruf:latest is public β no registry authentication or imagePullSecrets required.
Prerequisites
- Docker 24+ and Docker Compose v2+ installed on your host
- A publicly accessible domain/URL (Slack must be able to reach your instance to send events)
- A Slack app with the correct scopes and redirect URLs configured
Create a Slack App
Before running Morgenruf you need to create a Slack app to get your credentials:
- Go to api.slack.com/apps and click Create New App β From Manifest.
- Paste the manifest from the GitHub repo (in
slack-manifest.yaml), replacingAPP_URLwith your public URL. - Under Basic Information, copy your Client ID, Client Secret, and Signing Secret.
Docker Compose Setup
Create a directory for Morgenruf and add a docker-compose.yml:
version: '3.8'
services:
bot:
image: ghcr.io/morgenruf/morgenruf:latest
env_file: .env
ports:
- "3000:3000"
depends_on:
- postgres
restart: unless-stopped
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: morgenruf
POSTGRES_USER: morgenruf
POSTGRES_PASSWORD: changeme
volumes:
- postgres_data:/var/lib/postgresql/data
restart: unless-stopped
volumes:
postgres_data:
docker-compose.yml
Change POSTGRES_PASSWORD to a strong, unique password before running in production. Use a password manager or openssl rand -base64 32 to generate one.
Environment Setup
Copy the example env file and fill in your values:
curl -o .env https://raw.githubusercontent.com/morgenruf/morgenruf/main/.env.example
bash
Open .env in your editor and set the required values:
# Slack credentials (from api.slack.com/apps β Basic Information)
SLACK_CLIENT_ID=your_client_id
SLACK_CLIENT_SECRET=your_client_secret
SLACK_SIGNING_SECRET=your_signing_secret # different from CLIENT_SECRET!
# Database (matches docker-compose.yml postgres service)
DATABASE_URL=postgresql://morgenruf:changeme@postgres:5432/morgenruf
# Public URL of this instance (no trailing slash)
APP_URL=https://morgenruf.your-domain.com
# Flask session signing key β generate with: openssl rand -hex 32
FLASK_SECRET_KEY=replace_with_a_long_random_string
# Optional: Resend API key for welcome + weekly digest emails
# RESEND_API_KEY=re_...
# Optional: override the HTTP port (default 3000)
# PORT=3000
# Optional: log verbosity (DEBUG/INFO/WARNING, default INFO)
# LOG_LEVEL=INFO
.env
Start Morgenruf
Pull the latest image and start all services in the background:
docker compose pull
docker compose up -d
bash
Database Migrations
Migrations run automatically when the container starts (via an init container in Kubernetes, or on startup in Docker). You can also run them manually:
docker run --rm --env-file .env ghcr.io/morgenruf/morgenruf:latest python src/migrate.py
bash
Or against a running container:
docker exec morgenruf-bot-1 python src/migrate.py
bash
Always run migrations after pulling a new image to ensure the schema is up to date.
Verify the Installation
Check the health endpoint to confirm the bot is running:
curl http://localhost:3000/healthz
bash
A healthy instance returns:
{"status": "ok", "db": "connected"}
json
Check logs if something looks wrong:
docker compose logs -f bot
bash
Local Development with Ngrok
Slack requires a publicly accessible HTTPS URL to deliver events. For local development, use ngrok to create a tunnel:
# Install ngrok from https://ngrok.com, then:
ngrok http 3000
bash
Copy the https://xxxx.ngrok-free.app URL ngrok gives you, then:
- Set
APP_URLin your.envto this URL - Update your Slack app's Event Subscriptions Request URL to
https://xxxx.ngrok-free.app/slack/events - Update the OAuth Redirect URLs to
https://xxxx.ngrok-free.app/slack/oauth_redirect - Restart the bot:
docker compose restart bot
The free tier of ngrok generates a new URL each time you restart it. For persistent local dev URLs, use a paid ngrok plan or a static reserved domain.
Upgrading
To upgrade to the latest version:
docker compose pull
docker compose up -d
bash
Migrations run automatically on container start. To run them manually, see the Database Migrations section above.