How to Install Mattermost on Ubuntu 24.04 — Self-Hosted Slack Alternative
Team chat has become the central nervous system of modern companies, and giving that nervous system away to a SaaS vendor is an increasingly expensive decision. Mattermost is an open-source, self-hosted messaging platform built in Go and React that speaks fluent Slack while keeping every message, file and credential on infrastructure you control. This tutorial walks through a production-grade Mattermost Team Edition install on a fresh Ubuntu 24.04 VPS — PostgreSQL 16, systemd, Nginx with WebSocket support, TLS, SMTP, SSO, plugins and backups.
Prefer a managed deployment? Our Professional VPS plan gives you the 6 vCPU / 12 GB / 200 GB NVMe footprint you need for a 200-user Mattermost server at a flat monthly price.
Table of Contents
Why Self-Host a Slack Alternative?
Slack and Microsoft Teams are polished products, but for many teams they are also the single largest recurring SaaS line item after payroll. Mattermost flips the trade-off.
- Data sovereignty. Every message, DM, file and credential lives inside one Postgres database and one data directory. Nothing leaves your jurisdiction. For teams in regulated industries — finance, defence, healthcare, EU public sector — that alone is usually enough to justify self-hosting.
- Unlimited message history. Slack's free tier hides messages older than 90 days, and paid tiers charge per active user per month forever. With Mattermost you keep every message from day one with no retention paywall.
- Predictable cost. A 200-person Slack Business+ deployment costs roughly USD 15/user/month, or about USD 36,000 per year. The same team runs comfortably on a single VPS for under EUR 300 per year. Even at 10x overprovisioning the math does not get close.
- Integration depth. Because you run the server, you can drop webhooks onto the local network, pipe production alerts in from Prometheus, and query PostgreSQL directly for analytics. SaaS tools charge extra for each of those or ratelimit them.
- Open source. You can read the code, audit the security posture, fork the UI and build custom plugins in Go. There is no vendor holding your archive hostage during contract renegotiations.
Prerequisites
Before starting the install, make sure you have:
- Ubuntu 24.04 LTS VPS with root or sudo access.
- A domain name such as
chat.example.comwith an A record pointing to your VPS public IP. TLS will not work without a real domain. - At least 4 GB of RAM and 50 GB disk. For 100+ users plan on 8 GB RAM and 100+ GB NVMe. The Professional VPS plan at 6 vCPU / 12 GB / 200 GB NVMe is a good sweet spot.
- Outbound ports 80, 443 and 25/587 open for Certbot and SMTP.
- Optional SMTP account (Postmark, SES, Brevo, SendGrid, your own Postfix) to send email notifications.
ssh root@your-server-ipStep 1: Prepare the Ubuntu Server
Update the package index, upgrade installed packages and install the prerequisites we will need later.
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl wget tar ufw nginx certbot python3-certbot-nginx \
ca-certificates gnupg lsb-releaseSet the hostname and timezone (recommended — timestamps in a chat server matter):
sudo hostnamectl set-hostname chat.example.com
sudo timedatectl set-timezone UTCOpen the firewall for SSH, HTTP and HTTPS:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enableMattermost itself listens on port 8065 internally. We will keep that port firewalled and only expose it via Nginx.
Step 2: Install and Configure PostgreSQL 16
Mattermost supports PostgreSQL 12+ and MySQL 8, but PostgreSQL 16 is now the officially recommended backend and the one we deploy on all new installs.
Add the official PostgreSQL apt repository:
sudo install -d /usr/share/postgresql-common/pgdg sudo curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \ -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.ascecho "deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] \ https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | \ sudo tee /etc/apt/sources.list.d/pgdg.list
sudo apt update sudo apt install -y postgresql-16 postgresql-contrib-16
Verify the service is up:
sudo systemctl status postgresqlCreate a database and role for Mattermost. Replace StrongDbPassword! with a generated secret (openssl rand -base64 24).
sudo -u postgres psql <<'EOF'
CREATE USER mmuser WITH PASSWORD 'StrongDbPassword!';
CREATE DATABASE mattermost OWNER mmuser;
GRANT ALL PRIVILEGES ON DATABASE mattermost TO mmuser;
\c mattermost
GRANT ALL ON SCHEMA public TO mmuser;
EOFTighten authentication so only local connections from the mmuser role can reach the database. Edit /etc/postgresql/16/main/pg_hba.conf and ensure there is a line:
host mattermost mmuser 127.0.0.1/32 scram-sha-256Reload PostgreSQL:
sudo systemctl reload postgresqlQuick connection test:
psql "host=127.0.0.1 user=mmuser dbname=mattermost password=StrongDbPassword!" -c '\conninfo'You should see You are connected to database "mattermost"....
Step 3: Download Mattermost Team Edition
Mattermost distributes Team Edition as a tarball for stable Linux builds. Fetch the latest release and verify the checksum. At the time of writing the current stable is 10.5, substitute whatever version is marked stable on docs.mattermost.com.
cd /tmp
MM_VERSION=10.5.0
wget https://releases.mattermost.com/${MM_VERSION}/mattermost-team-${MM_VERSION}-linux-amd64.tar.gz
wget https://releases.mattermost.com/${MM_VERSION}/mattermost-team-${MM_VERSION}-linux-amd64.tar.gz.sha256sum
sha256sum -c mattermost-team-${MM_VERSION}-linux-amd64.tar.gz.sha256sumIf the checksum line prints OK, extract and move the tree to /opt/mattermost:
tar -xvzf mattermost-team-${MM_VERSION}-linux-amd64.tar.gz
sudo mv mattermost /opt/
sudo mkdir -p /opt/mattermost/dataThe final layout should look like this:
/opt/mattermost/
├── bin/
├── client/
├── config/
├── data/
├── fonts/
├── i18n/
├── logs/
├── manifest.txt
├── plugins/
├── prepackaged_plugins/
└── templates/Step 4: Create the mattermost System User
Running the server as an unprivileged user is the first line of defence. Create a dedicated mattermost system user and transfer ownership of the install directory.
sudo useradd --system --user-group --home /opt/mattermost --shell /usr/sbin/nologin mattermost
sudo chown -R mattermost:mattermost /opt/mattermost
sudo chmod -R g+w /opt/mattermostConfirm ownership:
ls -ld /opt/mattermost
ls -l /opt/mattermost | headYou should see mattermost mattermost on every entry.
Step 5: Configure config.json
Mattermost reads all runtime settings from /opt/mattermost/config/config.json. The three fields that must be set before first start are SiteURL, DriverName and DataSource.
Open the file:
sudo -u mattermost nano /opt/mattermost/config/config.jsonLocate and update the ServiceSettings and SqlSettings blocks. Keep the rest of the file as shipped — we will tune via the System Console UI later.
{
"ServiceSettings": {
"SiteURL": "https://chat.example.com",
"ListenAddress": ":8065",
"EnableLocalMode": true,
"LocalModeSocketLocation": "/var/tmp/mattermost_local.socket"
},
"SqlSettings": {
"DriverName": "postgres",
"DataSource": "postgres://mmuser:[email protected]:5432/mattermost?sslmode=disable&connect_timeout=10",
"MaxIdleConns": 20,
"MaxOpenConns": 300,
"ConnMaxLifetimeMilliseconds": 3600000
},
"FileSettings": {
"Directory": "/opt/mattermost/data/",
"MaxFileSize": 104857600
},
"LogSettings": {
"EnableConsole": true,
"ConsoleLevel": "INFO",
"EnableFile": true,
"FileLevel": "INFO",
"FileLocation": "/opt/mattermost/logs/"
}
}Notes:
- Use the real domain you will serve on for
SiteURL. Leaving this blank causes CORS, CSRF and email-link problems later. sslmode=disableis fine for loopback PostgreSQL. If you run Postgres on a separate host, switch toverify-fulland provide CA certificates.MaxOpenConns=300is the documented default for multi-user installs. Lower it on 4 GB servers to 100.
cd /opt/mattermost
sudo -u mattermost ./bin/mattermostYou will see a long stream of Running sqlstore migration log lines, followed by:
Server is listening on [::]:8065Press Ctrl+C to stop — we will bring it up properly via systemd next.
Step 6: Create the systemd Service
Create the unit file:
sudo tee /etc/systemd/system/mattermost.service > /dev/null <<'EOF' [Unit] Description=Mattermost After=network.target postgresql.service Requires=postgresql.service[Service] Type=notify ExecStart=/opt/mattermost/bin/mattermost TimeoutStartSec=3600 KillMode=mixed Restart=always RestartSec=10 WorkingDirectory=/opt/mattermost User=mattermost Group=mattermost LimitNOFILE=49152
[Install] WantedBy=multi-user.target EOF
Enable and start:
sudo systemctl daemon-reload
sudo systemctl enable --now mattermost
sudo systemctl status mattermostExpected output:
● mattermost.service - Mattermost
Loaded: loaded (/etc/systemd/system/mattermost.service; enabled; preset: enabled)
Active: active (running) since ...
Main PID: 21234 (mattermost)
Tasks: 34
Memory: 410.0MTail the logs while the server warms up:
sudo journalctl -u mattermost -fWhen you see Server is listening on [::]:8065, confirm locally:
curl -I http://127.0.0.1:8065Step 7: Register the First Admin
Before putting Nginx in front of Mattermost, register the first user. The first account created in a fresh install is automatically granted the System Admin role.
For now, temporarily open port 8065 to your workstation only:
sudo ufw allow from YOUR.LOCAL.IP.ADDR to any port 8065 proto tcpOpen http://your-server-ip:8065 in a browser. You will see the Mattermost welcome screen. Enter:
- Email address (will receive admin notifications).
- Username (lowercase, no spaces).
- Strong password.
Close the temporary firewall hole:
sudo ufw delete allow from YOUR.LOCAL.IP.ADDR to any port 8065 proto tcpWe will now route all traffic through Nginx on 443.
Step 8: Nginx Reverse Proxy with WebSocket Streaming
Mattermost relies heavily on WebSockets for real-time message delivery. The reverse proxy must forward Upgrade and Connection headers and disable buffering so long-lived streams stay responsive.
Create /etc/nginx/sites-available/mattermost:
sudo tee /etc/nginx/sites-available/mattermost > /dev/null <<'EOF' upstream mattermost_backend { server 127.0.0.1:8065; keepalive 32; }Map for WebSocket upgrade
map $http_upgrade $connection_upgrade { default upgrade; '' close; }HTTP — redirect to HTTPS
server { listen 80; listen [::]:80; server_name chat.example.com; return 301 https://$host$request_uri; }HTTPS
server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name chat.example.com;# Certbot will drop certificates here ssl_certificate /etc/letsencrypt/live/chat.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/chat.example.com/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d;
# Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always; add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
client_max_body_size 100M; proxy_http_version 1.1; proxy_read_timeout 600s; proxy_send_timeout 600s;
# WebSocket endpoint — streaming, no buffering 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_set_header X-Frame-Options SAMEORIGIN;
proxy_buffers 256 16k; proxy_buffer_size 16k; proxy_read_timeout 6000s; proxy_pass http://mattermost_backend; }
# Everything else — HTTP API + static assets location / { proxy_set_header Connection $connection_upgrade; proxy_set_header Upgrade $http_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_set_header X-Frame-Options SAMEORIGIN;
proxy_buffers 256 16k; proxy_buffer_size 16k; proxy_cache_valid 200 2m; proxy_cache_valid 404 1m; proxy_read_timeout 600s;
proxy_pass http://mattermost_backend; } } EOF
Enable the site and remove the default:
sudo ln -sf /etc/nginx/sites-available/mattermost /etc/nginx/sites-enabled/mattermost
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -tDon't reload yet — we need a certificate first.
Step 9: Issue a Let's Encrypt TLS Certificate
Use Certbot's Nginx plugin to obtain and install a certificate in one command:
sudo certbot --nginx -d chat.example.com --redirect --agree-tos -m [email protected] --no-eff-emailCertbot patches the Nginx config with the certificate paths (or leaves yours intact if the paths already match — our config above anticipates that). Reload Nginx:
sudo systemctl reload nginxVisit https://chat.example.com — you should see the Mattermost login page served over HTTPS with a green padlock. Log in as the admin you created in Step 7.
Renewals are automatic via the certbot.timer systemd unit that ships with the package. Verify it:
sudo systemctl list-timers | grep certbotStep 10: Configure SMTP Email
Email notifications, password resets and team invites all require SMTP. In the System Console (avatar menu → System Console → Site Configuration → Notifications → Email), set:
- Enable Email Notifications: true
- Notification Display Name:
Mattermost - Notification From Address:
[email protected] - SMTP Server / Port: your provider (e.g.
smtp.postmarkapp.com/587) - SMTP Username / Password: your API credentials
- Connection Security:
STARTTLS
No errors were reported while sending an email. If it fails, check that outbound port 587 is open on the VPS and that the From address matches an authenticated sender domain in your SMTP provider.You can also drive SMTP entirely from config.json under EmailSettings if you prefer Infrastructure-as-Code:
"EmailSettings": {
"EnableSignUpWithEmail": true,
"SMTPServer": "smtp.postmarkapp.com",
"SMTPPort": "587",
"SMTPUsername": "your-postmark-token",
"SMTPPassword": "your-postmark-token",
"ConnectionSecurity": "STARTTLS",
"FeedbackName": "Mattermost",
"FeedbackEmail": "[email protected]"
}Restart Mattermost after editing JSON directly:
sudo systemctl restart mattermostStep 11: Enable SAML or OIDC Single Sign-On
For teams of more than a handful of users, centralised identity is non-negotiable. Mattermost Team Edition supports GitLab OAuth, Google, Office 365 and generic OpenID Connect out of the box. SAML 2.0 and AD/LDAP are included in Enterprise Edition — for open-source SAML you can front Mattermost with Keycloak and use its OIDC bridge.
Option A: OpenID Connect (recommended, works with Team Edition)
Go to System Console → Authentication → OpenID Connect and set:
- Enable sign-in with OpenID Connect: true
- Discovery Endpoint:
https://keycloak.example.com/realms/company/.well-known/openid-configuration - Client ID:
mattermost - Client Secret: (from your IdP)
- Button Text:
Sign in with SSO
- Client type:
OpenID Connect - Valid redirect URI:
https://chat.example.com/signup/openid/complete - Client authentication:
On
Option B: SAML (Enterprise Edition only)
If you run Mattermost Enterprise, head to System Console → Authentication → SAML 2.0 and upload the identity provider metadata XML. Mattermost generates service provider metadata at /api/v4/saml/metadata.
Step 12: Install Plugins (GitHub, Jira, Incident Response)
Plugins are Mattermost's integration surface. The ecosystem is substantial — GitHub, Jira, Zoom, ServiceNow, PagerDuty, Calls, Playbooks (incident response) and hundreds of community plugins.
Install from the System Console → Plugin Management → Plugin Marketplace. Three plugins we recommend for every engineering team:
com.github.manland.mattermost-plugin-github/github subscriptions add owner/repo pulls,issues,releases
- Get @-mentioned when someone requests review on your PR.jiraplaybooksStart incident, Page on-call, Post-mortem doc), and any engineer can spin up a war room with one slash command: /playbook run sev-1-outage.
- Built-in status updates, timeline and metric export.Enable each plugin from the marketplace. For air-gapped installs, download the .tar.gz from the plugin repo, then Plugin Management → Upload Plugin. Plugins run in the Mattermost process so they inherit the systemd unit's resource limits — no extra services to manage.
Step 13: Move File Storage to S3
Local disk works until your data directory outgrows the VPS. Mattermost supports any S3-compatible object store: AWS S3, Backblaze B2, DigitalOcean Spaces, MinIO, Cloudflare R2.
In System Console → Environment → File Storage, set:
- File Storage System:
amazons3 - Amazon S3 Bucket:
mattermost-prod - Amazon S3 Region:
eu-central-1 - Amazon S3 Access Key ID / Secret: scoped IAM keys with
s3:GetObject,s3:PutObject,s3:DeleteObject,s3:ListBucketon the bucket only - Amazon S3 SSL:
true - Amazon S3 Server-Side Encryption:
true
To migrate existing local files to the new bucket:
cd /opt/mattermost sudo -u mattermost ./bin/mmctl --local export create --attachments
...transfer the generated export to the new bucket viaaws s3 cpor mc mirror
For most teams moving to S3 saves disk cost and simplifies backups — you can now snapshot only the PostgreSQL database for a full point-in-time restore.
Step 14: Automated Backups
A full Mattermost backup has two components:
Create /usr/local/bin/mattermost-backup.sh:
sudo tee /usr/local/bin/mattermost-backup.sh > /dev/null <<'EOF' #!/bin/bash set -euo pipefailBACKUP_DIR=/var/backups/mattermost TS=$(date +%Y%m%d-%H%M%S) mkdir -p "$BACKUP_DIR"
1. PostgreSQL logical dump
PGPASSWORD='StrongDbPassword!' pg_dump -h 127.0.0.1 -U mmuser -d mattermost -F c \ -f "$BACKUP_DIR/mattermost-db-$TS.dump"2. Data directory (skip if using S3)
tar -czf "$BACKUP_DIR/mattermost-data-$TS.tar.gz" -C /opt/mattermost data3. Retention — keep 14 daily backups
find "$BACKUP_DIR" -type f -name 'mattermost-*' -mtime +14 -delete4. Optional — push to S3
aws s3 cp "$BACKUP_DIR/mattermost-db-$TS.dump" s3://my-backups/mattermost/
aws s3 cp "$BACKUP_DIR/mattermost-data-$TS.tar.gz" s3://my-backups/mattermost/
EOF
sudo chmod +x /usr/local/bin/mattermost-backup.sh
Schedule nightly via cron:
echo '15 3 * root /usr/local/bin/mattermost-backup.sh >> /var/log/mattermost-backup.log 2>&1' | \
sudo tee /etc/cron.d/mattermost-backupTo restore, stop Mattermost, drop the database, recreate it, then pg_restore:
sudo systemctl stop mattermost
sudo -u postgres dropdb mattermost
sudo -u postgres createdb mattermost -O mmuser
PGPASSWORD='StrongDbPassword!' pg_restore -h 127.0.0.1 -U mmuser -d mattermost \
/var/backups/mattermost/mattermost-db-YYYYMMDD-HHMMSS.dump
sudo tar -xzf /var/backups/mattermost/mattermost-data-YYYYMMDD-HHMMSS.tar.gz -C /opt/mattermost
sudo chown -R mattermost:mattermost /opt/mattermost/data
sudo systemctl start mattermostRun this restore drill on a scratch VPS at least once a quarter — untested backups are prayers, not backups.
FAQ
Is Mattermost really free for self-hosting?
Yes. Mattermost Team Edition is free and open source under a mix of MIT and AGPL-v3 licences. You can self-host it on your own VPS with no user cap and no message-history cap. Enterprise Edition adds features like SAML, LDAP, compliance export and high-availability clustering — but the core chat experience, plugins, mobile apps and API are all in Team Edition.
How much RAM does Mattermost need?
A small team of up to 50 users runs comfortably on 4 GB of RAM, which fits on an entry-level VPS. For 100–500 users plan on 8–16 GB and move PostgreSQL to a dedicated host once you cross ~500 concurrent connections. For 1000+ users we recommend our Professional VPS (6 vCPU, 12 GB, 200 GB NVMe) for the app tier and a separate managed PostgreSQL.
Can I migrate from Slack to Mattermost?
Yes. Mattermost ships a built-in mmctl import slack CLI (and a web wizard) that imports channels, messages, users and file attachments from a Slack workspace export ZIP. Mentions, timestamps and threaded replies are preserved. The import is one-way — for ongoing sync during a phased migration, run both for a few weeks and archive Slack once adoption hits critical mass.
Does Mattermost support video and voice calls?
Yes. The official Calls plugin provides 1:1 and group voice/video meetings over WebRTC, with screen sharing and recording. It is free and runs inside your Mattermost server — no third-party SaaS involved. For larger webinars or classrooms you can integrate Jitsi or BigBlueButton via a slash-command plugin.
How do I back up Mattermost?
Run pg_dump of the PostgreSQL mattermost database and archive /opt/mattermost/data (or rely on S3 versioning if you moved file storage off-box). A nightly cron with both commands, rotated for 14 days, produces a restorable snapshot. Step 14 of this guide walks through the exact script we ship on customer installs.
Can Mattermost use external storage like S3?
Yes. In System Console → Environment → File Storage, switch from local to amazons3 and provide your bucket, region and access keys. Mattermost works with AWS S3, MinIO, Backblaze B2, DigitalOcean Spaces and Cloudflare R2 — anything S3-compatible. Existing local files can be migrated with mmctl export create --attachments or a one-off rclone copy.
How does Mattermost compare to Rocket.Chat and Zulip?
All three are solid open-source options. Mattermost has the closest parity with Slack, the most polished mobile apps, and the strongest enterprise story (compliance, SAML, playbooks). Rocket.Chat is more flexible for omnichannel customer support (livechat widget, WhatsApp/Telegram bridges). Zulip uses a unique topic-threaded model that teams with long async discussions tend to prefer once they get used to it. For most engineering teams migrating from Slack, Mattermost is the least disruptive choice.
Next Steps
- Install the desktop and mobile apps. Point them at
https://chat.example.com— Mattermost supports iOS, Android, Windows, macOS and Linux with push notifications routed through the Mattermost Push Proxy (hosted, free) or your own. - Wire up incoming webhooks for alerts. Send Prometheus Alertmanager, Sentry, GitHub Actions and your own scripts into a dedicated
#alertschannel. - Set up retention policies. In large teams, Mattermost's built-in message and file retention (Enterprise feature) keeps your database lean.
- Enable rate limiting and abuse protection under System Console → Environment → Rate Limiting, especially if your server is exposed to the open internet.
- Read the official admin documentation at docs.mattermost.com — it is one of the better-maintained open-source docs sites and covers every configuration knob we glossed over here.
Deploy Mattermost on a Production-Ready VPS>
Our Professional VPS plan (6 vCPU, 12 GB RAM, 200 GB NVMe) is sized for 200-user Mattermost deployments out of the box. Flat monthly pricing, unmetered bandwidth, EU and US data centres.>
Launch a Professional VPS and have your Slack alternative running in under an hour.