Zum Inhalt

Self-Hosting Team Chat: Mattermost on Debian Linux (Go Backend, PostgreSQL, WebRTC Calls & Webhooks)

Executive Summary: Mattermost Team Edition is the premier open-source, self-hosted Slack and Microsoft Teams replacement purpose-built for engineering, DevOps, and incident-response teams. Powered by a compiled Go backend and PostgreSQL, Mattermost delivers sub-second channel switching, rich Markdown and code syntax highlighting, Slack-compatible incoming/outgoing Webhooks (for Prometheus, Uptime Kuma, Forgejo, and GitLab CI alerts), and built-in peer-to-peer/SFU WebRTC Voice & Screen-Sharing Calls (UDP 8443) without relying on external SaaS cloud servers.

  • Go + PostgreSQL Performance


    Handles thousands of concurrent WebSocket clients and millions of searchable messages inside a single Go binary consuming < 250 MB RAM.

  • Slack-Compatible Webhooks


    Drop-in compatibility with Slack JSON webhook payloads—pipe Uptime Kuma, Zabbix, Forgejo, and Systemd alerts directly into Ops channels.

  • Built-In WebRTC Calls & Screen Share


    Host low-latency voice huddles and 1080p screen-sharing sessions directly inside channels via UDP/TCP port 8443.

  • Native Desktop, iOS & Android Apps


    Connect official Linux/Windows/macOS desktop clients and mobile apps with full self-hosted data sovereignty.


1. Mattermost Architecture & WebSocket / WebRTC Flows

Mattermost uses HTTPS + WebSockets (/api/v4/websocket) on port 8065 for real-time chat events and a dedicated UDP/TCP port (8443) for low-latency WebRTC media streams (Calls plugin):

%%{init: {'flowchart': {'wrappingWidth': 600}}}%%
flowchart TB
    CLI["💻 Desktop & Mobile Apps<br/>(HTTPS / WSS + WebRTC)"]
    PROXY["🔒 Reverse Proxy (TLS 1.3)<br/>chat.nejat-hakan.de:443"]
    MM["💬 Mattermost Server (Go)<br/>127.0.0.1:8065 (HTTP/WS)<br/>0.0.0.0:8443 (UDP/TCP Calls)"]
    PG["🗄️ PostgreSQL 17<br/>Channels, Users & Full-Text"]

    CLI <==>|"1. Chat & API (WSS :443)"| PROXY
    PROXY <==>|"Proxy :8065"| MM
    CLI <==>|"2. Direct WebRTC Media (:8443/udp)"| MM
    MM <--> PG

2. Production compose.yaml (Mattermost Team Edition + PostgreSQL 17)

Mattermost runs inside its container as non-root UID/GID 2000:2000. Provision the host directories with 2000:2000 ownership first:

# File: /opt/mattermost-ops/01_prepare_dirs.sh
sudo mkdir -p /srv/containers/mattermost/{config,data,logs,plugins,client-plugins,bleve-indexes,postgres}
sudo chown -R 2000:2000 /srv/containers/mattermost/{config,data,logs,plugins,client-plugins,bleve-indexes}
# File: /srv/containers/mattermost/compose.yaml
services:
  mattermost:
    image: docker.io/mattermost/mattermost-team-edition:release-10
    container_name: mattermost
    depends_on:
      mattermost-db:
        condition: service_healthy
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    pids_limit: 200
    ports:
      - "127.0.0.1:8065:8065/tcp" # Web UI & WebSocket API behind Reverse Proxy
      - "8443:8443/udp"           # WebRTC Voice & Screen-Sharing Calls (UDP)
      - "8443:8443/tcp"           # WebRTC Fallback (TCP)
    environment:
      TZ: "Europe/Berlin"
      MM_SQLSETTINGS_DRIVERNAME: "postgres"
      MM_SQLSETTINGS_DATASOURCE: "postgres://mmuser:${MM_DB_PASSWORD}@mattermost-db:5432/mattermost?sslmode=disable&connect_timeout=10"
      MM_BLEVESETTINGS_INDEXDIR: "/mattermost/bleve-indexes"
      MM_SERVICESETTINGS_SITEURL: "https://chat.nejat-hakan.de"
      MM_SERVICESETTINGS_LISTENADDRESS: ":8065"
      MM_TEAMSETTINGS_ENABLEOPENSERVER: "false"
    volumes:
      - /srv/containers/mattermost/config:/mattermost/config:rw
      - /srv/containers/mattermost/data:/mattermost/data:rw
      - /srv/containers/mattermost/logs:/mattermost/logs:rw
      - /srv/containers/mattermost/plugins:/mattermost/plugins:rw
      - /srv/containers/mattermost/client-plugins:/mattermost/client/plugins:rw
      - /srv/containers/mattermost/bleve-indexes:/mattermost/bleve-indexes:rw

  mattermost-db:
    image: docker.io/library/postgres:17-bookworm
    container_name: mattermost-db
    restart: unless-stopped
    environment:
      POSTGRES_USER: "mmuser"
      POSTGRES_PASSWORD: "${MM_DB_PASSWORD}"
      POSTGRES_DB: "mattermost"
    volumes:
      - /srv/containers/mattermost/postgres:/var/lib/postgresql/data:rw
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U mmuser -d mattermost"]
      interval: 10s
      timeout: 5s
      retries: 5

3. Sending Infrastructure Alerts via Incoming Webhooks

In Mattermost, navigate to Integrations → Incoming Webhooks → Add Incoming Webhook, select your #infra-alerts channel, and trigger rich Markdown alerts from any Linux script or systemd OnFailure= unit:

# File: /usr/local/bin/notify-mattermost.sh
#!/usr/bin/env bash
set -euo pipefail

WEBHOOK_URL="https://chat.nejat-hakan.de/hooks/your-generated-webhook-id"
HOSTNAME_STR=$(hostname -f)

curl -fsSL -X POST -H 'Content-Type: application/json' \
    -d "{
      \"username\": \"srv001-watchdog\",
      \"text\": \"### 🚨 Infrastructure Alert on \`${HOSTNAME_STR}\`\n| Check | Status | Details |\n| :--- | :--- | :--- |\n| **Backup Job** | ✅ OK | Verified Borg archive in 14.2s |\"
    }" \
    "$WEBHOOK_URL"

Where to Go Next

Compare centralized team chat (Mattermost) with decentralized End-to-End Encrypted federation (Matrix Synapse):

  • Matrix Synapse Homeserver


    Need decentralized federation across domains and Olm/Megolm End-to-End Encryption (Element)? Deploy Matrix Synapse.

    Matrix Synapse Guide

  • Uptime Kuma Monitoring


    Connect Uptime Kuma directly to your Mattermost Incoming Webhook for instant outage alerts.

    Uptime Kuma Guide

  • Forgejo Git Webhooks


    Post real-time Pull Request and CI build notifications into Mattermost channels.

    Forgejo Guide