Migrating a Bluesky account to a new handle or a different hosting provider can feel risky because you must transfer your posts, follows, and account settings without losing data. The goat CLI is an open-source command-line tool that automates the entire migration process by reading your existing account and writing it to a new Bluesky account. This article explains how to install the goat CLI, what prerequisites you need, and the exact steps to migrate your account safely. You will also learn how to verify the migration and handle common errors during the transfer.
Key Takeaways: Migrate a Bluesky Account Using goat CLI
- goat CLI install command:
npm install -g @atproto/goatinstalls the tool globally on your system. - goat account migrate command:
goat account migrate --from-handle oldhandle.bsky.social --to-handle newhandle.bsky.socialtransfers posts, follows, and profile data. - Manual domain verification step: You must add a TXT DNS record for a custom domain handle before migration completes.
What the goat CLI Does for Bluesky Account Migration
The goat CLI is the official command-line interface for the AT Protocol that Bluesky runs on. It provides a dedicated account migrate subcommand that copies your entire account state from one Bluesky handle to another. The tool transfers your posts, reposts, likes, follows, lists, and profile metadata. It does not transfer direct messages because Bluesky does not store DMs in a portable format yet.
Before you start, you need the following items ready:
- Node.js version 18 or later installed on Windows, macOS, or Linux.
- Two Bluesky accounts that both exist and are active. The source account is the one you are leaving. The destination account is the new one that receives the data.
- App passwords for both accounts. Generate them in Bluesky Settings > App Passwords.
- DNS control if you are migrating to a custom domain handle. You must add a TXT record to prove domain ownership.
Steps to Install goat CLI and Migrate Your Bluesky Account
The migration process has three phases: installation, migration command execution, and domain verification if applicable. Follow these steps in order.
Phase 1: Install goat CLI on Your Machine
- Open your terminal
On Windows, open Command Prompt or PowerShell as administrator. On macOS or Linux, open Terminal. - Install goat globally
Typenpm install -g @atproto/goatand press Enter. Wait for the installation to finish. You will see a success message when done. - Verify the installation
Rungoat --version. The output shows the version number. If you see an error, ensure Node.js is installed and that npm is in your system PATH.
Phase 2: Run the Migration Command
- Generate app passwords for both accounts
Log in to Bluesky on the web. Go to Settings > App Passwords. Create one app password for the source account and one for the destination account. Copy each password to a secure text file. - Execute the migrate command
In your terminal, run the following command. Replace the placeholder values with your actual handles and app passwords.goat account migrate --from-handle oldhandle.bsky.social --from-password OLD_APP_PASSWORD --to-handle newhandle.bsky.social --to-password NEW_APP_PASSWORD - Wait for the tool to process
The CLI will display a progress bar. It first reads all records from the source account, then writes them to the destination account. The time depends on the number of posts and follows. A typical account with 5000 posts takes about 10 minutes. - Check the final output
When finished, you see a summary line that says “Migration complete.” The tool also prints the total number of records transferred.
Phase 3: Verify Domain Handle Ownership (Custom Domain Only)
If your destination handle is a custom domain such as user.example.com, you must verify domain ownership before Bluesky accepts the migrated account. The goat CLI does not do this step automatically.
- Add a TXT record to your DNS
Log in to your domain registrar or DNS hosting provider. Create a TXT record with the name_atprotoand the valuedid=did:plc:YOUR_DESTINATION_DID. You can find the DID in the migration output or by runninggoat account get --handle newhandle.bsky.social. - Wait for DNS propagation
DNS changes can take up to 48 hours, but most providers update within 30 minutes. Use a tool likedig _atproto.yourdomain.com TXTto verify the record is visible. - Confirm the handle in Bluesky
Open Bluesky Settings > Account > Handle. Click “I have my own domain” and enter your custom domain. Bluesky checks the TXT record and activates the handle if the DID matches.
Common Migration Problems and How to Resolve Them
“Account not found” Error When Running the Command
This error means the handle or app password is incorrect. Double-check that you typed the handle exactly, including the .bsky.social suffix or your custom domain. Regenerate the app password if you are unsure it is correct.
Migration Stops at “Reading timeline” and Never Finishes
The goat CLI can hang if the source account has a very large number of records or if Bluesky servers are under load. Press Ctrl+C to cancel the command. Then run the command again with the --resume flag. The tool picks up from where it stopped instead of starting over.
Posts Appear Twice on the Destination Account
This happens when you run the migration command more than once without cleaning the destination account. The tool does not deduplicate records. To fix this, delete the destination account entirely and create a fresh one before re-running the migration.
Custom Domain Handle Shows “Unverified” in Bluesky
The DNS TXT record is either missing, has a typo in the DID value, or has not propagated. Verify the record with a DNS lookup tool. If the record is correct, wait 30 minutes and refresh the Bluesky handle settings page.
goat CLI Migration vs Manual Account Recreation
| Item | goat CLI Migration | Manual Account Recreation |
|---|---|---|
| Setup time | 10-15 minutes | Several hours |
| Posts transferred | All posts including text and embedded media links | None unless manually copied |
| Follows transferred | All follow relationships | Must refollow each account individually |
| Lists and feeds | All user lists and saved feeds | Lost completely |
| Risk of data loss | Low if app passwords are correct | High because manual copying is error-prone |
You now have a fully migrated Bluesky account with your posts, follows, and profile data intact. To confirm everything is working, log in to the destination account on the Bluesky web app and browse your timeline and profile. For advanced users, the goat CLI also supports the account export command to back up your data as JSON files before migrating. Use goat account export --handle yourhandle.bsky.social --password YOUR_APP_PASSWORD > backup.json to create a local archive you can restore later.