Mattermost gives a team its own Slack, with the entire message history in a database you control and no per-user monthly fee. Installing it takes twenty minutes. The parts that decide whether it works properly are less obvious: a site URL that breaks invitations when wrong, a reverse proxy that must forward WebSocket upgrades, and mobile push notifications that cannot come from your server at all.
Mattermost is a Go binary in front of a PostgreSQL database, which makes it one of the easier team chat servers to run and one of the easiest to misconfigure in ways that only appear later. Two settings account for most of the trouble: the site URL, and whether the reverse proxy passes WebSocket upgrades through.
MassiveGRID Ubuntu VPS: Ubuntu 24.04 LTS pre-installed · Proxmox HA cluster with automatic failover · Ceph 3x replicated NVMe storage · independent CPU, RAM and storage scaling · 12 Tbps DDoS protection · 100% uptime SLA
Deploy a Cloud VPS — from $1.99/mo
Dedicated VPS for guaranteed cores · Fully managed hosting
Sizing the Server
Mattermost is light for its category. The database does most of the work, and message history is small compared with the file uploads that accumulate alongside it.
| Users | Configuration | MassiveGRID VPS |
|---|---|---|
| Up to 25 | 2 vCPU / 4 GB GB / 64 GB | $9.58/mo |
| Up to 200 | 2 vCPU / 8 GB GB / 128 GB | $13.42/mo |
| Up to 1,000 | 4 vCPU / 8 GB GB / 256 GB | $20.44/mo |
| 1,000+ | 8 vCPU / 16 GB GB / 256 GB+ | $38.32/mo |
Storage is the line that grows without warning. Text is negligible; screenshots, recordings and shared documents are not. Plan on file uploads dominating the disk within a year, and decide early whether they live on the server or in object storage.
Database First
Mattermost supports PostgreSQL, and PostgreSQL is the only sensible choice now that MySQL support is deprecated. Install it and create a dedicated database and role:
apt update && apt install -y postgresql
sudo -u postgres psql
CREATE DATABASE mattermost;
CREATE USER mmuser WITH PASSWORD 'use-a-generated-password';
GRANT ALL PRIVILEGES ON DATABASE mattermost TO mmuser;
\c mattermost
GRANT ALL ON SCHEMA public TO mmuser;
\q
That last grant matters on PostgreSQL 15 and newer, where a non-owner no longer gets schema privileges implicitly. Omit it and the first migration fails with a permission error that looks like a connection problem.
Installing Mattermost
Use the official repository so upgrades follow apt rather than a manual tarball dance:
curl -fsSL https://deb.packages.mattermost.com/repo-setup.sh | bash -s mattermost
apt update && apt install -y mattermost
Then point it at the database in /opt/mattermost/config/config.json:
"SqlSettings": {
"DriverName": "postgres",
"DataSource": "postgres://mmuser:use-a-generated-password@127.0.0.1:5432/mattermost?sslmode=disable&connect_timeout=10"
}
Start it and confirm it is listening on 8065 before adding anything in front:
systemctl enable --now mattermost
ss -lntp | grep 8065
journalctl -u mattermost -f
The Site URL Setting
Set ServiceSettings.SiteURL to the exact public URL, scheme included, with no trailing slash:
"ServiceSettings": {
"SiteURL": "https://chat.example.com",
"ListenAddress": "127.0.0.1:8065"
}
An empty or wrong site URL does not stop the server starting, which is why it goes unnoticed. It breaks email invitation links, OAuth redirects, mobile app connections and permalinks, and each of those failures presents as a different problem. If invitations arrive pointing at localhost, this is why.
Binding the listen address to localhost is the other half. Mattermost speaks plain HTTP; TLS is the proxy's job, and leaving 8065 open to the internet exposes an unencrypted login form.
Nginx and WebSockets
Mattermost keeps a WebSocket open for live message delivery. A proxy that does not forward the upgrade produces the single most reported symptom: the client loads, shows history, and never receives a new message until you refresh.
upstream mattermost {
server 127.0.0.1:8065;
keepalive 32;
}
server {
listen 443 ssl;
http2 on;
server_name chat.example.com;
ssl_certificate /etc/letsencrypt/live/chat.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/chat.example.com/privkey.pem;
client_max_body_size 50M;
location ~ /api/v[0-9]+/(users/)?websocket$ {
proxy_pass http://mattermost;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
}
location / {
proxy_pass http://mattermost;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Set client_max_body_size to match the file size limit you configure in Mattermost. If the proxy limit is lower, uploads fail at the proxy with a 413 and the application logs show nothing at all, which sends people looking in the wrong place.
Then get a certificate:
certbot --nginx -d chat.example.com
Email Is Not Optional
Without working SMTP there are no invitations, no password resets and no notification emails, which reduces the server to something only existing members can use. Configure it in the System Console under Environment, then SMTP, and use the built-in connection test rather than trusting the settings.
Use a transactional provider rather than sending directly from the VPS. A new IP address has no sending reputation, and invitation emails that land in spam look like a broken deployment to everyone you invited.
File Storage and Backups
By default uploads go to /opt/mattermost/data. That is fine and it means the disk grows steadily. The alternative is S3-compatible object storage, which keeps the server stateless and makes the backup story simpler:
"FileSettings": {
"DriverName": "amazons3",
"AmazonS3Bucket": "mattermost-files",
"AmazonS3Endpoint": "s3.example.com",
"AmazonS3SSL": true
}
Our guide to self-hosting MinIO covers running that endpoint yourself if you would rather not use a third party.
A backup needs three things, and two of them are commonly forgotten. The database, which is the obvious one. The files directory, without which the database references uploads that no longer exist. And config.json, which holds every setting you spent an afternoon getting right.
pg_dump -U mmuser -h 127.0.0.1 mattermost | gzip > /backup/mm-$(date +%F).sql.gz
tar czf /backup/mm-data-$(date +%F).tar.gz /opt/mattermost/data /opt/mattermost/config
Then test a restore into a scratch instance. An untested backup of a chat server is discovered to be incomplete at the worst possible time, because chat history is exactly the data nobody can reconstruct.
Mobile Push Notifications
This is the part that surprises self-hosters. Push notifications to iOS and Android cannot be sent directly by your server, because they require signed credentials for Apple and Google that belong to whoever published the app. Mattermost therefore routes them through a push proxy.
You can use the proxy Mattermost operates, or run mattermost-push-proxy yourself, which requires building and publishing your own mobile apps with your own certificates. Most teams use the hosted proxy and accept that notification metadata passes through it. If that is unacceptable on privacy grounds, budget real effort for the alternative rather than treating it as a configuration option.
Hardening
Turn off open registration unless you want the internet joining your team. In the System Console, restrict account creation to specific email domains or disable it entirely and invite people explicitly. Enable multi-factor authentication, and enforce it for administrators at minimum.
Then close everything except the proxy and SSH:
ufw allow 22/tcp
ufw allow 443/tcp
ufw allow 80/tcp
ufw enable
Keep PostgreSQL bound to localhost. A chat server holds a complete record of internal conversation, which makes its database a higher-value target than most people treat it as.
What You Give Up Against Slack
Being straight about this saves a disappointing rollout. The Team Edition omits features that Slack users assume: compliance export, custom retention policies, guest accounts, SAML single sign-on and Active Directory sync are Enterprise. The third-party app ecosystem is far smaller, so an integration that exists as a Slack app may need building against the Mattermost API.
What you get is the full message history in a database you control, no per-user monthly fee, and no vendor deciding to delete messages older than 90 days on a free plan. For most teams that trade is favourable, and it is worth making deliberately rather than discovering the gaps in week three.
Infrastructure for a System People Depend On
Team chat becomes load-bearing quickly. Once it carries incident coordination and decisions, an outage is not an inconvenience, it is the tool your team needs during the outage being unavailable.
Every MassiveGRID VPS runs on a Proxmox high-availability cluster with automatic failover, so a hardware fault migrates the workload rather than ending it. Storage is Ceph with three-way replication across independent NVMe drives, which is the protection that matters for a message archive nobody can recreate. CPU, RAM and storage scale independently, so the storage growth described above does not force you to buy cores you will not use.
Deploy a Cloud VPS from $1.99/mo, or read our sizing guide for self-hosted applications before choosing a plan.