Mastodon Backup

Prologue

People on the Fediverse like to point out how you have to backup your data regularly in case of a catastrophic failure effecting your instance, the host of your instance, the data centre of your instance or maybe your admin. Heck, even a catastrophic failure effecting an admin of another instance might make you wish you had a backup of your follows list1.

Remembering to make a backup manually is already difficult enough2, but then you also need to remember how to make that backup…

Manual Backups

Got to "App Settings". You do not know where "App Settings" are? I know the feeling… They are behind the three cogs on the top left. Navigate to "Import and export". Click "Data export". Click the "CSV" link. Click the other "CSV" link. Click the other other "CSV" link. You have now downloaded your "follows", "muted" and "blocked" accounts lists.

Automatic Backups

I have recently discovered toot - Mastodon CLI client which can output a list of your follows and people following you. I have contributed code for "muted" and "blocked" commands which got accepted and released in version 0.38.

We first need to authenticate to our instance by running toot login. This is only needed once. For more information see the official documentation.

I am running the following script from cron once a day:

#! /bin/ksh
ACCOUNT=@florian@bsd.network
INSTANCE=bsd.network
BACKUPDIR=/home/mastodonbackup/backup

set -e
set -o pipefail

exit_if_nonzero_or_stderr() {
        (
                set -o pipefail
                { "$@" 1>&3 ;} 2>&1 | {
                        if IFS= read -r line; then
                                printf "%s\n" "$line"
                                cat
                                exit 1
                        fi
                } >&2
        ) 3>&1
}

exit_if_nonzero_or_stderr /usr/local/bin/toot muted | \
    awk '$2 !~ /^@.*@/ {print $2 "@'${INSTANCE}'"; next} {print $2}' \
    > ${BACKUPDIR}/muted.new

mv ${BACKUPDIR}/muted{.new,}

exit_if_nonzero_or_stderr /usr/local/bin/toot blocked | \
    awk '$2 !~ /^@.*@/ {print $2 "@'${INSTANCE}'"; next} {print $2}' \
    > ${BACKUPDIR}/blocked.new
mv ${BACKUPDIR}/blocked{.new,}

exit_if_nonzero_or_stderr /usr/local/bin/toot following ${ACCOUNT} | \
    awk '$2 !~ /^@.*@/ {print $2 "@'${INSTANCE}'"; next} {print $2}' \
    > ${BACKUPDIR}/following.new
mv ${BACKUPDIR}/following{.new,}

exit_if_nonzero_or_stderr /usr/local/bin/toot followers ${ACCOUNT} | \
    awk '$2 !~ /^@.*@/ {print $2 "@'${INSTANCE}'"; next} {print $2}' \
    > ${BACKUPDIR}/followers.new
mv ${BACKUPDIR}/followers{.new,}

toot outputs accounts from the local instance without @instance. awk(1) checks for the existence of two @ characters and if they are not present outputs @instance@ after the account name.

Unfortunately toot always exits with 0, even if there is an error so we need to check if there is some output on stderr so that we do not replace our backup with empty files. For that I copy-pasted exit_if_nonzero_or_stderr from stackoverflow like a pro.

My crontab entry looks like this:

~ 9 * * * -sn su -s /bin/sh mastodonbackup -c /home/mastodonbackup/backup.sh

-sn ensures that the script is only run once and that no mail is sent after a successful run. See crontab(5) for details.

Epilogue

I have automated backing up the most important lists from my mastodon account. My mastodon account is running on somebody else's computer. The backup data is written as text files on a system that I control. That system is hooked up to my standard backup solution to have daily, multiple-redundant backups.

I expire my toots after 90 days using ephemetoot. I have configured it to save the toots and media before deleting them from my account.

Footnotes:

1

If a remote admin decides to de-federate your instance it will sever all your connections with people on that instance.

2

My last manual backup was over a year old.

Published: 2023-07-26