团队聊天工具看起来很轻,但真正放到生产环境后,最容易踩坑的不是“容器能不能启动”,而是域名、WebSocket、文件存储、移动推送、语音通话和备份能不能一起正常工作。
本文以 Ubuntu 24.04、Mattermost 11.7 ESR、官方 Docker Compose、PostgreSQL 和 Caddy 为例,搭建一套适合中小团队长期使用的私有协作平台。你会完成 HTTPS、管理员初始化、SMTP、移动推送、Calls 端口、备份、恢复和升级,并知道哪些功能不能只靠一台低配 VPS 硬撑。
版本说明:截至 2026 年 9 月,Mattermost 官方 Docker 示例使用 11.7 ESR 系列。生产环境建议固定具体补丁版本并订阅安全公告,不要长期使用滚动 tag。部署前请再次查看 Mattermost 官方 Docker 仓库 的
env.example和发布说明。
Mattermost 可以理解为一套可私有部署的团队消息与协作平台,核心能力包括频道、私聊、文件、搜索、Webhook、Bot、插件、桌面端和移动端。它适合这些场景:
- 团队消息、附件或审计数据不能交给公共 SaaS;
- 内网研发、运维或客户支持团队需要统一沟通入口;
- 希望把 GitLab、Jira、CI/CD、告警和 ChatOps 接入同一个频道;
- 有人能维护 Linux、Docker、数据库、备份和安全更新;
- 愿意先做 10–50 人试运行,再根据真实负载扩容。
如果只是三五个人临时聊天,又没有人负责恢复演练,托管服务通常更省事。自建的价值是数据控制和集成自由,不是“完全没有成本”。VPS、域名、邮件、对象存储、备份空间和运维时间都要算进去。
| 方案 | 更适合什么场景 | 主要优势 | 自建时要承担什么 |
|---|---|---|---|
| Mattermost | 内部协作、研发与运维 ChatOps | 数据可控、Webhook/插件丰富、支持内网 | 服务器、数据库、备份、推送和升级 |
| Slack | 快速开通、外部协作、成熟 SaaS 生态 | 运维负担低、集成多 | 数据与费用受 SaaS 方案约束 |
| Discord | 社区、语音房间、公开用户运营 | 社区体验成熟、上手快 | 不适合作为严格受控的企业数据平台 |
不要只按界面像不像 Slack 来选。真正影响成本的是活跃用户数、附件量、合规要求、移动推送、单点登录和通话规模。
Mattermost 官方给小团队的基线是 1 vCPU、2 GB RAM 可支持最多约 1000 个注册用户,但那是应用级参考值,实际负载会受到活跃人数、插件、搜索、文件上传和通话影响。单机还要同时运行 PostgreSQL、Docker 与 Caddy,建议从下面的配置起步:
| 使用规模 | 建议配置 | 磁盘 | 说明 |
|---|---|---|---|
| 10–30 人试运行 | 2 vCPU、4 GB RAM | 60 GB NVMe | 文字消息为主,少量附件 |
| 30–100 人日常使用 | 4 vCPU、8 GB RAM | 100–200 GB NVMe | 插件、Webhook、较多附件 |
| 100 人以上或高活跃 | 4–8 vCPU、16 GB+ RAM | 独立数据库或对象存储 | 先压测,再拆分应用与数据库 |
磁盘容量不能只看系统安装占用。按团队习惯估算附件增长:
一年附件容量 ≈ 活跃用户数 × 每人每月附件量 × 12 × 1.5 安全系数
50 人团队如果每人每月上传 100 MB,一年原始附件约 60 GB,加上数据库、日志、索引和备份,很快就会超过 100 GB。附件多时,可以后续接入 S3 兼容存储;站内已有 MinIO 对象存储部署教程。
本文使用以下数据路径:
浏览器 / 桌面端 / 手机端
│ HTTPS 443
▼
Caddy(宿主机)
│ 127.0.0.1:8065
▼
Mattermost 容器
│ Docker 内网 5432
▼
PostgreSQL 容器
Calls 媒体:客户端 ── UDP/TCP 8443 ──> Mattermost
公网只需要按功能开放:
22/tcp:SSH,最好限制管理 IP;80/tcp、443/tcp:Caddy 自动签发证书和 Web 访问;8443/udp、8443/tcp:启用 Mattermost Calls 时开放;8065/tcp:只绑定127.0.0.1,不要暴露公网;5432/tcp:只在 Docker 网络内使用。
Calls 的 8443 是媒体端口,不能让 Caddy 的 HTTP 反向代理代转。远程用户所在网络如果屏蔽 UDP,还要评估 TCP 回退或 TURN;通话人数较多时应把媒体处理拆到 RTCD,而不是继续给单机堆配置。
准备一个子域名,例如 chat.example.com,把 A 记录指向 VPS 公网 IPv4。如果添加 AAAA 记录,必须确认 IPv6 也能访问服务器,否则证书签发或客户端连接可能间歇失败。
export MM_DOMAIN="chat.example.com"
getent ahosts "$MM_DOMAIN"
curl -4 https://ifconfig.me
更新系统并设置防火墙:
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install -y ca-certificates curl git openssl ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw allow 8443/tcp
sudo ufw allow 8443/udp
sudo ufw enable
sudo ufw status verbose
如果暂时不使用 Calls,可以不开放 8443,减少攻击面。
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
sudo docker version
sudo docker compose version
生产环境的 healthcheck、日志轮转和资源限制可参考 Docker Compose 生产环境配置清单。
不要用 mattermost-preview 镜像做生产环境。官方明确说明 Preview 模式带有已知密码、关闭邮件、数据不持久化且不支持正常升级。
sudo install -d -o "$USER" -g "$USER" -m 0750 /opt/mattermost
cd /opt/mattermost
git clone https://github.com/mattermost/docker.git
cd docker
cp env.example .env
先检查当前仓库状态和示例配置:
git status --short
git log -1 --oneline
grep -E '^(POSTGRES_IMAGE_TAG|MATTERMOST_IMAGE|MATTERMOST_IMAGE_TAG)=' env.example
生成高强度数据库密码:
openssl rand -base64 36
编辑 /opt/mattermost/docker/.env,至少修改这些值:
DOMAIN=chat.example.com
TZ=Asia/Shanghai
RESTART_POLICY=unless-stopped
POSTGRES_IMAGE_TAG=18-alpine
POSTGRES_USER=mmuser
POSTGRES_PASSWORD=replace-with-a-long-random-password
POSTGRES_DB=mattermost
MATTERMOST_IMAGE=mattermost-team-edition
MATTERMOST_IMAGE_TAG=11.7.9
APP_PORT=8065
CALLS_PORT=8443
MM_SERVICESETTINGS_SITEURL=https://chat.example.com
Team Edition 适合先做自建评估;需要高级权限、合规、生产级移动推送或大规模 Calls 时,再核对 Entry、Professional 与 Enterprise 的功能边界。版本号也不要盲目照抄:部署当天应从官方发布页确认 11.7 ESR 的最新安全补丁。
限制配置文件权限:
chmod 600 .env
官方容器内 Mattermost 用户的 UID/GID 是 2000。目录所有者不正确时,最常见的结果是容器不断重启并提示 permission denied。
cd /opt/mattermost/docker
mkdir -p ./volumes/app/mattermost/{config,data,logs,plugins,client/plugins,bleve-indexes}
sudo chown -R 2000:2000 ./volumes/app/mattermost
数据库目录会由 PostgreSQL 容器初始化,不要把应用目录和数据库目录混成一个挂载点。
官方 docker-compose.without-nginx.yml 会把 8065 发布到宿主机所有地址。这里单独创建一个 Caddy 覆盖文件,让 Web 端口只监听回环地址,同时保留 Calls 媒体端口:
cd /opt/mattermost/docker
cat >docker-compose.caddy.yml <<'YAML'
services:
mattermost:
ports:
- "127.0.0.1:${APP_PORT}:8065"
- "${CALLS_PORT}:${CALLS_PORT}/udp"
- "${CALLS_PORT}:${CALLS_PORT}/tcp"
YAML
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml config >/tmp/mattermost-compose.yml
grep -A12 'ports:' /tmp/mattermost-compose.yml
确认展开后的 8065 HostIp 是 127.0.0.1,再启动:
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml pull
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml up -d
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml ps
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml logs --tail=100 postgres mattermost
本机健康检查:
curl -fsS http://127.0.0.1:8065/api/v4/system/ping
ss -lntup | grep -E ':8065|:8443'
正常情况下,ping 接口会返回包含 status: OK 的 JSON,8065 只监听 127.0.0.1。
sudo apt-get install -y debian-keyring debian-archive-keyring apt-transport-https curl gnupg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list >/dev/null
sudo apt-get update
sudo apt-get install -y caddy
创建 /etc/caddy/Caddyfile:
chat.example.com {
encode zstd gzip
reverse_proxy 127.0.0.1:8065 {
header_up X-Real-IP {remote_host}
header_up X-Forwarded-Proto {scheme}
}
log {
output file /var/log/caddy/mattermost-access.log {
roll_size 100MiB
roll_keep 7
}
}
}
Caddy 的 reverse_proxy 默认支持 WebSocket,不需要手写 Upgrade 规则。校验并重载:
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
sudo systemctl status caddy --no-pager
curl -I https://chat.example.com
curl -fsS https://chat.example.com/api/v4/system/ping
想进一步理解自动 HTTPS、日志和多域名配置,可查看 VPS 用 Caddy 反向代理完全指南。
浏览器打开 https://chat.example.com,创建第一个账号和团队。第一个账号应立即设为系统管理员,并完成以下基础项:
- 设置站点名称、支持邮箱和用户注册策略;
- 关闭不需要的公开注册,优先邀请制或受控域名注册;
- 配置 SMTP,验证邀请、密码重置和提及邮件;
- 检查最大文件大小和允许的文件类型;
- 只安装确实需要的插件,并记录版本和权限;
- 创建普通测试账号,不要只用管理员账号验收。
SMTP 发送失败时,先检查 VPS 是否允许出站 587/465,再查看 Mattermost 日志。很多云厂商默认封锁 25 端口,不要把“容器没报错”当作邮件已经送达。
网页和桌面通知正常,不代表手机锁屏推送正常。自托管环境有三种典型选择:
- Mattermost 托管的 HPNS:面向符合条件的商业方案,具有生产服务承诺;
- 免费 TPNS:适合测试,不建议作为生产通知保证;
- 自建 Push Proxy:需要自行编译匹配的移动 App,并管理 APNs/FCM 凭据。
在 System Console 的 Push Notification Server 中配置后,用两个真实账号测试:账号 A 登录手机并退到后台,账号 B 从浏览器发送私信,再确认手机是否收到通知。对高敏感数据,还要评估推送内容是否经过第三方通知基础设施。
小团队可先使用集成式 Calls。确认防火墙和云安全组都开放 8443 UDP/TCP,然后在 System Console 中检查 Calls 插件配置。
sudo ufw status numbered
sudo ss -lunp | grep ':8443'
sudo ss -ltnp | grep ':8443'
测试时至少准备两个账号和两个不同网络,例如一台电脑走家庭宽带、一部手机走蜂窝网络。只在同一个局域网测试,发现不了 NAT、UDP 屏蔽或公网地址配置问题。
50 人以上频繁通话、需要高可靠媒体或录制时,应根据官方架构部署 RTCD、TURN 或 Calls Offloader。录制、转写和更大规模群组通话可能受商业版本约束,采购 VPS 前先确认所需许可证。
Mattermost 的可恢复备份至少包括:
- PostgreSQL 数据库;
volumes/app/mattermost/config;volumes/app/mattermost/data中的本地附件;- 插件和 Bleve 索引配置;
.env与自定义 Compose 文件;- Caddy 配置和必要的外部存储凭据。
下面做一次一致性更好的离线备份。先停应用,保留 PostgreSQL 运行以导出数据库:
cd /opt/mattermost/docker
set -a
. ./.env
set +a
BACKUP_DIR="/var/backups/mattermost/$(date +%F-%H%M%S)"
sudo install -d -m 0700 "$BACKUP_DIR"
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml stop mattermost
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml exec -T postgres \
pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc >"$BACKUP_DIR/mattermost.pgdump"
sudo tar -C /opt/mattermost/docker -czf "$BACKUP_DIR/mattermost-files.tgz" \
.env docker-compose.caddy.yml volumes/app
sudo cp /etc/caddy/Caddyfile "$BACKUP_DIR/Caddyfile"
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml start mattermost
sudo sha256sum "$BACKUP_DIR"/* | sudo tee "$BACKUP_DIR/SHA256SUMS"
sudo du -sh "$BACKUP_DIR"
/var/backups 还在同一块 VPS 磁盘上,不算灾难备份。完成后应把加密副本同步到另一台服务器或对象存储,并设置保留周期。更完整的轮换思路可参考 VPS 自动备份方案。
不要等主机坏了才第一次运行 pg_restore。在隔离的测试 VPS 上完成以下流程:
- 使用相同或兼容版本的 Docker、Mattermost 与 PostgreSQL;
- 恢复
.env、Compose 文件和应用目录; - 启动 PostgreSQL,创建空数据库;
- 使用
pg_restore --clean --if-exists导入数据库; - 恢复文件目录所有者为
2000:2000; - 启动 Mattermost,检查频道、私信、附件、插件和搜索;
- 用测试域名验证登录、上传、下载、邮件和 Calls。
恢复目标不是“命令退出码为 0”,而是用户能读到历史消息并下载原附件。演练后记录恢复耗时,才能知道团队能接受多长停机。
Mattermost 更新频繁,补丁常包含安全修复。升级前先阅读当前版本到目标版本的升级说明,不要跨多个大版本直接拉最新镜像。
cd /opt/mattermost/docker
git fetch --all --tags
git status --short
git diff env.example
完整备份并验证后,修改 .env 中的具体 MATTERMOST_IMAGE_TAG,然后执行:
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml pull mattermost
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml up -d
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml ps
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml logs --tail=200 mattermost
curl -fsS https://chat.example.com/api/v4/system/ping
升级会运行数据库迁移。若迁移已改变数据库结构,只把应用镜像 tag 改回旧版不一定安全;可靠回滚需要同时恢复升级前数据库和文件快照。因此升级窗口必须有可用备份,并提前写好停止条件。
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml ps
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml logs --tail=200 postgres mattermost
sudo namei -l volumes/app/mattermost/config
sudo chown -R 2000:2000 volumes/app/mattermost
重点看数据库密码是否一致、挂载目录权限、磁盘是否已满以及 .env 是否有特殊字符解析问题。
检查浏览器开发者工具中的 WebSocket 连接、Caddy 日志和 Site URL:
sudo journalctl -u caddy -n 100 --no-pager
sudo tail -n 100 /var/log/caddy/mattermost-access.log
grep '^MM_SERVICESETTINGS_SITEURL=' /opt/mattermost/docker/.env
常见原因是 Site URL 仍为 HTTP、DNS 指向旧服务器,或前面还有一层 CDN/代理没有正确支持 WebSocket。
df -h
df -i
sudo du -sh /opt/mattermost/docker/volumes/app/mattermost/data
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml logs --tail=100 mattermost
同时检查 Mattermost 最大文件限制、Caddy 前面的 CDN 限制和目录权限。不要只在 Caddy 里放宽大小,却忘了应用配置。
优先排查 8443 UDP/TCP、云安全组、NAT 公网地址和用户网络是否屏蔽 UDP。Caddy 只负责 443 上的 HTTPS/WebSocket 信令,不转发 8443 的媒体流。
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml exec -T postgres pg_isready -U mmuser -d mattermost
sudo docker compose -f docker-compose.yml -f docker-compose.caddy.yml logs --tail=100 postgres
不要为了排查临时把 5432 开到公网。需要进一步定位时,可参考 PostgreSQL 连接失败排查指南。
- 域名、证书和
/api/v4/system/ping正常; - 8065 只监听 127.0.0.1,5432 没有暴露公网;
- 普通用户可以登录、发消息、上传并下载附件;
- 浏览器和桌面端消息实时刷新;
- SMTP 邀请与密码重置邮件可以送达;
- 移动推送用两个真实账号完成锁屏测试;
- Calls 在两个不同网络间完成语音测试;
- 数据库、附件、配置和密钥都有异机加密备份;
- 已在隔离环境完成一次恢复演练;
- 固定了具体版本,并订阅 Mattermost 安全公告。
第一次部署不要同时开启所有插件、SSO、全文搜索、Calls 录制和对象存储。先让 10–20 人稳定使用两周,记录 CPU、内存、磁盘、数据库大小、附件增长和通话问题,再决定是否扩容或拆分。
真正可靠的 Mattermost 不是“首页能打开”,而是消息实时、手机能提醒、附件可恢复、升级能回滚。把这四项纳入验收,才算从 Docker 演示环境走到可长期维护的团队协作平台。
