Moving a self-hosted Mastodon instance to a new server can feel like a high-risk operation. You need to transfer user accounts, posts, media files, and the relational database without losing data or breaking federation. This article explains the full migration process using the official Mastodon backup and restore tools.
The root cause of migration complexity is Mastodon’s reliance on PostgreSQL, Redis, and object storage. If any component is out of sync, the new instance may fail to serve timelines or accept new posts. This guide covers the exact steps to export data from the old server and import it on the new server.
After reading, you will be able to move your instance with minimal downtime and verify that federation continues to work. The final section covers common pitfalls such as SSL certificate mismatches and database encoding errors.
Key Takeaways: Mastodon Instance Migration Between Servers
- pg_dump and pg_restore: Export and import the PostgreSQL database with the –format=custom flag to preserve indexes and constraints.
- rsync for media files: Copy system-media- directories and the public/system folder to the new server while preserving permissions.
- RAILS_ENV=production bundle exec rake db:migrate: Run database migrations after restore to update schema to the current Mastodon version.
Why Mastodon Migration Requires a Database Dump and Media Sync
Mastodon stores all user data, posts, and relationships in a PostgreSQL database. Media files such as images, videos, and custom emoji reside on the filesystem or in object storage. To migrate, you must transfer both the database and the media files. The database dump must be created with pg_dump --format=custom because this format preserves the exact schema, including indexes, sequences, and constraints. A plain SQL dump may cause foreign key violations during restore on the new server.
Redis cache data does not need to be migrated. Mastodon regenerates caches from the database after the first request. However, you must copy the .env.production configuration file because it contains the secret keys for OAuth tokens, VAPID keys, and the database password. Without these secrets, the new instance will issue new tokens and invalidate all existing user sessions.
Object storage users (S3/MinIO) can skip the media sync step. Instead, update the S3_ environment variables in .env.production to point to the same bucket. The new server will serve media directly from that bucket.
Steps to Migrate a Self-Hosted Mastodon Instance to a New Server
Prepare the New Server
- Install Mastodon dependencies
On the new server, install the same operating system and software versions as the old server. Use the official Mastodon installation guide for your distro. Install PostgreSQL 12 or later, Redis 6 or later, Ruby 3.0+, Node.js 16+, and all required system packages. - Create the Mastodon user and directories
Runadduser --disabled-login mastodonand create the/home/mastodon/livedirectory. Set ownership to the mastodon user. - Clone the Mastodon codebase
As the mastodon user, rungit clone https://github.com/mastodon/mastodon.git /home/mastodon/liveand check out the same version tag as the old server. Usegit checkout v4.2.0(replace with your version). - Install Ruby gems and Node packages
Runbundle install --deployment --without development testandyarn install --pure-lockfileinside/home/mastodon/live.
Export Data from the Old Server
- Stop Mastodon services
On the old server, runsystemctl stop mastodon-to stop web, streaming, and sidekiq processes. This prevents new data from being written during the export. - Dump the PostgreSQL database
Runpg_dump --format=custom mastodon_production > /tmp/mastodon_db.dumpas the postgres user. Replacemastodon_productionwith your actual database name. The custom format is required for reliable restore. - Copy the .env.production file
Copy/home/mastodon/live/.env.productionto a safe location. This file contains SECRET_KEY_BASE, OTP_SECRET, and VAPID keys. Without it, the new server will not be able to decrypt existing user tokens. - Sync media files with rsync
Runrsync -avz --delete /home/mastodon/live/public/system/ user@new-server:/home/mastodon/live/public/system/. The –delete flag removes files on the new server that no longer exist on the old server. If you use object storage, skip this step.
Import Data to the New Server
- Copy the database dump and .env.production to the new server
Use scp or rsync to transfer/tmp/mastodon_db.dumpand.env.productionto the new server. Place.env.productionin/home/mastodon/live/and set ownership to the mastodon user. - Restore the PostgreSQL database
Create an empty database on the new server:createdb -O mastodon mastodon_production. Then restore:pg_restore --dbname=mastodon_production /tmp/mastodon_db.dumpas the postgres user. The restore will recreate all tables, indexes, and sequences. - Run database migrations
Switch to the mastodon user and runcd /home/mastodon/live && RAILS_ENV=production bundle exec rails db:migrate. This updates the schema to the current version of the codebase. If you skipped this step, the instance may throw ActiveRecord::StatementInvalid errors. - Precompile assets
RunRAILS_ENV=production bundle exec rails assets:precompile. This generates CSS and JavaScript files for the web interface. - Start Mastodon services
Runsystemctl start mastodon-web mastodon-streaming mastodon-sidekiq. Check logs withjournalctl -u mastodon-web -ffor any startup errors.
Verify the Migration
- Test the web interface
Open the instance URL in a browser. You should see the login page and existing posts. Log in with an existing account and verify that the home timeline shows posts from before the migration. - Check federation
Search for a remote account from a different instance. If the search returns results, federation is working. If not, verify that the DNS A record and reverse DNS for the new server IP are correct. - Send a test post
Create a new post and verify it appears on the public timeline after a few seconds. Check that media attachments load correctly.
Common Migration Problems and Their Solutions
Database Restore Fails with “ERROR: role ‘mastodon’ does not exist”
The pg_restore command requires the PostgreSQL role that owns the database. Create the role on the new server with createuser -s mastodon before running the restore. The -s flag gives superuser privileges, which are needed for some extensions.
Media Files Not Loading After Migration
If media files are missing, verify the path /home/mastodon/live/public/system exists on the new server and has the correct ownership. Run chown -R mastodon:mastodon /home/mastodon/live/public/system. If you use object storage, ensure the S3_ENABLED variable is set to true in .env.production and the bucket name is correct.
User Sessions Invalidated After Migration
This happens when SECRET_KEY_BASE in .env.production does not match the old value. Copy the exact file from the old server. Do not regenerate the secret. If you lost the file, users must log out and log back in. You can force a session reset by running RAILS_ENV=production bundle exec rails db:seed on the new server, but this will delete all existing sessions.
Federation Broken After IP Change
Mastodon uses the instance domain name for federation, not the IP address. Update the DNS A record to point to the new server IP before starting Mastodon services. Wait for DNS propagation (TTL time). After propagation, other instances will send activities to the new IP automatically.
| Item | Filesystem Storage | Object Storage (S3) |
|---|---|---|
| Media transfer method | rsync public/system directory | Update S3_ env vars only |
| Downtime required | Medium (media sync time) | Low (database only) |
| Risk of data loss | High if rsync interrupted | Low (media already remote) |
| Network bandwidth | High (full media copy) | None for media |
You can now migrate a self-hosted Mastodon instance to a new server by exporting the PostgreSQL database, copying the .env.production file, and syncing media files via rsync or object storage. After restoring the database and running migrations, verify the web interface and federation. For a faster migration with less downtime, switch to object storage on the old server before moving. This eliminates the media sync step entirely.