hyperreal.coffee

Setup a Mastodon instance on Fedora Server

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:

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

1. Prerequisites and basic setup

Become the root user and install the following packages:

1dnf 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:

1npm install -g corepack
2corepack enable
3yarn set version classic

Add the mastodon user, then switch to it:

1adduser -m -U mastodon
2su - mastodon

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

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

Install the required Ruby version:

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

Install bundler:

1gem install bundler --no-document

Return to the root user:

1exit

Setup postgresql:

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

Become the postgresql user and run psql:

1su - postgres
1psql

In the psql prompt, create the database role:

1CREATE USER mastodon CREATEDB;
2\q

Go back to the root user:

1exit

Become the mastodon user:

1su - mastodon

Check out the Mastodon code:

1git clone https://github.com/mastodon/mastodon.git live
2cd live
3git 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.

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

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

2. 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:

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

This will:

Choose one of the options below to continue.

3. 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:

1systemctl stop mastodon-web mastodon-sidekiq mastodon-streaming

Become the mastodon user:

1su - mastodon

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

1pg_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:

Run these commands on the new server machine

Ensure the Redis server is started:

1systemctl enable --now redis

Become the mastodon user:

1su - mastodon

Create an empty database:

1createdb -T template0 mastodon_production

Import the postgresql database:

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

Precompile Mastodon’s assets:

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

Rebuild the home timelines for each user:

1RAILS_ENV=production ./bin/tootctl feeds build

Go back to root user:

1exit

As root, start the Mastodon systemd services:

1systemctl 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.

4. 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:

1dnf install nginx certbot python3-certbot-nginx

Copy the Nginx configuration:

1cp -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:

1nginx -t

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

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

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

1certbot --nginx -d example.com

Enable and start Nginx:

1systemctl enable --now nginx.service

You can now go to section 6. SELinux

5. Setup with Caddy

Add the Caddy repository and install Caddy:

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

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

 1example.com {
 2    @local {
 3        file
 4        not path /
 5    }
 6    @local_media {
 7        path_regexp /system/(.*)
 8    }
 9    @streaming {
10        path /api/v1/streaming/*
11    }
12    @cache_control {
13        path_regexp ^/(emoji|packs|/system/accounts/avatars|/system/media_attachments/files)
14    }
15
16    root * /home/mastodon/live/public
17    log {
18        output file /var/log/caddy/mastodon.log
19    }
20
21    encode zstd gzip
22
23    handle_errors {
24        rewrite 500.html
25        file_server
26    }
27
28    header {
29        Strict-Transport-Security "max-age=31536000"
30    }
31    header /sw.js Cache-Control "public, max-age=0"
32    header @cache_control Cache-Control "public, max-age=31536000, immutable"
33
34    handle @local {
35        file_server
36    }
37
38    reverse_proxy @streaming {
39        to http://localhost:4000
40
41        transport http {
42          keepalive 5s
43          keepalive_idle_conns 10
44        }
45    }
46
47    reverse_proxy {
48        to http://localhost:3000
49
50        header_up X-Forwarded-Port 443
51        header_up X-Forwarded-Proto https
52
53        transport http {
54          keepalive 5s
55          keepalive_idle_conns 10
56        }
57    }
58}

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:

1chmod +x /home/mastodon/live/public
2chmod +x /home/mastodon/live
3chmod +x /home/mastodon
4chmod +x /home

You can now go to section 6. SELinux

6. 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:

1ausearch -m AVC -ts recent

6.1 Nginx

This can be fixed by setting the following SELinux booleans:

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

6.2 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:

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

Set the SELinux booleans for httpd:

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

Restart Caddy.

6.3 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:

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

Restart the Mastodon systemd services:

1systemctl 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.

#Fedora   #Mastodon   #Fediverse   #Selfhosting