Mattermost is a self-hosted Slack with threads, markdown, file sharing, voice and video channels, and an integrations framework serious enough that regulated teams run on it. If you need "Slack but ours", this is the one.
What it is
Channels, DMs, threads, emoji, slash commands, webhooks in and out, and a plugin marketplace. Mattermost Calls provides built-in voice/video with a Jitsi backend, so you get meetings without running a whole second stack.
Before you start
- Use the
team-editionimage. Theenterprise-editiontag needs a licence key and will boot into a nag screen without one. - Redis needs a
maxmemorypolicy. Withoutallkeys-lruit will grow until Postgres starts failing and nobody knows why. - SMTP is configured by file, not env var, and it is fiddly. Budget time for it — chat that silently fails to send email is worse than no email.
1 — Create the directories
mkdir -p ~/services/mattermost/{config,data,logs,plugins,client/plugins}
cd ~/services/mattermost
2 — Write the compose file
services:
db:
image: postgres:16-alpine
container_name: mm-db
restart: unless-stopped
environment:
POSTGRES_USER: mmuser
POSTGRES_PASSWORD: CHANGE_ME
POSTGRES_DB: mattermost
volumes:
- ./pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U mmuser"]
interval: 10s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
container_name: mm-cache
restart: unless-stopped
command: >-
redis-server --requirepass CHANGE_ME
--maxmemory 256mb --maxmemory-policy allkeys-lru
healthcheck:
test: ["CMD", "redis-cli", "-a", "CHANGE_ME", "ping"]
interval: 30s
timeout: 5s
retries: 3
app:
image: mattermost/mattermost-team-edition:latest
container_name: mattermost
restart: unless-stopped
depends_on:
db:
condition: service_healthy
cache:
condition: service_healthy
ports:
- "127.0.0.1:8065:8065"
volumes:
- ./config:/mattermost/config
- ./data:/mattermost/data
- ./logs:/mattermost/logs
- ./plugins:/mattermost/plugins
- ./client/plugins:/mattermost/client/plugins
environment:
MM_SQLSETTINGS_DATASOURCE: >-
postgres://mmuser:CHANGE_ME@db:5432/mattermost?sslmode=disable&connect_timeout=10
MM_SQLSETTINGS_DRIVERNAME: postgres
MM_SERVICESETTINGS_SITEURL: https://chat.example.com
MM_SERVICESETTINGS_LISTENADDRESS: ":8065"
MM_SERVICESETTINGS_ENABLELOCALMODE: "true"
MM_CACHESETTINGS_CACHETYPE: redis
MM_CACHESETTINGS_REDISADDRESS: cache:6379
MM_CACHESETTINGS_REDISPASSWORD: CHANGE_ME
MM_TEAMSETTINGS_ENABLEUSERACCESSIONS: "true"
3 — Start it
docker compose up -d
docker compose logs -f app | grep -i "server is listening\|error"
4 — First-run setup
- Create the first account in the browser at
http://yourhost:8065, then immediately create the System Admin role. The first user is not automatically a system admin. -
System Console → Environment → Web Server: set
Serve over TLS connectionsoff only if your proxy terminates TLS, and setWebsocket URLtowss://chat.example.com. Missing that wss setting is why live typing sometimes turns into a spinner. -
System Console → Environment → Email: set SMTP. Send yourself a test and read the result in
./logsbefore declaring it done. - Under Integrations, create an incoming webhook for notifications and an outgoing one for your own automations. This is the single most useful feature in the product.
5 — Back it up
docker exec -u mmuser mm-db pg_dump -U mmuser mattermost | gzip > mm-$(date +%F).sql.gz
tar -czf mm-plugins.tgz ./plugins ./client/plugins