Mastodon Setup

Topics: Mastodon

This note records setup, update, and scaling guidance for a self-hosted Mastodon instance.

Setup steps

Web/Sidekiq node

apt install -y \
  imagemagick ffmpeg libpq-dev libxml2-dev libxslt1-dev file git-core \
  g++ libprotobuf-dev protobuf-compiler pkg-config nodejs gcc autoconf \
  bison build-essential libssl-dev libyaml-dev libreadline6-dev \
  zlib1g-dev libncurses5-dev libffi-dev libgdbm-dev \
  libidn11-dev libicu-dev libjemalloc-dev rbenv nginx

Install latest version of ruby-build:

git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build

PostgreSQL

apt update
apt install -y libpq-dev redis-server redis-tools postgresql postgresql-contrib

Updates

As user toot:

git fetch --tags && git checkout v4.1.16
bundle install
yarn install --frozen-lockfile

As user root:

systemctl list-units | grep -o 'mastodon-.*\.service' | while read service; do echo "$service"; systemctl restart "$service"; done

Tuning

Ruby

Use Ruby version 3.0.4.

Sidekiq

Don’t set your Sidekiq concurrency (threads) greater than 50. You will just slow things down. Instead, add more Sidekiq processes, up to one per CPU. Be sure you allocate enough database connections for that total concurrency.

Setup for smaller instances:

sidekiq -c NUMBER_OF_THREADS -q default,8 -q push,6 -q ingress,4 -q mailers,2 -q pull

Database

Use pgBouncer on all web and job machines to pool database connections.

Puma

set WEB_CONCURRENCY to core count * 1.5 and then tune MAX_THREADS. High thread values will see diminishing returns! (Evan Phoenix, puma author)

These variables are set in /etc/systemd/system/mastodon-web.service.

nginx

Set worker_rlimit_nofile properly:

worker_processes auto;
worker_rlimit_nofile 10000;

events {
    worker_connections 2048;
}

Minio object storage

On the Minio cluster, create the bucket, user, and access policy:

$ mc admin mb main/masto1
$ mc admin user add main masto1 s3cr3t123
$ mc admin policy add main bucket-masto1 masto1-policy.json
$ mc admin policy set main/masto1 bucket-masto1 user=masto1
$ mc anonymous set download main/masto1

The access policy masto1-policy.json for user masto1:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
            "s3:ListBucket",
            "s3:PutObject",
            "s3:GetObject",
            "s3:DeleteObject"
        ],
      "Effect": "Allow",
      "Resource": [
        "arn:aws:s3:::masto1/*", "arn:aws:s3:::masto1"
      ],
      "Sid": "BucketAccessForUser"
    }
  ]
}

Important: Make sure to remove the s3:ListBucket action permission from the anonymous bucket policy.

Sync existing files to object storage:

$ s3cmd --configure
$ s3cmd --acl-public sync ~toot/live/public/system/ s3://masto1/

Reference