morgenruf.dev

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:

  1. Go to api.slack.com/apps and click Create New App β†’ From Manifest.
  2. Paste the manifest from the GitHub repo (in slack-manifest.yaml), replacing APP_URL with your public URL.
  3. 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:

  1. Set APP_URL in your .env to this URL
  2. Update your Slack app's Event Subscriptions Request URL to https://xxxx.ngrok-free.app/slack/events
  3. Update the OAuth Redirect URLs to https://xxxx.ngrok-free.app/slack/oauth_redirect
  4. 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.