TIL: Setting up a Mastodon Instance

, Jochen

This was easier than expected. For the most part, I followed the official "Installing from source" documentation. But there were some differences, too. Therefore writing this up might be useful for someone with a similar setup.

The Stack I Used

  • Ansible to set up the user account / database / config files
  • Traefik as a reverse proxy load-balancer also handling SSL termination and gzip compression

The Boring Stuff

Surprisingly deploying Mastodon is very similar to deploying a Django application. Which might explain why it took me less time than expected. For Django projects, I also often have to deploy NodeJS, because it's some SPA in front of a JSON API. And installing Ruby is also similar to how I install Python: build it from source.

Mastodon is deployed by an ansible playbook executing the following tasks:

  • Install all required system packages like ImageMagick / libjemalloc-dev...
  • Create a separate Unix user to isolate the instance
  • Create a dedicated Postgres database and user
  • Use nvm to install the latest v16 node version (maybe it's safe to use a newer one?)
  • Use rbenv to install ruby 3.0.4 (maybe use a newer one?)
  • Clone the mastodon repository and install all the required gems / run database migrations / build static assets
  • Create a .env.production file from a template
  • Create a dynamic config for traefik
  • Create systemd unit files for sidekiq, web, and streaming

Then, there's a second playbook that removes all things the deploy playbook created. It's really useful to have a playbook like this to be able to test whether the complete deployment works from scratch after changes.

And then there are two playbooks dealing with backup / restore of the database. Restore is necessary because you don't know whether your backup works if you haven't tried to restore it yet 😁. In this case, it all worked pretty seamlessly using just the standard ansible community.general.postgresql_db module with backup and restore states.

As I said, this is all pretty standard, pretty boring stuff. And I'm bad at writing ansible playbooks so I'm a little bit hesitant to publish those. But if you want to see them: drop me a line via Email or Mastodon.

Traefik Configuration

The traefik config is a little bit different from other web services usually deployed because there are two services to which traefik should load-balance to:

Here's my ansible template to create the config. This blog post was helpful to get me started.

File Serving

In the original documentation, Nginx was used to serve files like images or videos. But for an instance with not much traffic, it's probably fine to use the built-in file-serving capabilities of puma the application server that is used to run RoR. The only configuration parameter you have to set is:

Environment="RAILS_SERVE_STATIC_FILES=true"

In the systemd unit file for the Mastodon web (RoR) service.

Using a Vanity Username

My new Mastodon instance is running at fedi.wersdoerfer.de, but I'd like to have @jochen@wersdoerfer.de as my username. A little bit of configuration is needed to make that happen which I read about in Getting Mastodon running on a custom domain - a blog post by Simon Willison:

LOCAL_DOMAIN=wersdoerfer.de

WEB_DOMAIN=fedi.wersdoerfer.de

Need to be added to the .env.production file. Then I had to make some redirects to make wersdoerfer.de/@jochen point to the right location. I did pretty much the same thing as Andrew Godwin described in his article The Fediverse, And Custom Domains.

The Result

Just a screenshot to remember how the result looked like 🙂:

Mastodon Screenshot

Return to blog