Setup a Mastodon instance on Fedora Server

published on 2022-12-12 by hyperreal

I’ll go through the steps to setup a Mastodon instance on Fedora Server. This guide is based on the original Install from source guide in the Mastodon documentation, but includes Fedora-specific tweaks such as packages, file path differences, and SELinux policies. I’ll first go through prerequistes and basic setup, and then branch off in the style of a choose-your-own-adventure. Section 2 is for installing a new Mastodon instance; Section 3 is for Migrating from an existing Mastodon instance; Section 4 is for setting up Mastodon with Nginx and Certbot; Section 5 is for setting up Mastodon with Caddy; Section 6 covers SELinux policy modules that need to be enabled for some critical services and executables to work.

This guide presumes the following: - Vanilla Fedora 37 Server install with SELinux in enforcing mode, fail2ban, and firewalld with the HTTP/S ports open. - Mastodon version 4.0.2 - You have a domain name for hosting the Mastodon instance. - You know how to configure SMTP, should you want it.

I’ll come back and update the guide as necessary for new releases of the software.

Prerequisites and basic setup

Become the root user and install the following packages:

dnf install postgresql-server postgresql-contrib ImageMagick ImageMagick-devel ffmpeg-free ffmpeg-free-devel libpq libpq-devel libxml2-devel libxslt-devel file git-core '@c-development' '@development-tools' protobuf-devel pkgconf-pkg-config nodejs bison openssl-devel libyaml-devel readline-devel zlib-devel ncurses-devel libffi-devel gdbm-devel redis libidn-devel libicu-devel jemalloc-devel perl-FindBin

Install corepack and set the yarn version:

npm install -g corepack
corepack enable
yarn set version classic

Add the mastodon user, then switch to it:

adduser -m -U mastodon
su - mastodon

As the mastodon user, install rbenv and rbenv-build:

git clone https://github.com/rbenv/rbenv.git ~/.rbenv
cd ~/.rbenv
src/configure
make -C src
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec bash
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build

Install the required Ruby version:

RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 3.0.6
rbenv global 3.0.6

Install bundler:

gem install bundler --no-document

Return to the root user:

exit

Setup postgresql:

postgresql-setup --initdb --unit postgresql
systemctl enable --now postgresql

Become the postgresql user and run psql:

su - postgres
psql

In the psql prompt, create the database role:

CREATE USER mastodon CREATEDB;
\q

Go back to the root user:

exit

Become the mastodon user:

su - mastodon

Check out the Mastodon code:

git clone https://github.com/mastodon/mastodon.git live
cd live
git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)

Install Ruby and JavaScript dependencies:

Note that the version of NodeJS installed on Fedora uses OpenSSL3, but Mastodon requires a version of Node with OpenSSL1.1. We can remedy this by using export NODE_OPTIONS–openssl-legacy-provider=.

bundle config deployment 'true'
bundle config without 'development test'
bundle install -j$(getconf _NPROCESSORS_ONLN)
export NODE_OPTIONS=--openssl-legacy-provider
yarn install --pure-lockfile

This is the end of the prerequisites and basic setup. Choose one of the options below to continue.

Install a new Mastodon instance

After running the steps from the previous section, we can now run the interactive setup wizard to setup a new Mastodon instance:

cd /home/mastodon/live
export NODE_OPTIONS=--openssl-legacy-provider
RAILS_ENV=production bundle exec rake mastodon:setup

This will: * Create a configuration file * Run asset precompilation * Create the database schema

Choose one of the options below to continue.

Migrate from an existing Mastodon instance

After running the steps from the prerequisites and basic setup section, we can now start migrating the data from an existing Mastodon instance.

Run these commands on the old server machine

Stop the mastodon systemd services:

systemctl stop mastodon-web mastodon-sidekiq mastodon-streaming

Become the mastodon user:

su - mastodon

Dump the postgresql database to /home/mastodon/mastodon_production.dump:

pg_dump -Fc mastodon_production -f mastodon_production.dump

Copy the following files from the old server machine to the same paths on the new server machine using rsync or whatever method you think best:

  • /home/mastodon/live/public/system directory, which contains

user-uploaded images and videos. This is not required if you’re using S3.

  • /home/mastodon/live/.env.production, which contains the server

config and secrets. * /home/mastodon/mastodon_production.dump

  • Your web server configuration

Run these commands on the new server machine

Ensure the Redis server is started:

systemctl enable --now redis

Become the mastodon user:

su - mastodon

Create an empty database:

createdb -T template0 mastodon_production

Import the postgresql database:

pg_restore -Fc -U mastodon -n public --no-owner --role=mastodon -d mastodon_production mastodon_production.dump

Precompile Mastodon’s assets:

cd live
export NODE_OPTIONS=--openssl-legacy-provider
RAILS_ENV=production bundle exec rails assets:precompile

Rebuild the home timelines for each user:

RAILS_ENV=production ./bin/tootctl feeds build

Go back to root user:

exit

As root, start the Mastodon systemd services:

systemctl enable --now mastodon-web mastodon-sidekiq mastodon-streaming

You can now update your DNS settings to point to the new server machine, rerun Certbot to update LetsEncrypt, etc. If you still need a web server setup, you can choose one of the options below, otherwise you can continue with section 6. SELinux.

Setup with Nginx and Certbot

The Mastodon repository provides an Nginx configuration. On Fedora, the Nginx configuration path is /etc/nginx/conf.d.

Become root on your Fedora Server and install Nginx and Certbot:

dnf install nginx certbot python3-certbot-nginx

Copy the Nginx configuration:

cp -v /home/mastodon/live/dist/nginx.conf /etc/nginx/conf.d/mastodon.conf

Edit /etc/nginx/conf.d/mastodon.conf and change example.com in the server_name directive to your Mastodon domain. You can make any other adjustments you need.

Ensure the syntax of the Nginx configuration is okay:

nginx -t

To acquire an SSL certificate, ensure the HTTP ports are open in your firewall:

firewall-cmd --zone=FedoraServer --permanent --add-service=http
firewall-cmd --zone=FedoraServer --permanent --add-service=https
firewall-cmd --reload

Now run Certbot to obtain the certificate (change example.com to your domain):

certbot --nginx -d example.com

Enable and start Nginx:

systemctl enable --now nginx.service

You can now go to section SELinux

Setup with Caddy

Add the Caddy repository and install Caddy:

dnf install 'dnf-command(copr)'
dnf copr enable @caddy/caddy
dnf install caddy

Create or edit the Caddyfile at /etc/caddy/Caddyfile:

example.com {
    @local {
	file
	not path /
    }
    @local_media {
	path_regexp /system/(.*)
    }
    @streaming {
	path /api/v1/streaming/*
    }
    @cache_control {
	path_regexp ^/(emoji|packs|/system/accounts/avatars|/system/media_attachments/files)
    }

    root * /home/mastodon/live/public
    log {
	output file /var/log/caddy/mastodon.log
    }

    encode zstd gzip

    handle_errors {
	rewrite 500.html
	file_server
    }

    header {
	Strict-Transport-Security "max-age=31536000"
    }
    header /sw.js Cache-Control "public, max-age=0"
    header @cache_control Cache-Control "public, max-age=31536000, immutable"

    handle @local {
	file_server
    }

    reverse_proxy @streaming {
	to http://localhost:4000

	transport http {
	  keepalive 5s
	  keepalive_idle_conns 10
	}
    }

    reverse_proxy {
	to http://localhost:3000

	header_up X-Forwarded-Port 443
	header_up X-Forwarded-Proto https

	transport http {
	  keepalive 5s
	  keepalive_idle_conns 10
	}
    }
}

To allow Caddy to access files in the user home directory, the executable bit needs to be set on the parent directories of the files being served:

chmod +x /home/mastodon/live/public
chmod +x /home/mastodon/live
chmod +x /home/mastodon
chmod +x /home

You can now go to section SELinux

SELinux

At this point, a web server should be running, but if SELinux is in enforcing mode, you will get a 502 Bad Gateway error if you try to browse to your Mastodon domain. The problem is that SELinux is not allowing the web server daemon to access files in /home/mastodon/live. This can be verified by running:

ausearch -m AVC -ts recent

Nginx

This can be fixed by setting the following SELinux booleans:

setsebool -P httpd_read_user_content=1
setsebool -P httpd_enable_homedirs=1

Caddy

You’ll need to set an SELinux policy to allow Caddy to write to /var/log/caddy:

module caddy 1.0;

require {
    type httpd_log_t;
    type httpd_t;
    class file write;
}

#============= httpd_t ==============
allow httpd_t httpd_log_t:file write;

Save this to a file named caddy.te. Now check, compile, and import the module:

checkmodule -M -m -o caddy.mod caddy.te
semodule_package -o caddy.pp -m caddy.mod
semodule -i caddy.pp

Set the SELinux booleans for httpd:

setsebool -P httpd_read_user_content=1
setsebool -P httpd_enable_homedirs=1

Restart Caddy.

bundle

SELinux also denies the /home/mastodon/.rbenv/shims/bundle executable. This can be verified by looking at journalctl -xeu mastodon-web.service and ausearch -m AVC -ts recent.

You’ll need the following SELinux policy for bundle to work:

module bundle 1.0;

require {
    type init_t;
    type user_home_t;
    class file { execute execute_no_trans open read };
}

#============= init_t ==============
allow init_t user_home_t:file { execute execute_no_trans open read };

Save this to a file named bundle.te. Now check, compile, and import the module:

checkmodule -M -m -o bundle.mod bundle.te
semodule_package -o bundle.pp -m bundle.mod
semodule -i bundle.pp

Restart the Mastodon systemd services:

systemctl restart mastodon-web mastodon-streaming mastodon-sidekiq

Your Mastodon instance should now be up and running!

Closing

If you have any questions, want to report any errors, or have any suggestions for improving this article, you can find me at the following places. You can also open up an issue in the GitHub interface.