Have your Mastodon and Eat It Too, for free

HOW TO HOST A LARGE MASTODON SERVER VERY CHEAPLY ON ORACLE CLOUD INFRASTRUCTURE

This is a how-to article. It assumes a basic level of Linux proficiency and skims over a lot of details. If there is a ton of popular request, I may come back and fill in more of those details.

Mastodon, the open source social network software, is a bit resource-hungry. In particular, it wants a fair bit of memory in order to run stably, and it likes to cache an insane volume of media (hundreds of gigabytes is a reasonable expectation).

Fortunately, as of 2022, cloud hosting providers are so desperate to get you onto their service that they are competitively giving away free server capacity, storage and bandwidth.

As a former employee at Oracle, I am happy to use their cloud offering, and that’s what this howto is about. I’m fully aware that not everyone trusts Big Red, and all I can say on the matter is that USB disks are cheap and secure offsite backups are your friend.

At time of writing, the Oracle Cloud free tier includes free Ampere (ARM64) VMs with four whole CPUs and a massive 24GB of RAM. This is where I recommend you put your Mastodon instance and associated database. You can also use the (much smaller) AMD64 parts of the cloud, but I keep those for things like my mail server and this blog.

I’m going to assume you’re comfortable installing and configuring your preferred Linux distro on your cloud VM(s).

The setup instructions at joinmastodon.org are clear, if a bit terse, so I’m going to assume that you can follow those too.

The key missing piece is the object storage. OCI free tier doesn’t include enough disk space for you to just keep your instance’s media cache in the server’s filesystem, and the cost of using normal block storage would be prohibitive, not to mention quite slow.

The answer is in Mastodon’s (very poorly documented) Amazon S3 block storage functionality.

  1. On your Oracle Cloud Infrastructure management page, go to Storage -> Object Storage & Archive Storage -> Buckets
  2. Click on ‘Create Bucket’ and name the bucket –
    1. I named mine after my instance domains, since I have more than one instance.
    2. I have enabled Auto Tiering in the hope of saving some money, although it may have no effect due to Mastodon’s cache TTL.
    3. I left “Encrypt using Oracle managed keys” at the default value.
    4. Leave your bucket visibility at ‘Private’ or this will impact the security of your instance.
    5. Click “Create”
    6. Take a copy of the ‘Namespace’ text for your new bucket.
  3. Under ‘Resources’ on the left, select “Pre-Authenticated Requests”.
    1. Leave the permissions at ‘Bucket’ and ‘Permit object reads’ – this special URL will be used by nginx to serve up the media in your bucket, so it only needs to be read-only.
    2. Select ‘enable object listing’
    3. This is very annoying: Oracle require these links to have an expiry date, and you can’t just type in a new date. You need to open up the date chooser and start clicking. I clicked through to the year 2100. You may have more or less patience than I do.
    4. Click “Create Pre-Authenticated Request”
    5. Copy the URL for the pre-authenticated request and SAVE IT SOMEWHERE!
  4. Now we play the long-and-tedious Oracle Cloud security game. Brace yourself.
  5. Open up the main menu again and select “Identity and Security”
    1. Under ‘Identity’ select ‘Groups’
    2. Click on ‘Create Group’
    3. Name your group whatever you like. Mine is called ‘Mastodon’ because I’m so creative.
    4. Click on ‘Create’
  6. Open up the main menu again and select “Identity and Security”
    1. Under ‘Identity’ select ‘Policies’
    2. Click ‘Create Policy’
    3. Name the policy something like ‘mastodon-bucket-policy’
    4. Under Policy Builder select ‘Show manual editor’
    5. In the manual editor box, paste this text (with your Mastodon group name):
      Allow group Mastodon to manage buckets in tenancy
      Allow group Mastodon to manage objects in tenancy

      Obviously these permissions are far more than is required, but I can at least confirm that they work. If you know this tool better than me and you can suggest a more reasonable set of permissions, please let me know.

    6. Click on ‘Create’
  7. Open up the main menu again and select “Identity and Security”
    1. Under ‘Identity’ select ‘Users’
    2. Click on ‘Create User’
    3. Select ‘IAM User’
    4. I called my user ‘Mastodon’ and am using the same user for both Instances / both Buckets. Fill in whatever details you like here.
    5. In the new user, click ‘Add User to Group’
    6. Select the group you created above
    7. Click ‘Add’
    8. Under ‘Resources’ on the left, click on ‘Auth Tokens’
    9. Click ‘Generate Token’. Call it whatever you like.
    10. SAVE A COPY OF THE TOKEN!
    11. Under ‘Resources’ on the left, click on ‘Customer Secret Keys’
    12. Click ‘Generate Secret Key’. Call it whatever you like.
    13. SAVE A COPY OF THE SECRET KEY
  8. That was a perfectly reasonable process, wasn’t it?
  9. When you set up your OCI account, you should have selected a region for your service. Make sure you know what your region is called. It’s listed as ‘Region’ in your compute instance details. Mine is ap-melbourne-1
  10. In your Mastodon server’s .env.production, you will see a bunch of commented-out “S3_…” parameters. When you’re finished editing them, they will look something like this:
    S3_ENABLED=true
    S3_ENDPOINT=https://<your bucket namespace>.compat.objectstorage.<your region>.oraclecloud.com
    S3_PROTOCOL=https
    S3_BUCKET=mastodon
    S3_HOSTNAME=<your bucket namespace>.compat.objectstorage.<your region>.oraclecloud.com
    AWS_ACCESS_KEY_ID=<your auth token>
    AWS_SECRET_ACCESS_KEY=<your secret key>
    S3_ALIAS_HOST=<your media proxy subdomain>
  11. Set up your nginx object storage proxy as documented here, but user the pre-approved request URL you generated earlier here:
    set $s3_backend ‘https://objectstorage.<your region>.oraclecloud.com/p/<your secret magic here>/o’;
    IMPORTANT NOTE: There is no trailing slash after the last ‘o’. If you include the slash, things will break in ways that take a lot of time and sanity to debug. Don’t ask me how I know.

That’s it. If this doesn’t make sense, or doesn’t work for you, or if you just need more detail on some aspect of this, please leave a comment or message me at @thorne and I will do what I have time and spoons for.

Good luck!