- Jonathan Haack
- Haack’s Networking
This tutorial is for users of Debian GNU/Linux who want to set up a mastodon instance. This particular instance runs in a VM (4-core / 16GB RAM / 1TB) for which the underlying host OS setup and hardware is outlined here: https://wiki.haacksnetworking.org/doku.php?id=computing:vmserver . I used Mastodon’s official docs and also relied fairly heavily on Linux Babe’s https://www.linuxbabe.com/debian/install-mastodon-debian-server whose use cases are often similar to my own. Alright, first install postgres SQL:
echo "deb [signed-by=/etc/apt/keyrings/postgresql.asc] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" | sudo tee /etc/apt/sources.list.d/pgdg.list
sudo mkdir -p /etc/apt/keyrings/
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo tee /etc/apt/keyrings/postgresql.asc
sudo apt update
sudo apt install -y postgresql postgresql-contrib
Once it is installed, create a database for mastodon:
sudo -u postgres -i psql
Once the database is created, we need to install ruby/gems, which is the platform mastodon uses along with nginx to serve content on the web. This includes creating a mastodon user to perform gem/rake operations. Also, the install method clones a git repo and builds the instance from source.
sudo apt install ruby ruby-dev
sudo adduser mastodon --system --group --disabled-login
sudo apt install git
git clone https://github.com/tootsuite/mastodon.git
sudo mv mastodon/ /var/www/
sudo chown mastodon:mastodon /var/www/mastodon/ -R
sudo -u mastodon git checkout v3.5.3
sudo gem install bundler
curl -sL https://deb.nodesource.com/setup_16.x | sudo bash -
sudo apt install nodejs
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
sudo apt update
sudo apt -y install yarn
That’s the ruby/gem toolchain there, and now install dependencies and libraries required for compiling the project from source:
sudo apt install redis-server optipng pngquant jhead jpegoptim gifsicle nodejs imagemagick ffmpeg libpq-dev libxml2-dev libxslt1-dev file g++ libprotobuf-dev protobuf-compiler pkg-config 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
After installing the package dependencies, I created an A record for https://gnulinux.social and then set up a postfix send-only MTA. I also created a PTR record for my domain, an SPF record, and a dmarc record. I have a separate tutorial on how to setup https://wiki.haacksnetworking.org/doku.php?id=computing:spfdkim , so view that if you need help. Typically, you need to contact your ISP or datacenter for help with your PTR records; my provider, https://dashboard.brownrice.com/ ,provides a web panel where I can add them myself. To setup postfix, do the following:
sudo apt install postfix mailutils
sudo nano /etc/postfix/main.cf
systemctl restart postfix
Now that our Mail Transfer Agent is setup, we can switch over to building the ruby/gem bundle. To do that, we first use the mastodon user to perform a ruby bundle configuration prior to building from source. After those three configurations are complete, we run the ruby install script with an environment variable required for the compiling.
sudo -u mastodon bundle config deployment 'true'
sudo -u mastodon bundle config without 'development test'
sudo -u mastodon bundle install -j$(getconf _NPROCESSORS_ONLN)
sudo -u mastodon RAILS_ENV=production bundle exec rake mastodon:setup
Next, we want to use the systemd units that Mastodon provides and change the example web root directory to our production web root directory. You can do this with sed like Linux Babe did, or just edit the files manually is fine too. Once you finish, reload all systemd units and start the three required services.
sudo cp /var/www/mastodon/dist/mastodon.service /etc/systemd/system/ sudo sed -i 's/home\/mastodon\/live/var\/www\/mastodon/g' /etc/systemd/system/mastodon-.service
sudo sed -i 's/home\/mastodon\/.rbenv\/shims/usr\/local\/bin/g' /etc/systemd/system/mastodon-*.service
sudo systemctl daemon-reload
sudo systemctl enable --now mastodon-web mastodon-sidekiq mastodon-streaming
Now that the back end ruby/gem environment is working and operational, you need to setup an nginx reverse proxy that will forward port 80/443 requests to that. To do this, install nginx and use the Mastodon suggested server blocks and edit appropriately in both the 80 and 443 blocks. Note, since the Mastodon template has a 443 block, we must temporarily use the self-signed cert/keys so that the service can start without error. Optionally, you could use cert-only with ACME, but I have trouble with that myself.
sudo apt install nginx
sudo cp /var/www/mastodon/dist/nginx.conf /etc/nginx/conf.d/mastodon.conf
sudo nano /etc/nginx/conf.d/mastodon.conf
sudo mkdir -p /var/nginx/cache/
systemctl restart nginx
Now that nginx is all set up with self-signed certs and the service is running, we can easily set up Let’s Encrypt. Personally, Linux Babe’s ACME command failed for me, so I ran my own trusted command that I’ve used for years and it worked right away.
sudo apt install certbot python3-certbot-nginx
sudo certbot --authenticator standalone --installer apache -d gnulinux.social --pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx"
Make sure to set up a cronjob for the cert to automatically renew:
30 2 * * 1 /usr/bin/certbot renew >> /var/log/le-renew.log
Also, I have four scripts that I run on this instance to keep nginx, postgres, and fail2ban running. I also have a separate script that dumps the entire database daily with a time stamp. If you need help setting up fail2ban, use this https://wiki.haacksnetworking.org/doku.php?id=computing:fail2ban . Here are the scripts currently in use:
- Keep Fail2Ban Running: https://repo.haacksnetworking.org/oemb1905/haackingclub/-/blob/master/scripts/fail2ban-restart.sh
- Keep Postgres Running: https://repo.haacksnetworking.org/oemb1905/haackingclub/-/blob/master/scripts/postgres-restart.sh
- Keep Nginx Running: https://repo.haacksnetworking.org/oemb1905/haackingclub/-/blob/master/scripts/nginx-restart.sh
- Nightly DB Dumps: https://repo.haacksnetworking.org/oemb1905/haackingclub/-/blob/master/scripts/postgres-dump.sh
Also, there’s no point in setting this up unless you have regular backups! In my case, since this is a VM, I just use the same script as I use for all my other instances. That script powers down the VM, and copies a sparse file, then tarballs it. After restarting the VM, my backup workstation pulls down the tarballs (also sparse) on a set schedule, keeping approximately 90 days of restore points. The backup script I use is found here and, of course, this runs on the host OS (not the Mastodon VM instance):
- VM Backup Script: https://repo.haacksnetworking.org/oemb1905/haackingclub/-/blob/master/scripts/sane-image-maker-gnulinux.sh
After doing all of this, I changed the admin password for the user I set up above, configured a few basic settings and visual preferences, and then ran the first backup using the script above. It’s still purring right now and fail2ban is busy blocking bots and trolls! Let’s go and happy hacking!
Remember, all the blog posts I put here do not get updated. Fort the most recent version of this tutorial, including changes, errors found, updates, etc., always visit the corresponding wiki: https://wiki.haacksnetworking.org/doku.php?id=computing:mastodon
Director, Haack’s Networking