If your team relies on Slack but you’re tired of message history limits, per-seat pricing, and having your conversations live on someone else’s servers — Mattermost is the answer. It’s an open-source, self-hosted team communication platform that looks and feels like Slack, but runs entirely on your hardware.

What is Mattermost?

Mattermost is a collaboration platform built for teams that need full control over their communication data. It offers channels, direct messages, threads, file sharing, search, and a plugin ecosystem — all packaged in a modern web interface that’ll feel familiar if you’ve used Slack or Microsoft Teams.

Why Self-Host Your Team Chat?

  • Full data ownership: Messages, files, and metadata stay on your server
  • No message limits: Unlimited history, no paywall for scrolling back
  • No per-seat costs: Add as many users as your hardware supports
  • Compliance ready: Meet data residency requirements without enterprise contracts
  • Extensible: Webhooks, slash commands, plugins, and a REST API
  • Offline capable: Works on your LAN even if the internet goes down

Mattermost Editions

Mattermost offers three editions:

EditionPriceBest For
Team EditionFree/Open SourceSmall teams, homelab
Professional$10/user/monthMid-size teams needing SSO/LDAP
EnterpriseCustom pricingLarge orgs with compliance needs

This guide uses the free Team Edition — it covers everything most self-hosters need.

Prerequisites

Before you begin, make sure you have:

  • A Linux server (Ubuntu 22.04+ or Debian 12+ recommended)
  • Docker and Docker Compose installed
  • At least 2GB RAM and 10GB disk space
  • A domain name (optional but recommended for HTTPS)
  • Basic familiarity with the terminal

Step 1: Create the Project Directory

mkdir -p ~/mattermost/{config,data,logs,plugins,client-plugins,bleve-indexes}
cd ~/mattermost

Mattermost stores quite a bit of data across these directories:

  • config/ — Server configuration
  • data/ — Uploaded files and attachments
  • logs/ — Application logs
  • plugins/ — Server-side plugins
  • client-plugins/ — Client-side plugins
  • bleve-indexes/ — Full-text search indexes

Step 2: Set Up Docker Compose

Create your docker-compose.yml:

services:
  mattermost:
    image: mattermost/mattermost-team-edition:latest
    container_name: mattermost
    restart: unless-stopped
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      - TZ=UTC
      - MM_SQLSETTINGS_DRIVERNAME=postgres
      - MM_SQLSETTINGS_DATASOURCE=postgres://mmuser:${POSTGRES_PASSWORD}@postgres:5432/mattermost?sslmode=disable&connect_timeout=10
      - MM_BLEVESETTINGS_INDEXDIR=/mattermost/bleve-indexes
      - MM_SERVICESETTINGS_SITEURL=${SITE_URL:-http://localhost:8065}
    ports:
      - "8065:8065"
    volumes:
      - ./config:/mattermost/config:rw
      - ./data:/mattermost/data:rw
      - ./logs:/mattermost/logs:rw
      - ./plugins:/mattermost/plugins:rw
      - ./client-plugins:/mattermost/client/plugins:rw
      - ./bleve-indexes:/mattermost/bleve-indexes:rw
    security_opt:
      - no-new-privileges:true

  postgres:
    image: postgres:16-alpine
    container_name: mattermost-db
    restart: unless-stopped
    environment:
      - POSTGRES_USER=mmuser
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
      - POSTGRES_DB=mattermost
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD", "pg_isready", "-U", "mmuser", "-d", "mattermost"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  postgres_data:

Step 3: Configure Environment Variables

Create an .env file with your secrets:

# Generate a strong password
POSTGRES_PASSWORD=$(openssl rand -base64 32)
echo "POSTGRES_PASSWORD=$POSTGRES_PASSWORD" > .env
echo "SITE_URL=https://chat.yourdomain.com" >> .env

Your .env file should look like:

POSTGRES_PASSWORD=your_generated_password_here
SITE_URL=https://chat.yourdomain.com

Important: Set SITE_URL to your actual domain. Mattermost uses this for email links, OAuth redirects, and WebSocket connections. If you’re running locally, use http://localhost:8065.

Step 4: Start Mattermost

docker compose up -d

Watch the logs to make sure everything starts cleanly:

docker compose logs -f mattermost

You should see messages about database migrations running (first launch only) and then:

Serverislisteningon[::]:8065

Visit http://your-server-ip:8065 in your browser. You’ll see the Mattermost setup wizard.

Step 5: Initial Setup

The first user you create becomes the System Administrator. Choose wisely:

  1. Enter your email, username, and password
  2. Create your first team (e.g., “Home” or your org name)
  3. You’ll land in the Town Square channel — this is your default public channel

Essential Admin Settings

Navigate to System Console (click the grid icon → System Console) and configure:

Site Configuration → Notifications:

ENSSCnoMMoatTTnbiPPnlfeeiSPcceotEarrimtvtoaie:niorln:5S8eNDs7coimutstrippifl.tiayycyo:autNrSiadTomoAnemRs:aT:iTMnLta.Srtcutoeemrmost

Authentication → Signup:

ERneasbtlreicOtpeCnreSaetrivoenr:tofaDlosmeain(su:nlyeosusrdyoomuaiwna.nctompubloipctiroengails)tration)

Site Configuration → Customization:

SSiitteeNDaemsec:riYpotuironT:eaPmriCvhaatteteamcommunication

Reverse Proxy with Nginx

For production use, you’ll want HTTPS. Here’s an Nginx config:

server {
    listen 80;
    server_name chat.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name chat.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/chat.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/chat.yourdomain.com/privkey.pem;

    location ~ /api/v[0-9]+/(users/)?websocket$ {
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffers 256 16k;
        proxy_buffer_size 16k;
        client_max_body_size 50M;
        proxy_pass http://localhost:8065;
    }

    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Frame-Options SAMEORIGIN;
        client_max_body_size 50M;
        proxy_pass http://localhost:8065;
    }
}

The key detail here is the WebSocket proxy block — Mattermost uses WebSockets for real-time messaging, and without proper upgrade headers, you’ll get a degraded experience with polling fallback.

If you’re using Caddy instead:

c}hat.ryeovuerrdsoem_apirno.xcyomlo{calhost:8065

Caddy handles WebSocket upgrades automatically — no extra config needed.

Enabling Plugins

Mattermost has a plugin marketplace built in. Some highlights:

  • Jira — Link Jira issues to channels
  • GitHub — Get PR notifications and slash commands
  • Zoom/Jitsi — One-click video calls from any channel
  • Playbooks — Incident response runbooks
  • Boards — Kanban boards (Trello-like) built into Mattermost

Enable the marketplace in System Console → Plugin Management:

EEnnaabblleePRleumgoitneMMaarrkkeettppllaaccee::ttrruuee

Then browse and install plugins directly from the UI.

Mobile and Desktop Apps

Mattermost offers native apps for all platforms:

  • Desktop: Windows, macOS, Linux — download here
  • Mobile: iOS and Android — available in app stores
  • Web: Works great in any modern browser

Point any client at your SITE_URL and log in. Push notifications require configuring the Hosted Push Notification Service (HPNS) or running your own push proxy.

Backups

Back up both the database and file storage:

#!/bin/bash
# backup-mattermost.sh
BACKUP_DIR="/backups/mattermost/$(date +%Y%m%d)"
mkdir -p "$BACKUP_DIR"

# Database dump
docker exec mattermost-db pg_dump -U mmuser mattermost | gzip > "$BACKUP_DIR/db.sql.gz"

# File storage
tar czf "$BACKUP_DIR/data.tar.gz" -C ~/mattermost data/

# Config
cp ~/mattermost/config/config.json "$BACKUP_DIR/config.json"

echo "Backup complete: $BACKUP_DIR"

To restore:

# Restore database
gunzip -c db.sql.gz | docker exec -i mattermost-db psql -U mmuser mattermost

# Restore files
tar xzf data.tar.gz -C ~/mattermost/

# Restart
docker compose restart mattermost

Troubleshooting

“WebSocket connection failed”

This almost always means your reverse proxy isn’t forwarding WebSocket upgrade headers. Check the Nginx config above — you need the Upgrade and Connection headers in the websocket location block.

Database connection errors on startup

Make sure PostgreSQL is healthy before Mattermost starts. The depends_on with condition: service_healthy in the Compose file handles this, but if you see issues:

docker compose logs postgres
docker exec mattermost-db pg_isready -U mmuser

High memory usage

Mattermost can be memory-hungry with many users. Tune these in System Console → Environment → Performance:

  • Maximum Users Per Team: Set a reasonable limit
  • Maximum Channels Per Team: Default 2000 is fine for most
  • Reduce Web Server Read/Write Timeout if connections are hanging

File upload failures

Check that the data/ directory has correct permissions:

sudo chown -R 2000:2000 ~/mattermost/data

Mattermost runs as UID 2000 inside the container.

Search not working

If full-text search returns no results, rebuild the Bleve indexes:

Go to System Console → Experimental → Bleve and click “Purge and rebuild indexes.”

Mattermost vs Slack vs Rocket.Chat

FeatureMattermostSlackRocket.Chat
Self-hosted
Free tier message limitUnlimited90 daysUnlimited
File storageYour disk5GB freeYour disk
Threads
Plugin ecosystemGoodExcellentGood
Mobile apps
SSO/LDAP (free)❌ (Pro+)❌ (Pro+)
Resource usageModerateN/AHigher
Ease of setupEasyN/AModerate

Mattermost wins on stability and enterprise polish — it feels like a product, not a project. Rocket.Chat offers more features in the free tier (like LDAP), but Mattermost’s UX is noticeably smoother.

Conclusion

Mattermost gives you a polished, Slack-like team chat experience without handing your conversations to a third party. The Docker setup takes about 10 minutes, the admin console is intuitive, and the mobile apps actually work well. For small teams and homelabs, the free Team Edition is more than enough — you get unlimited users, unlimited history, and full API access. If you outgrow it, the upgrade path to Professional or Enterprise is straightforward without needing to migrate data.

Your team’s conversations belong on your server. Mattermost makes that easy.