In this article, we will install Mastodon on an Ubuntu 18.04 VPS.

Mastodon is a free and open-source network platform that is very similar to the famous social media platform known as Twitter. However, unlike Twitter, there is no single central authority and there is no advertising. Mastodon is written in Ruby and JavaScript and its open-source nature makes sure that it remains open for anyone to use privately and securely. Anyone can create a Mastodon server and build their own communities with friends. Additionally, Mastodon is supported by and available through multiple apps for iOS, Android, and other platforms, which will allow you to keep in touch with your friends anywhere you go. Let’s start with installing Mastodon.

Prerequisites:

Before we start the installation, we need to fulfill the prerequisites:

  • A server that’s running Ubuntu 18.04
  • root access or user with sudo privileges
  • A domain name (or a subdomain) for the Mastodon server, e.g. your_domain_name.com

Start by logging into your VPS using the SSH protocol.

We will run all the commands as root in this tutorial, so if you aren’t using the root account, switch to root or use the sudo command in front of the commands. Alternatively, make sure you run each command with ‘sudo’ prefixed to them.

sudo -i

Step 1: Adding Required Repositories

In this step, we will add the required repositories to our system.

To make sure that curl is installed on your system, run the command:

apt install -y curl

Enable the NodeSource repository with the following curl command:

curl -sL https://deb.nodesource.com/setup_8.x | bash -

By using the following commands we will import the repository’s GPG key. After this, we will enable the Yarn APT 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

Step 2: Installing Packages

Now we will install the required system packages for Mastodon:

apt update

apt install -y \
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 libjemalloc-dev

Here’s a brief explanation of the dependencies we just installed:

  • 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 used as a 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.

Step 3: Installing Ruby

Ruby is also one of the requirements for Mastodon and we will install it in this step. We will use ‘rbenv’ to manage the Ruby versions. But since rbenv needs to be installed for a specific Linux user, the first thing we need to do is create a new Mastodon user. To create the Mastodon user, we will execute the command:

adduser --disabled-login mastodon

Then switch to the user mastodon with the command:

su - mastodon

And with the following commands we will 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

After we finish the rbenv and rbenv-build installation, we can install the exact Ruby version we need:

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

Because ruby_2.6.0 comes with a bundler that is not compatible with the default gem version, we’ll need to update the gem:

gem update --system

On top of this, we need to install bundler:

gem install bundler --no-document

Once all of the steps have been processed, we can return to the root user:

exit

Step 4: Setting up PostgreSQL

We will create a PostgreSQL user that can be used by the Linux user with the same username without requiring a separate password. This is a very simple method that uses the “ident” authentication which allows for easy setup as well as allowing local users to access the database without a password.

You can open the Postgres prompt by switching to the postgres user and then running the psql command, or you can execute the following command:

sudo -u postgres psql

Execute the command:

CREATE USER mastodon CREATEDB;

Exit from the Postgres prompt with:

\q

Step 5: Setting up Mastodon

Since we have prepared the server for installing Mastodon, we can proceed now with downloading the Mastodon code. Switch to the mastodon user with the command:

su - mastodon

We will use git to clone the latest stable release of Mastodon by running the commands:

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

Step 6: Installing the Ruby and JavaScript Dependencies

These are the last dependencies that we need to install. We will use bundler to install the rest of the Ruby dependencies:

bundle install \
-j$(getconf _NPROCESSORS_ONLN) \
--deployment --without development test

and use yarn to install Node.js dependencies:

yarn install --pure-lockfile

Step 7: Generating a Configuration File

We can run the interactive setup wizard in order to generate the configuration file:

RAILS_ENV=production bundle exec rake mastodon:setup

Now we can switch back to the root account because that is all we had to do with the mastodon user.

exit

Step 8: Setting up Nginx

Mastodon comes by default with an Nginx configuration template. In the next step, we will use a copy of this configuration template and adjust it for our needs.

cp /home/mastodon/live/dist/nginx.conf /etc/nginx/sites-available/mastodon.conf
ln -s /etc/nginx/sites-available/mastodon.conf /etc/nginx/sites-enabled/mastodon.conf

Then edit /etc/nginx/sites-available/mastodon.conf and replace your_domain.com with your actual domain name.

nano /etc/nginx/sites-available/mastodon.conf

We need to reload Nginx for the changes to take effect:

systemctl reload nginx

If for some reason you accidentally lack the Nginx file in that location, then you can use the following Nginx configuration and modify it according to your needs.

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

proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g;

server {
listen 80;
listen [::]:80;
server_name your_domain.com;
root /home/mastodon/live/public;
location /.well-known/acme-challenge/ { allow all; }
location / { return 301 https://$host$request_uri; }
}

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

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

# Uncomment these lines once you acquire a certificate:
# ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;

keepalive_timeout 70;
sendfile on;
client_max_body_size 80m;

root /home/mastodon/live/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";
add_header Strict-Transport-Security "max-age=31536000";
try_files $uri @proxy;
}

location /sw.js {
add_header Cache-Control "public, max-age=0";
add_header Strict-Transport-Security "max-age=31536000";
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 on;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

proxy_cache CACHE;
proxy_cache_valid 200 7d;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cached $upstream_cache_status;
add_header Strict-Transport-Security "max-age=31536000";

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;
}

Remember to save and exit the file, and restart Nginx as shown above in order to acknowledge the new file.

Step 9: Acquiring an SSL Certificate

We will use Let’s Encrypt to generate a free SSL certificate:

certbot certonly --webroot -d your_domain.com -w /home/mastodon/live/public/

Please do not forget to replace your_domain.com with your actual domain name. We can now edit the /etc/nginx/sites-available/mastodon.conf configuration file with:

nano /etc/nginx/sites-available/mastodon.conf

Uncomment and modify the ssl_certificate and ssl_certificate_key lines. Once we finish editing the configuration file, we need to reload Nginx once again for our changes to have effect:

systemctl reload nginx

At this point, if you try to access your domain in your preferred web browser, you will not be able to see Mastodon. This is because our Mastodon process is not yet started.

Step 10: Setting up systemd Services

In this step, we will start and enable the Mastodon service on the server. We can simply copy the systemd service templates from the Mastodon directory:

cp /home/mastodon/live/dist/mastodon-*.service /etc/systemd/system/

We can double check and make sure that the username and paths are correct in these files:

  • /etc/systemd/system/mastodon-web.service
  • /etc/systemd/system/mastodon-sidekiq.service
  • /etc/systemd/system/mastodon-streaming.service

Finally, we can start and enable the Mastodon systemd services:

systemctl start mastodon-web mastodon-sidekiq mastodon-streaming
systemctl enable mastodon-web mastodon-sidekiq mastodon-streaming

If the systemd files are missing by any chance you can use the following configurations. You need to create three systemd Mastodon service files.

For the Mastodon web workers service enter the following:

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

Place the following code:

[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

Then save and exit.

For the Mastodon background queue service:

nano /etc/systemd/system/mastodon-sidekiq.service

Paste the following code:

[Unit]
Description=mastodon-sidekiq
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="RAILS_ENV=production"
Environment="DB_POOL=25"
Environment="MALLOC_ARENA_MAX=2"
ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 25
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target

Then save and exit the file.

For the Mastodon streaming API service:

nano /etc/systemd/system/mastodon-streaming.service

Place the following code:

[Unit]
Description=mastodon-streaming
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=/home/mastodon/live
Environment="NODE_ENV=production"
Environment="PORT=4000"
Environment="STREAMING_CLUSTER_NUM=1"
ExecStart=/usr/bin/node ./streaming
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target

Save and exit.

You’ll need to reload the systemctl daemon list for the changes to take effect:

systemctl daemon-reload

You should now be able to start and enable the Mastodon systemd services as we did earlier.

Congratulations! You have successfully installed Mastodon on your server. You can access it by going to https://your_domain.com/.

In this article, we showed you how to install Mastodon on Ubuntu 18.04. Now you can use this knowledge and create your private social network with this wonderful application.


Of course, if you are one of our Managed Ubuntu Hosting customers, you don’t have to install Mastodon on your Ubuntu 18.04 VPS – simply ask our admins, sit back, and relax. Our admins will install Mastodon on Ubuntu 18.04 for you immediately.

PS. If you liked this post on how to install Mastodon on an Ubuntu 18.04 VPS, please share it with your friends on the social networks using the buttons below, or simply leave a comment in the comments section. Thank you.