Mastodon Production Guide
This guide was written for Ubuntu Server 18.04, you may run into issues if you are using another operating system. We are welcoming contributions for guides to other distributions.
This document is also written with the expectation that you have a technical level high enough to administrate Linux servers.
If you need help setting up your instance, you may want to try tooting at the #MastoAdmins hashtag.
What is this guide?
This guide is a walk through of the setup process of a Mastodon instance.
We use example.com to represent a domain or sub-domain. Example.com should be replaced with your instance domain or sub-domain.
You will need the following for this guide:
- A server running Ubuntu Server 18.04.
- Root access to the server.
- A domain or sub-domain to use for the instance.
DNS records should be added before anything is done on the server.
The records added are:
- A record (IPv4 address) for example.com
- AAAA record (IPv6 address) for example.com
A Helpful And Optional Note
tmuxwhen following through with this guide will be helpful.
Not only will this help you not lose your place if you are disconnected, it will let you have multiple terminal windows open for switching contexts (root user versus the mastodon user).
You can install tmux from the package manager:apt -y install tmux
All dependencies should be installed as root.
Extend Ubuntu repositories when using Ubuntu 18.04.1 LTS
Starting with .1-release Ubuntu 18.04.1 LTS (not 18.04), Canonical has removed the multiverse, universe and restricted repository from the sources.list file in /etc/apt/. It is now necessary to add those repositories, otherwise the installation of the following dependencies will fail. Simply run the following commands:
add-apt-repository multiverse add-apt-repository restricted apt update
You will need to add an external repository so we can have the version of node.js required.
We run this script to add the repository:
apt -y install curl curl -sL https://deb.nodesource.com/setup_8.x | bash -
The node.js repository is now added.
This is how you add the repository:
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list apt update
Various Other Dependencies
Now you need to install Yarn plus some more software.
Explanation of the dependencies
- imagemagick - Mastodon uses imagemagick for image related operations
- ffmpeg - Mastodon uses ffmpeg for conversion of GIFs to MP4s
- libprotobuf-dev and protobuf-compiler - Mastodon uses these for language detection
- nginx - nginx is our frontend web server
- redis-* - Mastodon uses redis for its in-memory data structure store
- postgresql-* - Mastodon uses PostgreSQL as its SQL database
- nodejs - Node is used for Mastodon's streaming API
- yarn - Yarn is a Node.js package manager
- Other -dev packages, g++ - these are needed for the compilation of Ruby using ruby-build.
apt -y install 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 libgdbm5 libgdbm-dev nginx redis-server redis-tools postgresql postgresql-contrib certbot yarn libidn11-dev libicu-dev
Dependencies That Need To Be Added As A Non-Root User
Let us create this user first:
Log in as the
sudo su - mastodon
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 # Restart shell exec bash # Check if rbenv is correctly installed type rbenv # Install ruby-build as rbenv plugin git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
To enable Ruby, run:
rbenv install 2.5.1 rbenv global 2.5.1
This will take some time. Go stretch for a bit and drink some water while the commands run.
node.js And Ruby Dependencies
Run the following to clone and install:
# Return to mastodon user's home directory cd ~ # Clone the mastodon git repository into ~/live git clone https://github.com/tootsuite/mastodon.git live # Change directory to ~/live cd ~/live # Checkout to the latest stable branch git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1) # Install bundler gem install bundler # Use bundler to install the rest of the Ruby dependencies bundle install -j$(getconf _NPROCESSORS_ONLN) --deployment --without development test # Use yarn to install node.js dependencies yarn install --pure-lockfile
That is all we need to do for now with the
mastodon user, you can now
exit back to root.
PostgreSQL Database Creation
Create a user for a PostgreSQL instance:
# Launch psql as the postgres user sudo -u postgres psql # In the following prompt CREATE USER mastodon CREATEDB; \q
Note that we do not set up a password of any kind, this is because we will be using ident authentication. This allows local users to access the database without a password.
Reminder: Replace all occurrences of example.com with your own instance's domain or sub-domain.
/etc/nginx/sites-available and open a new file:
Copy and paste the following and make edits as necessary:
Activate the nginx configuration added:
cd /etc/nginx/sites-enabled ln -s ../sites-available/example.com.conf
This configuration makes the assumption you are using Let's Encrypt as your TLS certificate provider.
If you are going to be using Let's Encrypt as your TLS certificate provider, see the
next sub-section. If not edit the
This section is only relevant if you are using Let's Encrypt as your TLS certificate provider.
Generation Of The Certificate
We need to generate Let's Encrypt certificates.
Make sure to replace any occurrence of 'example.com' with your Mastodon instance's domain.
Make sure that nginx is stopped at this point:
systemctl stop nginx
We will be creating the certificate twice, once with TLS SNI validation in standalone mode and the second time we will be using the webroot method. This is required due to the way nginx and the Let's Encrypt tool works.
certbot certonly --standalone -d example.com
After that successfully completes, we will use the webroot method. This requires nginx to be running:
systemctl start nginx # The certbot tool will ask if you want issue a new cert, please choose that option certbot certonly --webroot -d example.com -w /home/mastodon/live/public/
Automated Renewal Of Let's Encrypt Certificate
Let's Encrypt certificates have a validity period of 90 days.
You need to renew your certificate before the expiration date. Not doing so will make users of your instance unable to access the instance and users of other instances unable to federate with yours.
We can create a cron job that runs daily to do this:
Copy and paste this script into that file:
#!/usr/bin/env bash certbot renew systemctl reload nginx
Save and exit the file.
Make the script executable and restart the cron daemon so that the script runs daily:
chmod +x /etc/cron.daily/letsencrypt-renew systemctl restart cron
That is it. Your server will renew your Let's Encrypt certificate.
Mastodon Application Configuration
We will configure the Mastodon application.
For this we will switch to the
mastodon system user:
sudo su - mastodon
Change directory to
~/live and run the Mastodon setup wizard:
cd ~/live RAILS_ENV=production bundle exec rake mastodon:setup
The interactive wizard will guide you through basic and necessary options, generate new app secrets, setup the database schema and precompile the assets.
The assets precompilation takes a couple minutes, so this is a good time to take another break.
Mastodon systemd Service Files
We will need three systemd service files for each Mastodon service.
Now switch back to the root user.
For the Mastodon web workers service place the following in
[Unit] Description=mastodon-web After=network.target [Service] Type=simple User=mastodon WorkingDirectory=/home/mastodon/live Environment="RAILS_ENV=production" Environment="PORT=3000" ExecStart=/home/mastodon/.rbenv/shims/bundle exec puma -C config/puma.rb ExecReload=/bin/kill -SIGUSR1 $MAINPID TimeoutSec=15 Restart=always [Install] WantedBy=multi-user.target
For Mastodon background queue service, place the following in
[Unit] Description=mastodon-sidekiq After=network.target [Service] Type=simple User=mastodon WorkingDirectory=/home/mastodon/live Environment="RAILS_ENV=production" Environment="DB_POOL=5" ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push TimeoutSec=15 Restart=always [Install] WantedBy=multi-user.target
For the Mastodon streaming API service place the following in
[Unit] Description=mastodon-streaming After=network.target [Service] Type=simple User=mastodon WorkingDirectory=/home/mastodon/live Environment="NODE_ENV=production" Environment="PORT=4000" ExecStart=/usr/bin/npm run start TimeoutSec=15 Restart=always [Install] WantedBy=multi-user.target
Now you need to enable all of these services:
systemctl enable /etc/systemd/system/mastodon-*.service
Now start the services:
systemctl start mastodon-*.service
Check that they are properly running:
systemctl status mastodon-*.service
Remote media attachment cache cleanup
Mastodon downloads media attachments from other instances and caches it locally for viewing. This cache can grow quite large if not cleaned up periodically and can cause issues such as low disk space or a bloated S3 bucket.
The recommended method to clean up the remote media cache is a cron job that runs daily like so (put this in the mastodon system user's crontab with
RAILS_ENV=production @daily cd /home/mastodon/live && /home/mastodon/.rbenv/shims/bundle exec rake mastodon:media:remove_remote
That rake task removes cached remote media attachments that are older than NUM_DAYS, NUM_DAYS defaults to 7 days (1 week) if not specified. NUM_DAYS is another environment variable so you can specify it like so:
RAILS_ENV=production NUM_DAYS=14 @daily cd /home/mastodon/live && /home/mastodon/.rbenv/shims/bundle exec rake mastodon:media:remove_remote
If you plan on receiving email notifications or running more than just a single-user instance, you likely will want to get set up with an email provider.
There are several free email providers out there- a couple of decent ones are Mailgun.com, which requires a credit card but gives 10,000 free emails, and Sparkpost.com, which gives 15,000 with no credit card but requires you not be on a .space tld.
It may be easier to use a subdomain to setup your email with a custom provider - in this case, when registering your domain with the email service, sign up as something like "mail.domain.com"
Once you create your account, follow the instructions each provider gives you for updating your DNS records. Once you have all the information ready to go and the service validates your DNS configuration, edit your config file. These records should already exist in the configuration, but here's a sample setup that uses Mailgun that you can replace with your own personal info:
SMTP_SERVER=smtp.mailgun.org SMTP_PORT=587 SMTP_LOGIN=anAccountThatIsntPostmaster@mstdn.domain.com SMTP_PASSWORD=HolySnacksAPassword SMTP_FROM_ADDRESS=Domain.com Mastodon Admin <firstname.lastname@example.org>
Finally, to test this, spin up a Rails console (see the administration guide) and run the following commands to test this out:
m = UserMailer.new.mail to:'email@example.com', subject: 'test', body: 'awoo' m.deliver
That is all! If everything was done correctly, a Mastodon instance will appear when you visit
https://example.com in a web browser.
Congratulations and welcome to the fediverse!