Close these doors before your team logs into Mattermost
A fresh Mattermost install is tuned to get a team talking quickly. The settings to review before day one, and how to prove them from the outside.

A fresh Mattermost install is built to get a team talking in five minutes, so a few settings deserve a decision before the first real user signs in. This is the checklist we work through before a single real user signs in, with the settings by name and a way to prove each one from the outside.
The first account is the dangerous one
On a new Mattermost server, the first person to create an account becomes the system administrator. If the server is reachable from the internet before you have signed in, anyone who finds it first can take it. Scanners find new TLS certificates within minutes of issue, because certificate transparency logs are public and people watch them.
So the order matters: bring the server up on a private address or behind an allow list, create the administrator account yourself, and only then open it to the people who need it.
Right after that, close open sign up. In config.json or with mmctl:
mmctl --local config set TeamSettings.EnableOpenServer false
mmctl --local config set TeamSettings.RestrictCreationToDomains "example.com"
With open server off, new people arrive only by an invitation or by an administrator creating their account. Restricting creation to your own mail domain is a second lock in case an invite link travels further than intended.
Decide what leaves the building in a push notification
Mobile push is the setting most people never look at. When a message arrives for a phone, Mattermost hands a notification to a push proxy, which passes it to the phone platform's notification service. With the default content setting, the text of the message rides along.
For confidential teams, change it:
mmctl --local config set EmailSettings.PushNotificationContents generic_no_channel
generic_no_channel sends "you have a new message" and nothing more. On editions that support it, id_loaded is better still: the notification carries only a message ID, and the phone fetches the content from your own server over TLS. The same thinking applies to email notifications: if your mail relay is outside your control, set email notifications to send the minimum.
Sessions, MFA and the long tail of trust
A few settings decide how long a stolen laptop or a phished password stays useful:
mmctl --local config set ServiceSettings.EnableMultifactorAuthentication true
mmctl --local config set ServiceSettings.EnforceMultifactorAuthentication true
mmctl --local config set ServiceSettings.SessionLengthWebInHours 168
mmctl --local config set ServiceSettings.SessionLengthMobileInHours 720
mmctl --local config set ServiceSettings.SessionIdleTimeoutInMinutes 480
Enforcement of MFA depends on your edition; enabling it is available everywhere and is the minimum. Pick session lengths your users will tolerate, but pick them deliberately. Bots and integrations should use personal access tokens on dedicated bot accounts, not a human's password, so MFA on humans never breaks an integration.
Shut the side doors
Most compromises do not come through the login page. They come through features nobody uses:
mmctl --local config set FileSettings.EnablePublicLink false
mmctl --local config set PluginSettings.EnableUploads false
mmctl --local config set ServiceSettings.EnableInsecureOutgoingConnections false
mmctl --local config set RateLimitSettings.Enable true
Public file links let anyone with a URL fetch an attachment without logging in. Plugin uploads let any administrator account, including a compromised one, run arbitrary code on the server; install plugins from the file system as a change, not from the browser. Insecure outgoing connections allow webhooks and link previews to accept bad certificates. Rate limiting gives the API some resistance to password spraying.
Also check ServiceSettings.AllowCorsFrom. It should be empty unless you have a specific web app that needs it, and then it should name that origin exactly, never a wildcard.
mmctl --local talks to the server over a Unix socket on the machine itself and needs ServiceSettings.EnableLocalMode set to true. Keep local mode on for administration from the server, and keep shell access to that server tight, because local mode skips authentication by design.
Decide who can reach it at all
A team chat is used by a known group of people from a known set of places. The internet at large does not need to reach it. Options from most to least open:
- Public, behind a firewall that drops the networks your scan logs show as sources of abuse.
- Public, limited to the countries your team actually works from.
- Reachable only over your VPN or from your office addresses.
- On a private network only.
The country and provider filters belong at the firewall, not in Mattermost, so unwanted traffic never reaches the application. If you sit behind a reverse proxy, set ServiceSettings.TrustedProxyIPHeader so Mattermost logs and rate limits the real client address instead of your proxy's.
Prove it from the outside
A setting you changed is a hypothesis. A test from outside your network is evidence. From a machine that is not on your network or allow list:
# What does the logged-out client see?
curl -s 'https://chat.example.com/api/v4/config/client?format=old' \
| jq '{EnableOpenServer, EnableSignUpWithEmail, EnableMultifactorAuthentication}'
# Is plain HTTP redirected?
curl -sI http://chat.example.com/ | head -n 3
# Does a public file link work without a session? (use a link created before you disabled them)
curl -s -o /dev/null -w '%{http_code}\n' 'https://chat.example.com/files/<id>/public?h=<hash>'
If you filter by country or provider, test from a few places: a machine in an allowed country, one in a blocked country, one in a cloud provider's range. Do it after every change to the policy, not once at install. Firewall rules drift, and a rule that is wrong in the open direction does not raise an alarm.
Then put the important values under monitoring. A nightly job that reads mmctl config get for the keys above and compares them to the values you chose will tell you the day someone clicks the wrong toggle in the System Console.
Upgrades are part of hardening
Before upgrading, check that your plugins support the target release. Keep upgrades on the release line you intended: an "upgrade to latest" from a package or container tag can move you onto a different branch than you planned.
The short checklist
- Administrator created before the server is reachable.
- Open server off, creation restricted to your domain.
- Push notification contents reduced to generic or ID only.
- MFA enabled, enforced where your edition allows, sensible session lengths.
- Public links, plugin uploads and insecure outgoing connections off; rate limiting on.
- Network exposure chosen on purpose and enforced at the firewall.
- Every setting proven from the outside, and watched for drift.
We run Mattermost this way for our customers on dedicated instances in our own datacenters. If you would like a second pair of eyes on yours, we are happy to help.
Published 22 October 2026.
All field notes