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.
Upgrading to member profiles and Celebrations
The release that adds the member profile and Celebrations needs two changes in your Slack app, both optional. Nothing else changes: no new environment variables.
- Migrations run automatically. Three new ones, all additive:
051_member_profiles.sql(core),053_workspace_calendar.sql(core, working days and holidays) and054_celebrations.sql(the Celebrations module). - Add the
reactions:writebot scope to your Slack app, under OAuth & Permissions or by pasting the rootslack-manifest.yaml. Celebrations uses it only for the π on its posts. Without it the posts still go out. Workspaces get the reaction after they reinstall or re-authorise. - Update the
/morgenrufslash command under Slash Commands: descriptionYour profile, and what Morgenruf can do, usage hint[profile | help]. The command already exists and works without this change; only the hint Slack shows is new. - The stale
app/slack-manifest.yamlandapp/slack-manifest.jsonare removed. The pair at the repository root is the only manifest.