← All case studies Slack → Mattermost

Ingewikkeld imported Slack users and left a decade of messages behind

Ingewikkeld moved from Slack to Mattermost on Nexaa in early 2025. Users and channels imported. A decade of messages hit an upload limit and stayed in an offline archive. The server is a 1 CPU, 2 GB Team Edition container on MySQL.

Stefan wrote this on skoop.dev on 20 February 2026. The switch itself was early the year before. The company is Ingewikkeld. They wanted more control over the chat system, and they wanted one less dependency on a US vendor, given the political climate he names. He had wanted to try Nexaa because friends work there and the serverless containers looked easy. The tag page on his blog has no second Mattermost post.

The plan was a full move. Users, channels, and messages. He says they rarely searched the history, and still wanted it. He also says he missed that goal.

The Slack → Mattermost playbook expects a paid Slack export and a converted archive. That is the path they started. The archive is the part that stopped.

A small container on Nexaa

Mattermost needed a database first. Nexaa’s managed database is a form in the portal. He picked MySQL, one node, because other apps he planned to put on Nexaa use MySQL too. Mattermost also speaks Postgres. He did not use it. One engine for the host was the reason, not a Mattermost limit.

The app is the public image mattermost/mattermost-team-edition. When he wrote the post they were on release-10.12, with an upgrade to Mattermost 11 still on the list. They had started on an older tag. Changing the tag is the upgrade, unless that version also wants a manual step. He looked up Mattermost’s minimum requirements and chose 1 CPU and 2 GB of RAM.

The container is not public until you say so. Public port 80 maps to 8065, which is the port Mattermost listens on. Nexaa terminates TLS on the ingress, so the container does not handle certificates. He set MM_SQLSETTINGS_DATASOURCE to the database DSN, MM_SQLSETTINGS_DRIVERNAME to mysql, and DOMAIN to the public URL. He also set MM_LOGSETTINGS_CONSOLEJSON and MM_LOGSETTINGS_CONSOLELEVEL. The log level was for the first debugging pass. He set it to debug while he was finding what had broken.

Volumes hold the files that have to survive a restart. He made them larger than a year of use required. Nexaa will grow a volume and will not shrink one, so the size you pick first is the size you are stuck with until you need more. After about a year, with about 10 people, the usage was still small. Scaling stays manual, one replica. He says autoscaling is there, and a handful of people do not need it.

On the system console, SMTP goes through their Mailgun account. Without it, signup mail does not send. Open signup is off. Mattermost can hold many teams, each with its own channels, public or private. A team is the closest thing to a Slack workspace. They needed one, named Ingewikkeld.

He did most of this setup on a train from SymfonyCon in Vienna to Klosters. The import is the part that did not fit in that story.

Where the import stopped

A paid Slack workspace can export users, channels, and messages. They converted that export with mmetl on a laptop, which is the tool Mattermost documents for this. The conversion is local. The failure came when the result had to be uploaded.

Ingewikkeld had more than 10 years of Slack. The file was too big for Nexaa’s ingress. He could not raise the upload limit. The import errors stayed. He spent long enough on it to call the time wasted, then stopped.

Users and channels had already loaded. Those were the part he cared about most. The message archive is backed up somewhere they can still search. It is not inside Mattermost. He does not publish the byte size, and he does not say the server went down. The import just never finished.

Mattermost’s current migration guide draws a line Stefan does not mention. Smaller packages, their rough guide is under about 25 GB, go up with mmctl import upload. Larger ones are copied onto the server and processed with mmctl import process --bypass-upload --local. That flag only works with --local. This is the product doc, not his procedure. He does not say he tried it, and a portal upload through Nexaa’s ingress is the path that failed for him.

What broke later

The only downtime he reports is a bad config change he made after they were using it. He fixed it. Day to day they use the web app, or the desktop and phone apps, once DNS points at the container. He assumes you changed DNS. He does not record the record or the TTL.

If the history matters, find the upload limit before you convert a decade of JSON. A host that cannot raise it will take the users and leave the words. Keep the Slack export either way. Stefan still has his, outside the product. The playbook is the conversion. His post is the ceiling that conversion hit.