Skip to content

Instantly share code, notes, and snippets.

@johnspurlock-skymethod
Last active May 10, 2022 12:22
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johnspurlock-skymethod/da09c82e2ed8fabd5f5e164d942ce37c to your computer and use it in GitHub Desktop.
Save johnspurlock-skymethod/da09c82e2ed8fabd5f5e164d942ce37c to your computer and use it in GitHub Desktop.
Quickstart for installing a small Mastodon server on Amazon EC2

How to install a minimal Mastodon server on Amazon EC2

Useful links

Personal Mastodon instance guide (a minimal Mastodon on EC2) https://gist.github.com/AndrewKvalheim/a91c4a4624d341fe2faba28520ed2169

Installing Mastodon (official guide) https://docs.joinmastodon.org/admin/install/

Setup swap since we only have 1gb ram and the gp2 ssd is not charged for iops https://www.digitalocean.com/community/tutorials/how-to-add-swap-space-on-ubuntu-18-04

Tuning Mastodon https://github.com/felx/mastodon-documentation/blob/master/Running-Mastodon/Tuning.md

tootctl https://docs.joinmastodon.org/admin/tootctl/

Assumptions

This guide uses comments.yourdomain.org as the subdomain you own and on which you want to install your Mastodon instance. You can also use a main domain (e.g. yourdomain.org) if you don't have any other plans for that domain.

This guide also assumes:

  • you'll be sending outgoing emails via SES from the main domain (e.g. yourdomain.org)
  • you can receive incoming email for your initial admin user: e.g. admin@yourdomain.org
  • you have your own DNS provider, like route 53, cloudflare, gandi, etc.

AWS pre-reqs

create iam role: service-comments-yourdomain-org
  with an ec2 trust relationship

create ec2 security group: comments-yourdomain-org-1
  allow all inbound from admin ip
  allow all inbound from anywhere to ports 443 and 80

create ses smtp iam user: ses-smtp-user.yourdomain.org
  write down the smtp username, password, and smtp endpoint (like email-smtp.us-east-1.amazonaws.com)

create ses domain identity for yourdomain.org
  easy dkim
    key length: RSA_2048_BIT
    Publish DNS records to Route53: disabled  # perhaps enable if you use route53?
    DKIM signatures: enabled  # default
    tag: (whatever you want)
    
  add the 3 dkim cnames over on your dns provider, amazon verfies them pretty quickly

Create and launch your EC2 instance

Ubuntu Server 18.04 LTS (HVM), SSD Volume Type, 64-bit (x86)
t3a.micro (- ECUs, 2 vCPUs, 2.2 GHz, -, 1 GiB memory, EBS only)
on demand hourly rate: $0.0094 => $6.768 for 30 days
iam role: service-comments-yourdomain-org
Enable termination protection: yes
Credit specification: Unlimited
8gb general purpose ssd (gp2) volume (magnetic is half as expensive but worry about perf)
  $0.10/GB-month -> $0.80/mo
  Delete on Termination: no
tag: (whatever you want)
security group: comments-yourdomain-org-1
keypair: (create one or use existing, you'll need the keypair .pem file to ssh into the box below)

let's assume it launches with ip 123.123.123.123 and hostname ec2-123-123-123-123.compute-1.amazonaws.com

once running, use your dns provider to add comments.yourdomain.org as an A record pointing to the ec2 ip or CNAME record pointing to the ec2 hostname

Installation

ssh -i ~/path/to/ec2-keypair.pem ubuntu@ec2-123-123-123-123.compute-1.amazonaws.com

# initial os setup, create swap file

sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
sudo cp /etc/fstab /etc/fstab.bak
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
sudo sysctl vm.swappiness=10
sudo vi /etc/sysctl.conf  # add to the bottom: vm.swappiness=10
sudo sysctl vm.vfs_cache_pressure=200
sudo vi /etc/sysctl.conf  # add to the bottom: vm.vfs_cache_pressure=200
sudo vi /etc/default/grub.d/50-cloudimg-settings.cfg  # Append zswap.enabled=1 to GRUB_CMDLINE_LINUX_DEFAULT
sudo update-grub
sudo apt update
sudo apt upgrade
sudo reboot

# mastodon setup

sudo -i
curl -sL https://deb.nodesource.com/setup_12.x | bash -
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 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 libgdbm-dev \
  nginx redis-server redis-tools postgresql postgresql-contrib \
  certbot python3-certbot-nginx yarn libidn11-dev libicu-dev libjemalloc-dev

adduser --disabled-login mastodon
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
exec bash
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install 2.7.2  # takes a while
rbenv global 2.7.2
gem install bundler --no-document
exit  # back to root

sudo vi /etc/postgresql/10/main/postgresql.conf  # uncomment and set effective_cache_size = 512MB
  # db data is in here: /var/lib/postgresql/10/main
systemctl restart postgresql

sudo -u postgres psql
CREATE USER mastodon CREATEDB;
\q

su - mastodon  # switch to mastodon user
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)   # was v3.4.6 on 2022-02-06

bundle config deployment 'true'
bundle config without 'development test'
bundle install -j$(getconf _NPROCESSORS_ONLN)  # takes a while
yarn install --pure-lockfile

RAILS_ENV=production bundle exec rake mastodon:setup
  domain name: comments.yourdomain.org
  enable single user mode: no  # we may want to create a few posting users
  using docker: no
  postgres host:  # default
  postgres port:  # default
  postgres db: mastodon_production  # default
  postgres user: mastodon  #default
  postgress pass:  # default
  redis options: # defaults
  store uploads in the cloud: no  # maybe later
  emails from localhost: no
  smtp server: (amazon ses smtp server from above)
  smtp port: 587 # default
  smtp username: (amazon ses smtp username from above)
  smtp password: (amazon ses smtp password from above)
  smtp auth: plain  # default
  smtp openssl verify mode: none  # default
  email from: Notifications <notifications@yourdomain.org>
  send test: yes   # confirm email received
  save config: yes
  prepare database now: yes
  compile assets now: yes
  create admin user: yes
  username: admin  # default
  email: admin@yourdomain.org  # note the generated password: (hex string)

exit  # back to root

cp /home/mastodon/live/dist/nginx.conf /etc/nginx/sites-available/mastodon
ln -s /etc/nginx/sites-available/mastodon /etc/nginx/sites-enabled/mastodon
sudo vi /etc/nginx/sites-available/mastodon   # replace example.com with yourdomain.org
systemctl reload nginx

certbot --nginx -d comments.yourdomain.org
  email: admin@yourdomain.org
  choose option 1, may create a new "default" site you'll have to remove below
  Deploying Certificate to VirtualHost /etc/nginx/sites-enabled/mastodon
 - Congratulations! Your certificate and chain have been saved at:
   /etc/letsencrypt/live/comments.yourdomain.org/fullchain.pem
   Your key file has been saved at:
   /etc/letsencrypt/live/comments.yourdomain.org/privkey.pem
   Your cert will expire on yyyy-mm-dd. To obtain a new or tweaked
   version of this certificate in the future, simply run certbot again
   with the "certonly" option. To non-interactively renew *all* of
   your certificates, run "certbot renew"

# if it created/modified the "default" site, remove it and add to mastodon manually
rm /etc/nginx/sites-available/default
rm /etc/nginx/sites-enabled/default

sudo vi /etc/nginx/sites-available/mastodon
  ssl_certificate      /etc/letsencrypt/live/comments.yourdomain.org/fullchain.pem;
  ssl_certificate_key  /etc/letsencrypt/live/comments.yourdomain.org/privkey.pem;

# else if it modified the "mastodon" site automatically, you're good

systemctl reload nginx  # now seeing the elephant!

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

sudo vi /etc/systemd/system/mastodon-sidekiq.service  # change 25 to 5 in line: ExecStart=/home/mastodon/.rbenv/shims/bundle exec sidekiq -c 25

sudo vi /etc/systemd/system/mastodon-streaming.service  # ensure line exists: Environment="STREAMING_CLUSTER_NUM=1"

sudo vi /etc/systemd/system/mastodon-web.service  # append these lines to the end:
Environment="MAX_THREADS=2"
Environment="WEB_CONCURRENCY=1"

systemctl daemon-reload
systemctl enable --now mastodon-web mastodon-sidekiq mastodon-streaming

sudo reboot

# setup tootctl
sudo -i
su - mastodon
vi ~/.bashrc # append these lines to the end:
export PATH="$HOME/live/bin:$PATH"
export RAILS_ENV=production
exit
su - mastodon

# close registrations, disable signup form on the homepage
tootctl settings registrations close

# done!

-----

# to restart services:
(as root)
systemctl restart mastodon-sidekiq
systemctl reload mastodon-web
systemctl restart mastodon-streaming

How those 8 gigs on the EBS volume are used at various stages:

df -h
Filesystem      Size  Used Avail Use%
/dev/nvme0n1p1  7.7G  1.2G  6.6G  16% / # after launch
/dev/nvme0n1p1  7.7G  2.6G  5.2G  34% / # after swap setup + upgrade
/dev/nvme0n1p1  7.7G  3.4G  4.4G  44% / # after package installs
/dev/nvme0n1p1  7.7G  4.9G  2.9G  64% / # after all setup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment