TIL: Setting up a Mastodon Instance
,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
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:
- The web service powered by Ruby on Rails
- The streaming service handled by NodeJS
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 🙂: