Skip to content

Instantly share code, notes, and snippets.

@dnhn
Last active March 27, 2023 13:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dnhn/22e84819f9c85dd2afe418eadc639e96 to your computer and use it in GitHub Desktop.
Save dnhn/22e84819f9c85dd2afe418eadc639e96 to your computer and use it in GitHub Desktop.
Setup Mastodon on AWS EC2

AWS Services used:

  • EC2
  • S3
  • Cloudfront
  • Route 53
  • ACM
  • IAM

Notes:

  • Most of the shell commands require sudo
  • Unless using Elastic IP, public IPv4 address changes on EC2 instance restarts, update new IP address to domain records

1. Prepare resources

https://docs.joinmastodon.org/user/run-your-own

  • Route 53 hosted zone
  • EC2 instance
  • S3 bucket

2. Prepare machine

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

EC2 instance

  • AMI Ubuntu Server 20.04 arm64
  • Recommended memory at least 2GB t4g.small
  • Create key pair for SSH access
  • Network settings/security groups:
    • Inbound: SSH, HTTP, HTTPS, IPv6
    • Outbound: all traffic
  • Storage: 8GB gp3

Route 53 hosted zone, point domain and www to instance.

Create root (blank record name) and www A records with ec2 instance’s Public IPv4 address as values.

3. Begin installation

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

Step install postgresql: skip and follow this guide instead https://www.postgresql.org/download/linux/ubuntu

  • Run the script and continue

Step rbenv install Ruby, the installation may crash if machine memory is too low (crashed on t4g.nano, .5 GiB)

  • Stop instance and change to higher instance type (see note)

Step mastodon:setup full steps here https://github.com/mastodon/mastodon/blob/v3.5.3/lib/tasks/mastodon.rake

  • Domain name: example.com
  • Single user blog: yes if personal use only
  • Docker: no
  • PostgreSQL/Redis: no input, leave all default
  • Store uploaded files to cloud: yes + S3, leave all default, update values later
  • Access the uploaded files from your own domain: yes, files.example.com
  • Send a test e-mail with this configuration right now: no
  • Save configuration/Prepare database/Compile assets: yes

Step setup nginx and acquire SSL certificate (domain A records are required for certbot)

  • Get SSL certificate before setup nginx certbot certonly --standalone -d example.com

Setup systemd services and visit the domain in browser.

If this error appears This page is not correct / We're sorry, but something went wrong on our end.

  • SSH and run at root user sudo journalctl -u mastodon-web, if mastodon-web service stopped after application preloading that means system memory is insufficient to run the application (t4g.micro, 1 GiB)
    bundle[2066]: [2066] * Preloading application
    systemd[1]: mastodon-web.service: Main process exited, code=killed, status=9/KILL
    
  • Stop instance and change to higher instance type (see note)

4. Setup storage and access keys

Create S3 bucket and turn off Block all public access in Permissions.

AWS IAM

  • Add user
  • Select AWS credential type Access key - Programmatic access
  • At Permissions step, attach AmazonS3FullAccess policy to user
  • Continue until the last step Create user, store user credentials (access and secret keys) in a safe place

SSH to project repository, open .env.production and update these variables:

  • S3_BUCKET=name of the bucket created above
  • S3_REGION=find in bucket Properties, e.g. us-east-1
  • S3_HOSTNAME=use region id above and follow this format s3-us-east-1.amazonaws.com
  • AWS_ACCESS_KEY_ID=access key from IAM user above
  • AWS_SECRET_ACCESS_KEY=secret key from IAM user above

Try uploading files, at this step we can find the items in the S3 bucket but they are not public until deployed via a CloudFront distribution.

5. Setup sub-domain (files.example.com) and certificate

Go to Route 53 hosted zone and point sub-domain to instance like we did in step 2. Prepare machine.

Open /etc/nginx/sites-available/mastodon and add new server for the sub-domain:

server {
  server_name files.example.com
}

Run sudo certbot -d files.example.com to generate ssl certificate, then open newly created keys in editor

/etc/letsencrypt/live/files.example.com/fullchain.pem
/etc/letsencrypt/live/files.example.com/privkey.pem

Sub-domain entry above can be deleted after SSL generation.

Go to AWS ACM and import certificate, there are 3 fields:

  • Certificate body: look into fullchain.pem, there are 3 certificates, copy the first one
    -----BEGIN CERTIFICATE-----
    ...
    -----END CERTIFICATE-----
    
  • Certificate private key: privkey.pem
  • Certificate chain: the remains of fullchain.pem

6. Create Cloudfront distribution and attach sub-domain

Create distribution with S3 bucket above

  • At Default cache behaviour, redirect HTTP to HTTPS
  • At Settings, add the sub-domain (files.example.com) to Alternate domain name (CNAME) and select corresponding SSL certificate imported previously in ACM
  • Finish creating distribution

Update the sub-domain record in hosted zone

  • Record type A
  • Turn Alias on
  • Route traffic to CloudFront distribution and choose the distribution created above
  • Save the record

Complete configuration and enjoy.

Additional configuration, redirect www to non-www https://www.vultr.com/docs/nginx-redirects-for-non-www-sub-domains-to-www/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment