Migrating a small-ish community from Slack to Mattermost

06 Nov 2022 – Warsaw

Long story short, the company I founded with two friends is now a part of a bigger company and, as such, it was time to get rid of our old accounts at Google Apps, and get rid of paid Slack.

There’s also the financial consideration. At 30 users, Google Apps is ~$360 and Slack is ~$260, for a total of around $600 month. Tiny amount for a a working business, but a noticeable personal cost when it’s no longer an enterprise but a bunch of colleagues staying in touch. After one year of smooth transition period and integration into the new company, it was time to retire both of those services.

Google Apps is a no-brainer, but tedious: change user’s password, disable 2FA, log as that user, download archive of everything with Takeout (there are some legal requirements to keep data for 5 years), store in a cold, dry, dark place and you’re good.

But Slack wasn’t that easy. It was a good Slack. Since 2016, when we migrated from HipChat, it served not only as a communication hub for company business, but it hosted a lot of other activity. Exchanging memes and copypastas, opinions on local car services and restaurants, tips about raising a baby, dog or growing plants. Its searchable history is simply precious. A year after joining the large company, our old Slack is still seeing some activity, as some memes are either non-english or so spicy they just wouldn’t fly on the corporate Slack without risk of getting HR involved.

When your joke is so good that there is a special meeting with HR and managing director just to talk about it!

My search has begun some time ago for a self-hosted and open-source Slack alternative, one that would offer the most similar user experience and have a working Slack import. Mattermost seemed like the best fit. I first started with Mattermost cloud offering to test things out, and I was really happy with the experience, so it was time to perform the most difficult thing, Slack import. I did the import attempts on a local install first (full one, not dockerised as I had issues with the latter) before moving on to a production environment on a 2-core VPS.

The procedure is very well described in Mattermost’s documentation but there are a few caveats, learnings and edge cases that I’ve gathered, and this blogpost is here to share them. Disclaimer. I know that Mattermost is not a 20 billion dollar company, so I cut them some slack (see what I did there?), those are not complaints. And Mattermost is open-source, so if you don’t like those issues and don’t want to wait, you can always fix them yourself.

1. Perform the data migration in small chunks

Our complete Slack import, with its whole history since 2016, is 26GB with all the assets. Such zip file is quite a chonky boi to move around, upload and process. Mattermost’s error messages on import aren’t very helpful, so you want to keep that feedback loop as short as possible, with a single attempt taking minutes, not hours. Slack offers export of custom date range and Mattermost works well with multiple smaller imports (provided usernames and emails don’t change in between the imports) so keep them small, especially for the initial, experimenting phase, until you get the first one imported successfully. Then I moved to one-year-per-file Slack exports, each around 4-6 GB, and it did the trick.

2. Execute the migration commands to the letter

This one was a rookie mistake that I still made. Because Mattermost’s error messages are not helpful, a null pointer dereference exception (Mattermost backend and tooling are written in Go) will not tell you if the problem was because of memory or because the assets in imported zipfile were not in data/ subdirectory. Yes, there should be a data check before import attempt, see Disclaimer.

3. Unicode characters in channel names get funky

If your Slack channel names have unicode chars in them, you can either rename them to latin for the time of export-and-import, or deal with Mattermost importing them into funky names like c3afdb16, without any indication what was the original name of the channel until you browse the posts inside. I went with renaming Slack channels to latin charset before exporting and recommend you do it as wel.

4. Some unicode content can still throw errors

This only happened for 2016 import file and I frankly did not care that much about our oldest posts, so I did not investigate back then.

5. Slack will export only public channels history

…unless you get an enterprise plan or some legal proceedings documents that justify getting access to private conversations data. I did not want the former and did not have the latter, so I gave everybody a fair warning in advance that if there’s something valuable in private conversations or channels, it’s up to them to archive it manually.

6. Import custom emojis

One of the best features of any Slack, its idiosyncrasy, is the collection of custom emojis that have accrued over the years. Getting those imported into Mattermost is not obligatory but a very nice to have. And here’s how to do it.

First, when creating the Slack Exporter app (or later by editing it), add “emoji” access permission. When it’s time to export your custom emojis, go to emoji.list API documentation section, switch to “tester” tab, enter token of your Slack Exporter application and save generated json somewhere.

This JSON contains a hash of emojis, for each of the tuple key is the emoji call name (like partyparrot) and value is public downloadable url of the emoji file. In order to download all those emojis into files named after the call name, that we’re going to need for bulk importer script, I wrote a simple script in Ruby:

# Download all custom emojis from Slack into a proper filename EMOJINAME.png
require 'json'

f = File.open("slack-custom-emoji-export.json")
s = f.read
j = JSON.parse(s)

def target_filename(name, url)
  "#{name}#{File.extname(url)}"
end
# some are aliases
downloadables = j["emoji"].select{|k,v| !(v =~ /alias/)}
downloadables.each{|k,v| `wget #{v} -O #{target_filename(k,v)}`};nil

You’re going to end up with a lot of .jpg, .png and .gif files in current directory. Now install mmemoji and, after configuring your Mattermost credentials, run mmemoji for each of the extensions:

mmemoji create *.{gif,jpg,png}

Mattermost custom emojis do not support aliases, so if you want to import your aliased emojis you’d need to re-upload the files with new, alternative call names. Since in my use case the amount of aliases was small, I decided to just do that manually.

And that’s all! Our Mattermost installation is alive and kicking, and most importantly includes a fully searchable history of our entire Slack with a very similar experience (uploads, emojis, link previews). I suggest you give Mattermost a try. And if it fits your needs, get a paid plan to support further development!