Installing Mastodon on CentOS 7

Updated on December 28, 2017
Installing Mastodon on CentOS 7 header image

Mastodon is an open source self-hosted microblogging and social networking server. It provides Twitter like features; but rather than being controlled by a single company, it is a federation of decentralized servers connecting together to make an entire system of social networking. A small team can start their Mastodon server instance to communicate among themselves as well as with the other users of the federated community. It also enables you to choose your own privacy and moderation policies and define how you communicate with other users of different servers. The content an individual user creates and subscribes to is hosted on the server where his/her account is created. Users can follow each other even if they are not hosted on the same server. Each user in the Mastodon network has his/her unique name or ID which is in the @username@domain-name.com format. Client applications for Mastodon are available for almost all platforms.

Prerequisites

  • A Vultr CentOS 7 server instance.
  • A sudo user.

Note: We will use social.example.com as the domain name used for the Mastodon instance. Replace all occurrences of social.example.com with your actual domain name you want to use for your Mastodon application.

Update your base system using the guide How to Update CentOS 7, if necessary. Once your system has been updated, proceed to install the required dependencies.

Installing Dependencies

Mastodon requires several dependencies to work. Install the latest version of Node.js, which will be used to compile the JavaScript files.

curl --silent --location https://rpm.nodesource.com/setup_8.x | sudo bash -
sudo yum -y install nodejs

Install Yarn, which is a fast, reliable and secure dependency manager application. We will use Yarn to install the Node.js dependencies.

sudo wget https://dl.yarnpkg.com/rpm/yarn.repo -O /etc/yum.repos.d/yarn.repo
sudo yum -y install yarn

Install Redis. Redis is an in-memory data structure store and cache application.

sudo yum -y install redis

Start Redis and enable it to start at boot automatically.

sudo systemctl start redis
sudo systemctl enable redis

Install a few more dependencies which are required to build the Ruby installation and other dependencies.

sudo yum -y install ImageMagick git libxml2-devel libxslt-devel gcc bzip2 openssl-devel zlib-devel gdbm-devel ncurses-devel autoconf automake bison gcc-c++ libffi-devel libtool patch readline-devel sqlite-devel glibc-headers glibc-devel libyaml-devel libicu-devel libidn-devel

You will also need to install the development tools.

sudo yum -y groupinstall 'Development Tools'

Installing and Configuring PostgreSQL

PostgreSQL is an object relational database system. You will need to add the PostgreSQL repository to your system as the default yum repository contains an older version of the application.

sudo rpm -Uvh https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-centos96-9.6-3.noarch.rpm

Install the PostgreSQL database server.

sudo yum -y install postgresql96-server postgresql96-contrib postgresql96-devel

Initialize the database.

sudo /usr/pgsql-9.6/bin/postgresql96-setup initdb

Edit the pg_hba.conf file to enable MD5 based authentication.

sudo nano /var/lib/pgsql/9.6/data/pg_hba.conf

Find the following lines and change peer to trust and ident to md5.

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     peer
# IPv4 local connections:
host    all             all             127.0.0.1/32            ident
# IPv6 local connections:
host    all             all             ::1/128                 ident

Once updated, the configuration should look like this.

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     trust
# IPv4 local connections:
host    all             all             127.0.0.1/32            md5
# IPv6 local connections:
host    all             all             ::1/128                 md5

Start the PostgreSQL server and enable it to start at boot automatically.

sudo systemctl start postgresql-9.6
sudo systemctl enable postgresql-9.6

Change the password for the default PostgreSQL user.

sudo passwd postgres

Login to the shell as PostgreSQL user.

sudo su - postgres

Create a new PostgreSQL user for Mastodon.

createuser mastodon

PostgreSQL provides the psql shell to run queries on the database. Switch to the PostgreSQL shell by running.

psql

Set a password for the newly created user for Mastodon database and provide the permission to add new databases.

ALTER USER mastodon WITH ENCRYPTED password 'DBPassword' CREATEDB;

Replace DBPassword with a strong password. Exit from the psql shell:

\q

Switch to the sudo user.

exit

Install a few more required PostgreSQL dependencies.

sudo yum -y install libpqxx-devel protobuf-devel

Install Ruby

Create a new user for Mastodon and switch to the newly created user.

sudo adduser mastodon -d /opt/mastodon
sudo su - mastodon

We will install the latest version of Ruby using Ruby Version Manager, or RVM. It is used to install and manage multiple versions of Ruby.

Add the GPG key of RVM to your server.

gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB

Install RVM.

curl -sSL https://get.rvm.io | bash -s stable
source /opt/mastodon/.rvm/scripts/rvm

Now fetch the list of the available versions of Ruby.

rvm list known

You will see the following output.

[mastodon@vultr ~]$ rvm list known
# MRI Rubies
[ruby-]1.8.6[-p420]
[ruby-]1.8.7[-head] # security released on head
[ruby-]1.9.1[-p431]
[ruby-]1.9.2[-p330]
[ruby-]1.9.3[-p551]
[ruby-]2.0.0[-p648]
[ruby-]2.1[.10]
[ruby-]2.2[.7]
[ruby-]2.3[.4]
[ruby-]2.4[.1]
ruby-head

...

Now install the latest version of Ruby from the list.

rvm install 2.4

Use the installed version of Ruby.

rvm use 2.4

You should be able to verify its version.

ruby -v

You should see a similar output.

[mastodon@vultr ~]$ ruby -v
ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-linux]

Install bundler, which is the dependency manager for the Ruby application.

gem install bundler

Now switch to the home directory of the Mastodon user and clone the application repository files from Github.

cd ~
git clone https://github.com/tootsuite/mastodon.git app

Move the application directory and checkout the latest releases of the application.

cd ~/app
git checkout $(git describe --tags `git rev-list --tags --max-count=1`)

Configure the bundle to use a custom path for the PostgreSQL configuration. Also, install the Ruby dependencies.

bundle config build.pg --with-pg-config=/usr/pgsql-9.6/bin/pg_config
bundle install --deployment --without development test

Install the Node.js dependencies using Yarn.

yarn install --pure-lockfile

You will need to create a configuration file for the Mastodon application. Copy the sample configuration file.

cd ~/app
cp .env.production.sample .env.production

Before editing the file, generate three different secrets by running the following command three times. You will need to set these secrets in the configuration file.

RAILS_ENV=production bundle exec rake secret

You should get a similar output.

[mastodon@vultr app]$ RAILS_ENV=production bundle exec rake secret
0f17dab1cf4a07f6fac671ecd5815adcb59d012b338dae9350c66402250c6c729dccd6182b1a8f75c4fde55453ce283ea66e07ed4466cdc6d4d6974c98512967

Edit the configuration file using the nano editor.

nano .env.production

Find the following lines and change the values accordingly.

REDIS_HOST=127.0.0.1
REDIS_PORT=6379

Provide the database credentials, according to database user you have created during installation of PostgreSQL. Provide any name for database name as Mastodon will automatically create the database with the name you provide.

DB_HOST=127.0.0.1
DB_USER=mastodon
DB_NAME=mastodon
DB_PASS=DBPassword
DB_PORT=5432

Set the domain name of the application.

LOCAL_DOMAIN=social.example.com

Find the following lines and set its value to the secrets you have generated.

PAPERCLIP_SECRET=
SECRET_KEY_BASE=
OTP_SECRET=

Save the file and exit from the editor.

Now, run the following command to generate Web Push VAPID keys. You will get an error while generating the Web Push VAPID keys if you have not set the secrets in the configuration file mentioned above.

RAILS_ENV=production bundle exec rake mastodon:webpush:generate_vapid_key

You should see the following output.

[mastodon@vultr app]$ RAILS_ENV=production bundle exec rake mastodon:webpush:generate_vapid_key
VAPID_PRIVATE_KEY=DCMQdSPkdm-mepsNh4F3suc-UIvsABCvwFd03jSVemM=
VAPID_PUBLIC_KEY=BMMVcVXmqnV0C8S_ybZ7eQH-MXBEX2exqfdPSYQiMMUF2rRxfhoEtVF931i26ebMgmslHB_nvVadOdcBPhaFEjw=

Open the configuration file again and set the generated keys in their respective lines.

VAPID_PRIVATE_KEY=
VAPID_PUBLIC_KEY=

Finally, provide the information of your SMTP mail server so that the application can send emails to users. It is important that you use a working SMTP server as, upon new registration, users will be sent an email with an activation link. New users will have to click on the activation link to activate their account.

SMTP_SERVER=mail.example.com
SMTP_PORT=456
SMTP_LOGIN=mail@example.com
SMTP_PASSWORD=MailPassword
SMTP_FROM_ADDRESS=notifications@example.com
SMTP_TLS=true

Once done, save the file and exit from the editor. You have done the basic configuration of Mastodon instance.

Setup the PostgreSQL database for the first time.

RAILS_ENV=production bundle exec rails db:setup

First, the above command will create a new database with the name mentioned in the configuration. Then, it will write the database according to the configuration.

Precompile all CSS and JavaScript files to create the cache.

RAILS_ENV=production bundle exec rails assets:precompile

Mastodon is now installed on your server. Now you will need to setup an Nginx web server to serve the application and setup Systemd services to run the Mastodon services.

From now on, we will need to execute the commands using the sudo user.

Install Nginx with Let's Encrypt SSL

We will use the Nginx web server as a reverse proxy to serve the application to the users. We will also obtain and install SSL certificates from Let's Encrypt.

Install Nginx and Certbot. Certbot is the official certificates issuing client for Let's Encrypt CA.

sudo yum -y install nginx certbot

Adjust your firewall setting to allow the standard HTTP and HTTPS ports through the firewall as Certbot needs to make an HTTP connection for verifying the domain authority.

sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-service=https
sudo firewall-cmd --reload

Note: To obtain certificates from Let's Encrypt CA, you must ensure that the domain for which you wish to generate the certificates is pointed towards the server. If not, then make the necessary changes to the DNS records of your domain and wait for the DNS to propagate before making the certificate request again. Certbot checks the domain authority before providing the certificates.

Now use the builtin web server in Certbot to generate the certificates for your domain.

sudo certbot certonly --standalone -d social.example.com

The generated certificates are likely to be stored in the /etc/letsencrypt/live/social.example.com/ directory. The SSL certificate will be retained as fullchain.pem and the private key will be saved as privkey.pem.

Let's Encrypt certificates expire in 90 days, so it is recommended to set up auto renewal for the certificates using Cron jobs. Cron is a system service which is used to run periodic tasks.

Open the cron job file.

sudo crontab -e

Add the following line.

0 0 * * * /usr/bin/certbot renew --quiet

The above cron job will run daily at midnight. If the certificate is due for expiry, it will automatically renew the certificates.

Now create a new Nginx server block for the Mastodon site.

sudo nano /etc/nginx/conf.d/mastodon.conf

Populate the file with this.

map $http_upgrade $connection_upgrade {
  default upgrade;
  ''      close;
}

server {
  listen 80;
  listen [::]:80;
  server_name social.example.com;
  return 301 https://$host$request_uri;
}

server {
  listen 443 ssl http2;
  listen [::]:443 ssl http2;
  server_name social.example.com;

  ssl_protocols TLSv1.2;
  ssl_ciphers HIGH:!MEDIUM:!LOW:!aNULL:!NULL:!SHA;
  ssl_prefer_server_ciphers on;
  ssl_session_cache shared:SSL:10m;

  ssl_certificate     /etc/letsencrypt/live/social.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/social.example.com/privkey.pem;

  keepalive_timeout    70;
  sendfile             on;
  client_max_body_size 0;

  root /opt/mastodon/app/public;

  gzip on;
  gzip_disable "msie6";
  gzip_vary on;
  gzip_proxied any;
  gzip_comp_level 6;
  gzip_buffers 16 8k;
  gzip_http_version 1.1;
  gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

  add_header Strict-Transport-Security "max-age=31536000";

  location / {
    try_files $uri @proxy;
  }

  location ~ ^/(emoji|packs|system/accounts/avatars|system/media_attachments/files) {
    add_header Cache-Control "public, max-age=31536000, immutable";
    try_files $uri @proxy;
  }

  location @proxy {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";
    proxy_pass_header Server;

    proxy_pass http://127.0.0.1:3000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  location /api/v1/streaming {
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Proxy "";

    proxy_pass http://127.0.0.1:4000;
    proxy_buffering off;
    proxy_redirect off;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    tcp_nodelay on;
  }

  error_page 500 501 502 503 504 /500.html;
}

Be sure to change the domain name and the path to the SSL certificates.

Now you can start the Nginx web server and enable it to start at boot automatically.

sudo systemctl start nginx
sudo systemctl enable nginx

You will also be required to allow port 4000 through the firewall as the port will be providing the streaming API service.

sudo firewall-cmd --permanent --zone=public --add-port=4000/tcp
sudo firewall-cmd --reload

Provide the execution permissions for the Mastodon directory.

sudo chmod +x /opt/mastodon

Setting Up Systemd Service

You will need to start three different processes to start the Mastodon server successfully. Using the Systemd service will ensure that the Mastodon server is automatically started at boot and processes failures.

Create a new systemd service to run the Mastodon web service. This service will launch the built-in web server on port 3000.

sudo nano /etc/systemd/system/mastodon-web.service

Populate the file with the following content.

[Unit]
Description=Mastodon Web Service
After=network.target

[Service]
Type=simple
User=mastodon
Group=mastodon
WorkingDirectory=/opt/mastodon/app
Environment="RAILS_ENV=production"
Environment="PORT=3000"
ExecStart=/bin/bash -lc 'bundle exec puma -C config/puma.rb'
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target

Create the second Systemd service to run the Mastodon background queue service. The Mastodon queue service takes care of all the background tasks.

sudo nano /etc/systemd/system/mastodon-queue.service

Populate the file with the following content.

[Unit]
Description=Mastodon Queue Service
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/opt/mastodon/app
Environment="RAILS_ENV=production"
Environment="DB_POOL=5"
ExecStart=/bin/bash -lc 'bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push'
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target

Finally, create the last service file for the Mastodon streaming API service.

sudo nano /etc/systemd/system/mastodon-api.service

Populate the file with the following content.

[Unit]
Description=Mastodon Streaming
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/opt/mastodon/app
Environment="NODE_ENV=production"
Environment="PORT=4000"
ExecStart=/bin/npm run start
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target

Enable the Mastodon services to start at boot time automatically.

sudo systemctl enable mastodon-web mastodon-queue mastodon-api 

To start the Mastodon services, you can run this.

sudo systemctl start mastodon-web mastodon-queue mastodon-api 

To check the status of the services, you can run this.

sudo systemctl status mastodon-web mastodon-queue mastodon-api 

Conclusion

You can now access the Mastodon application by going to https://social.example.com. Create a new user account on your server and verify the user account by opening the link sent in the email. To provide administrator privileges to the account you just created, run this.

sudo su - mastodon && cd ~/app
RAILS_ENV=production bundle exec rails mastodon:make_admin USERNAME=<user_name>

Replace <user_name> with the actual username you have used to create the account in the Mastodon interface.

Your Mastodon instance is now installed and configured to run. Start by inviting your friends to join your instance and explore the features provided by Mastodon. You can use your social networking site in mobile by downloading any of the applications available for various platforms.