Ubuntu 16.04: Run Mastodon instance

This article will describe running Mastodon instance directly on Ubuntu 16.04 without Docker and Vargrant.

1 Limitation

  • This article reffers Production-guide.md and Alternatives.md in Mastodon documentation.
  • This Mastodon instance is for checking Mastodon usage and API. This is not production level.
  • The Mastodon, Redis, PostgreSQL, Apache and Postfix will be installed in the same machine.
  • Only <user>@localhost can be used for account (This Postfix setting only sends to localhost). If you need multiple account, you need to create multiple local user in machine for creating multiple mail address.
  • You need to login to machine for checking confirmation mail.
  • The Apache setting does not use RewriteRule and subdomain. This will prevent other web service which uses Apache.

2 Run Mastodon instance

The following script will run Mastodon instance. The LOCAL_DOMAIN variable can be used as IP address and FQDN. This will be used in confirmation mail.

$ LOCAL_DOMAIN=192.168.11.86 ./run-this-script.sh
#!/bin/sh

set -e

[ -z "${LOCAL_DOMAIN}" ] && \
  LOCAL_DOMAIN=ubuntu-1604-mastodon.hiroom2.com
[ -z "${MASTODON_DIR}" ] && \
  MASTODON_DIR=/var/lib/mastodon
MASTODON_TAG=v1.3.3

mastodon_install_depend()
{
  sudo apt install -y imagemagick ffmpeg libpq-dev libxml2-dev \
       libxslt1-dev file git curl ruby-bundler ruby-dev
  sudo gem install bundler
  curl -sL https://deb.nodesource.com/setup_6.x | sudo bash -
  sudo apt install -y nodejs
  sudo npm install -g yarn
}

mastodon_install_redis()
{
  sudo apt install -y redis-server redis-tools
  sudo systemctl enable redis-server
  sudo systemctl start redis-server
}

mastodon_install_postgres()
{
  sudo apt install -y postgresql postgresql-contrib
  cat <<EOF | sudo -u postgres psql
CREATE USER mastodon CREATEDB;
\q
EOF
  sudo sed \
       -e 's;^local.*postgres.*peer$;host all all 127.0.0.1/32 ident;g' \
       -i /etc/postgresql/9.?/main/pg_hba.conf

  sudo apt install -y pidentd
  sudo systemctl enable pidentd
  sudo systemctl start pidentd
  sudo systemctl restart postgresql
}

mastodon_install_mastodon()
{
  # Not use -m for disable creating skel like .bashrc.
  sudo useradd mastodon -M -d ${MASTODON_DIR}
  git clone https://github.com/tootsuite/mastodon.git
  cd mastodon
  git checkout ${MASTODON_TAG} -b ${MASTODON_TAG}
  cd ..
  sudo mkdir -p "$(dirname ${MASTODON_DIR})"
  sudo mv mastodon ${MASTODON_DIR}
  sudo chown -R mastodon:mastodon ${MASTODON_DIR}

  sudo su - mastodon -c "
cd ${MASTODON_DIR}
# The ruby version checker may not work correctly.
sed -e \"s/^ruby/#ruby/g\" -i Gemfile
bundle install --deployment --without development test
yarn install --pure-lockfile

PAPERCLIP_SECRET=\$(rake secret)
SECRET_KEY_BASE=\$(rake secret)
OTP_SECRET=\$(rake secret)

sed -e \"s/^REDIS_HOST=.*/REDIS_HOST=localhost/g\" \
    -e \"s/^DB_HOST=.*/DB_HOST=localhost/g\" \
    -e \"s/^DB_USER=.*/DB_USER=mastodon/g\" \
    -e \"s/^DB_NAME=.*/DB_NAME=mastodon/g\" \
    -e \"s/^LOCAL_DOMAIN=.*/LOCAL_DOMAIN=${LOCAL_DOMAIN}/g\" \
    -e \"s/^LOCAL_HTTPS=.*/LOCAL_HTTPS=true/g\" \
    -e \"s/^PAPERCLIP_SECRET=.*/PAPERCLIP_SECRET=\${PAPERCLIP_SECRET}/g\" \
    -e \"s/^SECRET_KEY_BASE=.*/SECRET_KEY_BASE=\${SECRET_KEY_BASE}/g\" \
    -e \"s/^OTP_SECRET=.*/OTP_SECRET=\${OTP_SECRET}/g\" \
    -e \"s/^SMTP_SERVER=.*/SMTP_SERVER=localhost/g\" \
    -e \"s/^SMTP_PORT=.*/SMTP_PORT=25/g\" \
    -e \"s/^SMTP_LOGIN=/#SMTP_LOGIN=/g\" \
    -e \"s/^SMTP_PASSWORD=/#SMTP_PASSWORD=/g\" \
    -e \"s/^SMTP_FROM_ADDRESS=.*/SMTP_FROM_ADDRESS=noreply@localhost/g\" \
    -e \"s/^#SMTP_AUTH_METHOD=.*/SMTP_AUTH_METHOD=none/g\" \
    -e \"s/^# DEFAULT_LOCALE=.*/DEFAULT_LOCALE=en/g\" \
    .env.production.sample > .env.production

RAILS_ENV=production bundle exec rails db:setup
RAILS_ENV=production bundle exec rails assets:precompile
"

  cat <<EOF | sudo tee /lib/systemd/system/mastodon-web.service
[Unit]
Description=mastodon-web
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=${MASTODON_DIR}
Environment="RAILS_ENV=production"
Environment="PORT=3000"
ExecStart=/usr/local/bin/bundle exec puma -C config/puma.rb
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
EOF

  cat <<EOF | sudo tee /lib/systemd/system/mastodon-sidekiq.service
[Unit]
Description=mastodon-sidekiq
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=${MASTODON_DIR}
Environment="RAILS_ENV=production"
Environment="DB_POOL=5"
ExecStart=/usr/local/bin/bundle exec sidekiq -c 5 -q default -q mailers -q pull -q push
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
EOF

  cat <<EOF | sudo tee /lib/systemd/system/mastodon-streaming.service
[Unit]
Description=mastodon-streaming
After=network.target

[Service]
Type=simple
User=mastodon
WorkingDirectory=${MASTODON_DIR}
Environment="NODE_ENV=production"
Environment="PORT=4000"
ExecStart=/usr/bin/npm run start
TimeoutSec=15
Restart=always

[Install]
WantedBy=multi-user.target
EOF

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

mastodon_install_apache()
{
  sudo apt install -y apache2
  cat <<EOF | sudo tee /etc/apache2/sites-available/mastodon.conf
<VirtualHost _default_:443>
  SSLEngine on
  SSLCertificateFile    /etc/ssl/certs/ssl-cert-snakeoil.pem
  SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key

  <Location /assets>
    Header always set Cache-Control "public, max-age=31536000, immutable"
  </Location>

  ProxyPreserveHost On
  RequestHeader set X-Forwarded-Proto "https"
  ProxyPass /500.html !
  ProxyPass /oops.png !
  ProxyPass /api/v1/streaming/ ws://localhost:4000/
  ProxyPassReverse /api/v1/streaming/ ws://localhost:4000/
  ProxyPass / http://localhost:3000/
  ProxyPassReverse / http://localhost:3000/
</VirtualHost>
EOF

  for mod in ssl proxy proxy_http headers; do
    sudo a2enmod ${mod}
  done
  sudo a2ensite mastodon

  sudo systemctl enable apache2
  sudo systemctl restart apache2
}

mastodon_install_postfix()
{
  cat <<EOF | sudo debconf-set-selections
postfix postfix/main_mailer_type select No configuration
EOF
  sudo apt install -y postfix

  cat <<EOF | sudo tee /etc/postfix/main.cf
compatibility_level = 2
command_directory = /usr/sbin
daemon_directory = /usr/lib/postfix/sbin
data_directory = /var/lib/postfix
mail_owner = postfix
myhostname = localhost
inet_interfaces = all
mydestination = localhost
local_recipient_maps = unix:passwd.byname \$alias_maps
unknown_local_recipient_reject_code = 550
mynetworks_style = subnet
mynetworks = 127.0.0.0/8
alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
smtpd_banner = \$myhostname ESMTP \$mail_name (Ubuntu)
debugger_command =
         PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
         ddd \$daemon_directory/\$process_name \$process_id & sleep 5
sendmail_path = /usr/sbin/postfix
newaliases_path = /usr/bin/newaliases
mailq_path = /usr/bin/mailq
setgid_group = postdrop
inet_protocols = ipv4
EOF

  sudo newaliases
  sudo systemctl enable postfix
  sudo systemctl restart postfix
}

mastodon_main()
{
  mastodon_install_depend
  mastodon_install_redis
  mastodon_install_postgres
  mastodon_install_mastodon
  mastodon_install_apache
  mastodon_install_postfix
}

mastodon_main "$@"

3 Access to Mastodon instance

Access to Mastodon instance with the following URL.

https://<server>

Because the chrome does not have this page's certification, the crome warns the following and cannot to access to this page. You need to click "ADVANCED" and "Proceed to <server> (unsafe)". The other browser will needs the similar way.

0001_YourConnectionIsNotPrivate.png

Mastodon top page is displayed. Add user with <user>@localhost.

0002_MastodonTopPage.png

Confirmation mail caution is displayed.

0003_NeedConfirmation.png

Login to machine's user and run mutt. The confirmation mail is received. Click page link for activating account.

$ sudo apt install -y mutt
$ mutt

0004_ConfirmationMail.png

Mail address is validated. Input mail address and password.

0005_ConfirmationDone.png

Mastodon login is done.

0006_WelcomeToMastodon.png

TOOT can be done.

0007_MyFirstTOOT.png