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 MBRAM. -
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.
-
Uptime Kuma Monitoring
Connect Uptime Kuma directly to your Mattermost Incoming Webhook for instant outage alerts.
-
Forgejo Git Webhooks
Post real-time Pull Request and CI build notifications into Mattermost channels.