A Mastodon server is more than the application files on disk. The database, /home/mastodon/live/.env.production secrets, local media files, Redis state, reverse proxy, certificate path, and DNS record all have to move together when a new machine takes over the same public instance.
A source-install move on Linux usually starts with Mastodon in /home/mastodon/live and systemd units named mastodon-web, mastodon-sidekiq, and mastodon-streaming. Prepare the new machine with the same Mastodon release and dependencies before the final cutover, but do not run a fresh instance setup that would generate a different environment file or empty identity.
The final migration window starts when the old services stop accepting writes. Keep the old host available but read-only until the new machine serves the HTTPS API, WebFinger resolves the same account identity, and background services have restarted with the restored database and media state.
Related: How to back up a Mastodon server
Related: How to configure a Mastodon domain
Related: How to configure S3 media storage for Mastodon
Related: How to upgrade Mastodon
Instance domain: social.example.com Source host: mastodon-old.example.net Target host: mastodon-new.example.net Target address: 203.0.113.20 Migration directory: /home/mastodon/migration/2026-06-27
Use neutral staging paths that are outside /home/mastodon/live so application deploys do not mix with migration artifacts.
Lower the record early enough for older cached answers to expire before the address changes.
Tool: DNS TTL Change Window Calculator
Leave Mastodon application services stopped on the target until the restored environment file, database, media, and Redis dump are in place.
Related: How to install Mastodon from source
$ sudo -u mastodon install -d -m 700 /home/mastodon/migration/2026-06-27
$ sudo systemctl stop mastodon-web mastodon-sidekiq mastodon-streaming
This starts the write-free window. Keep PostgreSQL and Redis running until their dump and snapshot steps complete.
$ sudo -u mastodon cp /home/mastodon/live/.env.production /home/mastodon/migration/2026-06-27/env.production
Protect env.production like a password vault. Losing or replacing these secrets breaks sessions, two-factor authentication, web push subscriptions, and federation signatures.
$ sudo -u mastodon pg_dump -Fc mastodon_production -f /home/mastodon/migration/2026-06-27/mastodon_production.dump
$ sudo -u mastodon pg_restore --list /home/mastodon/migration/2026-06-27/mastodon_production.dump ; ; Archive created at 2026-06-27 09:12:44 UTC ; dbname: mastodon_production ; Format: CUSTOM ##### snipped ##### 3475; 0 16387 TABLE DATA public statuses mastodon
$ sudo -u mastodon rsync -aH /home/mastodon/live/public/system/ mastodon@mastodon-new.example.net:/home/mastodon/live/public/system/
Skip this path only when media already lives in object storage and the restored env.production points at the same bucket and media hostname.
Related: How to configure S3 media storage for Mastodon
$ sudo redis-cli SAVE OK
Redis mainly preserves Sidekiq queues, scheduled retries, and feed cache state. Home feeds can be rebuilt, but a copied dump keeps in-flight background work closer to the source state.
$ sudo install -m 600 -o mastodon -g mastodon /var/lib/redis/dump.rdb /home/mastodon/migration/2026-06-27/redis-dump.rdb
Use the actual Redis dump path when the source server stores dump.rdb somewhere other than /var/lib/redis/dump.rdb.
$ sudo -u mastodon sha256sum /home/mastodon/migration/2026-06-27/env.production /home/mastodon/migration/2026-06-27/mastodon_production.dump /home/mastodon/migration/2026-06-27/redis-dump.rdb a4f3cc9e43c7ff5e73ec01dbe27a9fc7ddbaff042f95d63b47e45807a2c04e89 /home/mastodon/migration/2026-06-27/env.production c38fd5f584f6f9d02e179dad42ff58cfb99999371b5ff6ad39fbc1c2c090ecb5 /home/mastodon/migration/2026-06-27/mastodon_production.dump 22ebe168a91f8b5e9cadc90ac93fa6f8f2ab70bb54df20cf9dacdcdf42bdd87b /home/mastodon/migration/2026-06-27/redis-dump.rdb
$ ssh mastodon@mastodon-new.example.net 'install -d -m 700 /home/mastodon/migration/2026-06-27'
$ sudo -u mastodon rsync -aH /home/mastodon/migration/2026-06-27/ mastodon@mastodon-new.example.net:/home/mastodon/migration/2026-06-27/
$ sudo -u mastodon install -m 600 /home/mastodon/migration/2026-06-27/env.production /home/mastodon/live/.env.production
$ sudo -u mastodon createdb -T template0 mastodon_production
Run this only against an empty target database. Drop or rename any accidental test database before restoring the production dump.
$ sudo -u mastodon pg_restore --jobs=4 --schema=public --no-owner --role=mastodon --dbname=mastodon_production /home/mastodon/migration/2026-06-27/mastodon_production.dump
Set --jobs to a value the target database server can handle. The restore can take a long time on large instances.
$ sudo -u mastodon psql -d mastodon_production -c "SELECT username, domain FROM accounts WHERE username = 'alice' LIMIT 1;" username | domain ----------+-------- alice | (1 row)
$ sudo systemctl stop redis-server
$ sudo install -m 660 -o redis -g redis /home/mastodon/migration/2026-06-27/redis-dump.rdb /var/lib/redis/dump.rdb
Keep Redis stopped while replacing dump.rdb so it cannot overwrite the restored file.
$ sudo -u mastodon env RAILS_ENV=production /home/mastodon/live/bin/rails assets:precompile Done in 38.42s.
$ sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo systemctl start redis-server mastodon-web mastodon-sidekiq mastodon-streaming
$ sudo systemctl is-active mastodon-web mastodon-sidekiq mastodon-streaming active active active
$ sudo -u mastodon env RAILS_ENV=production /home/mastodon/live/bin/tootctl feeds build
This can take a long time on instances with many local users. Home feed state can be regenerated after the database, environment, and services are restored.
$ sudo -u mastodon env RAILS_ENV=production /home/mastodon/live/bin/tootctl search deploy
Skip this step only when full-text search is not configured.
Related: How to configure Mastodon full-text search
Keep the old host available during the DNS transition so stale resolvers do not reach a powered-off machine.
Tool: DNS Propagation Checker
$ curl -I https://social.example.com/api/v2/instance HTTP/2 200 server: nginx content-type: application/json; charset=utf-8 cache-control: max-age=300, public x-request-id: 8f9e4c1a9b2d4e6f strict-transport-security: max-age=63072000; includeSubDomains
$ curl -s 'https://social.example.com/.well-known/webfinger?resource=acct:alice@social.example.com'
{"subject":"acct:alice@social.example.com","links":[{"rel":"self","type":"application/activity+json","href":"https://social.example.com/users/alice"}]}
For a split-domain setup, send the WebFinger request to the account domain and keep resource aligned with LOCAL_DOMAIN.
Related: How to configure a Mastodon domain
$ sudo rm -rf /home/mastodon/migration/2026-06-27
Do not remove the source backup or shut down the old host until DNS, API access, WebFinger, Sidekiq processing, and media loading have been checked through the maintenance window.