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.

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) and 054_celebrations.sql (the Celebrations module).
  • Add the reactions:write bot scope to your Slack app, under OAuth & Permissions or by pasting the root slack-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 /morgenruf slash command under Slash Commands: description Your 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.yaml and app/slack-manifest.json are removed. The pair at the repository root is the only manifest.