Self-hosting a Mastodon server is an excellent way to take control of your social networking experience, but managing storage for media files can quickly become a challenge. As your server grows, so does the demand for scalable, cost-effective, and reliable storage. Local storage can be limiting and expensive to expand, making it less ideal for long-term hosting.
That’s where Cloudflare R2 comes in (or any other preferred remote storage solution). With no egress fees and seamless integration via the S3 API, Cloudflare R2 offers a modern solution to store and serve your Mastodon media files efficiently. By moving your storage to the cloud, you can free up local resources while ensuring your server is ready to handle increasing traffic and media uploads.
In this guide, I’ll walk you through the entire process of how I migrated the toot.re media storage to Cloudflare R2 with the help of rclone
, a powerful tool for syncing and managing files between local and cloud storage.
Whether you’re looking to reduce costs or future-proof your server, this guide has you covered and also let you learn of a things I can do better next time.
Prerequisites
You need the following to start doing the migration:
A running Mastodon instance on Linux
SSH access to the Mastodon server and its configuration files
A Cloudflare account with an R2 storage enabled
rclone
on the server (used to sync files between local storage and R2).
Preparing Cloudflare R2
Let’s start with setting up a Cloudflare R2 bucket.
Log in to Cloudflare dashboard
Navigate to R2 Object Storage
Create a new bucket (e.g., mastodon-media)
When the bucket is created, you need to generate R2 API Tokens, so your Mastodon instance, and rclone
, can access the bucket with the right permissions.
To generate R2 API Tokens go to the R2 Object Storage page and click on “Manage R2 API Tokens”. Then, follow these steps:
Click on “Create API Token”
Give the token a smart name, e.g. “R2 token for Mastodon server <url>”
Set the permissions to Object Read & Write
Specify the bucket if you want to limit this API token to a certain bucket
Specify how long you want to keep the token alive with the TTL.
Be aware that if you do not choose “Forever” here, you might lose access to your bucket if you do not recreate a token and add that to your Mastodon.env.production
config file.If you want to limit access to this API token by IP address, you can either limit, or deny access per IP address.
Now click “Create API Token”
When the API Token creation was successful, record the Access Key ID and Secret Access Key, since you will not see these again when you navigate to another screen.
Since we now have created a way to access our R2 bucket on Cloudflare, it’s time to start using it. Before we hook up Mastodon with the bucket, let’s first install and/or configure rclone
.
Installing and configuring rclone
When migrating from local storage to an R2 bucket, you need to be sure to move all local files to the R2 bucket. To do this, we cannot just simple copy/move files, we need a nice tool for this. Meet rclone.
To install rclone
on Linux systems, run:
sudo -v ; curl https://rclone.org/install.sh | sudo bash
This script installs the latest stable release of rclone
, after it checks if rclone
is installed. It won't re-download if not needed.
Test if rclone is installed by running rclone --version
and compare that to the latest stable version in the rclone
website.
Now that rclone
is installed, let’s move on and configure rclone
to make use of the R2 API token we created.
To configure rclone
we need to edit the config file which we can find by running rclone config file
. This will output a path like /home/mastodon/.config/rclone/rclone.conf
, which we can use to create/edit that file.
Open the file with your favorite CLI text editor (mine is nano) and make sure all info below is in that file before saving it. Make sure you use your rclone API token keys.
[r2]type = s3provider = Cloudflareaccess_key_id = your_r2_api_access_idsecret_access_key = your_r2_api_secret_access_idendpoint = your_cloudflare_api_endpointbucket_acl = privateregion = autono_check_bucket = true
Your endpoint URL can be found in the Cloudflare R2 page. Open the bucket, click “Settings” and copy the S3 API URL in “Bucket Details”.
The no_check_bucket
needs to be added if you are using Objectl-level Permissions as I have advised when creating the R2 API Token. Cloudflare rclone docs mention this.
Now that we have configure rclone
, let’s continue using it.
Backup and sync Mastodon media files
Before doing anything with the local Mastodon media files, make a backup.
For a standard install, the storage is located at /home/mastodon/live/public/system/
, please check this before continuing with making a backup. You want to be sure to backup the right data.
Now execute this command to create a compressed tar file with all of the Mastodon media files of your instance: tar -czvf mastodon_media.tar.gz /home/mastodon/live/public/system/
.
This might take a long time depending on the amount of files stored locally.
If you need to free up some space, read this post on how to clean the cache.
Again, before doing anything with the local Mastodon media files, make a backup.
When the backup has completed, it’s time to sync the local files with the R2 bucket. To start this, execute: rclone sync /home/mastodon/live/public/system/ r2:mastodon-media -P
where r2
is the name of the configuration for rclone
, mastodon-media
is the name of the bucket, and -P
let’s you see the progress.
Take some time for a break, because this can take a very long time, as in hours.
The next thing we need to do, is configure Mastodon to create our R2 bucket
Configuring Mastodon to use Cloudflare R2
To have Mastodon use the R2 bucket, there are just a few steps to take. Let’s go.
Make a backup of your
.env.production
file located in the Mastodon installation directory, typically/home/mastodon/live/
Edit the
.env.production
file and make sure the following is added:
S3_ENABLED=trueS3_BUCKET=your_bucket_nameS3_REGION=autoAWS_ACCESS_KEY_ID=your_r2_api_access_idAWS_SECRET_ACCESS_KEY=your_r2_api_secret_access_idS3_ALIAS_HOST=your.r2.hostnameS3_HOSTNAME=your.r2.hostnameS3_ENDPOINT=your_cloudflare_api_endpointS3_PERMISSION=private
Carefully replace all data for S3_BUCKET, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, S3_ALIAS_HOST, S3_HOSTNAME and S3_ENDPOINT.
If you want to use a custom domain name for your bucket, you can add one by added a custom domain in the “Public access” settings of your bucket.
When the .env.production
file is saved, it’s time to restart Mastodon.
You can choose to flip the switch while the `rclone sync` is running, or wait until the `rclone` job is done. I flipped the switch during the `rclone` job, and it results in images not being loaded, because they were not yet synced.
To restart the Mastodon processes, run:
$ sudo systemctl restart mastodon-web mastodon-sidekiq mastodon-streaming
Now post a test post, and upload an image, like the post I posted. This should be working, and if it is, congratulate yourself with a job well done! If it’s not working, please carefully go over all the steps in this guide.
Conclusion
This was a tough one for me, since I had no clue what to do before I started this process. After reading a lot of docs and posts by other Mastodon instance owners, I decided I was ready to do this. And yes, I learned a few things (see the Lessons learned paragraph).
I think embracing cloud storage for your Mastodon server not only enhances your infrastructure but also demonstrates a commitment to providing the best experience for your users. By investing in a robust and scalable solution like Cloudflare R2, you position your server to handle the demands of the decentralized social web.
Decentralized FTW!
Other optional things to do
Clean up local storage
When the migration has succeeded, and the rclone job is done, you can safely remove local Mastodon media files. Be sure that you have made a backup, and then execute: rm -rf /home/mastodon/live/public/system/*
. If you are not sure to execute this right after the migration, then set a reminder to do this one month after the migration.
Monitor your bucket usage
Regularly open the Cloudflare dashboard and check the metrics of your R2 bucket. It’s advised to keep track of the resource usage, performance, and plan ahead the costs.
Backup R2 bucket offsite
It’s never a bad thing to have a backup. So, go ahead, and think of a way to backup your bucket to another place, in case Cloudflare pulls the plug. Next on my list is setting up an rclone sync
job to sync the bucket contents to my Synology Diskstation.
Remove cached remote accounts that no longer exist
Mastodon keeps a cache for all accounts it ‘sees’. Now would be a good time to clean that up. You can use the Mastodon Admin CLI tool accounts cull
command to that for you.
The result of this command on my server was this: Visited 298103 accounts, removed 3283
. 🎉
Lessons learned (aka “things I can do better next time”)
Make a backup of local files.
Read the docs carefully. You might miss that you have to add
no_check_bucket = true
to therclone
config to prevent “Access Denied, 403” errors when usingrclone
and post a question on the Cloudflare Discord to find that out.Use
rclone sync
and notrclone move
. Because, well,move
deletes local files, and in case of failure and not doing what I said in bullet 1., you loose files.
Photo by Elevate on Unsplash