···11-# Account Migration
11+# Account Migration
22+33+**Update May 2025:** An updated guide to account migration is now [part of the atproto specifications](https://atproto.com/guides/account-migration). There is also [a blog post available](https://whtwnd.com/bnewbold.net/3l5ii332pf32u) which describes how to do an account migration using a command-line tool (`goat`).
2435### ⚠️ Warning ⚠️ ️
44-Account migration is a potentially destructive operation. Part of the operation involves signing away your old PDS's ability to make updates to your DID. If something goes wrong, you could be permanently locked out of your account, and Bluesky will not be able to help you recover it.
66+Account migration is a potentially destructive operation. Part of the operation involves signing away your old PDS's ability to make updates to your DID. If something goes wrong, you could be permanently locked out of your account, and Bluesky will not be able to help you recover it.
5768Therefore, we do not recommend migrating your primary account yet. And we specifically recommend _against_ migrating your main account if you do not understand how PLC operations work.
7988-Also, the Bluesky PDS is not currently accepting incoming migrations (it will in the future). Therefore this is currently a one-way street. If you migrate off of `bsky.social`, _you will not be able to return_. However, you will be able to migrate between other PDSs.
99-1010
11111212Account Migration occurs in 4 main steps:
···22222323To do so, you need a JWT signed with the signing key associated with your DID. You can obtain this through calling `com.atproto.server.getServiceAuth` from your old PDS. If your old PDS is not willing to provide the authentication token, you will need to update your DID document to point to a signing key that you possess in order to mint an authentication token yourself.
24242525-With this JWT set as a Bearer token, you can then create an account on the new PDS by calling `com.atproto.server.createAccount`. You'll need to fulfill any challenges that the new PDS requires - such as an invite code.
2525+With this JWT set as a Bearer token, you can then create an account on the new PDS by calling `com.atproto.server.createAccount`. You'll need to fulfill any challenges that the new PDS requires - such as an invite code.
26262727After creating an account, you'll have a signing key on the new PDS and an empty repository. Your account will be in a "deactivated" state such that it is not usable yet.
2828···32323333First, you can grab your entire repository in the form of a [CAR file](https://ipld.io/specs/transport/car/carv1/) by calling `com.atproto.sync.getRepo`. You can then upload those exact bytes to your new PDS through `com.atproto.repo.importRepo`. The new PDS will parse the CAR file, index all blocks and records, and sign a new commit for the repository.
34343535-Next, you'll need to upload all relevant blobs. These can be discovered by calling `com.atproto.sync.listBlobs` on your old PDS. For each blob, you'll need to download the contents through `com.atproto.sync.getBlob` and upload them to your new PDS through `com.atproto.repo.uploadBlob`.
3535+Next, you'll need to upload all relevant blobs. These can be discovered by calling `com.atproto.sync.listBlobs` on your old PDS. For each blob, you'll need to download the contents through `com.atproto.sync.getBlob` and upload them to your new PDS through `com.atproto.repo.uploadBlob`.
36363737Finally, you'll need to migrate private state. Currently the only private state held on your PDS is your preferences. You can migrate this by calling `app.bsky.actor.getPreferences` on your old PDS, and submitting the results to `app.bsky.actor.putPreferences` on your new PDS.
3838
+2-1
Dockerfile
···11FROM node:20.11-alpine3.18 as build
2233-RUN npm install -g pnpm
33+RUN corepack enable
4455# Move files into the image and install
66WORKDIR /app
77COPY ./service ./
88+RUN corepack prepare --activate
89RUN pnpm install --production --frozen-lockfile > /dev/null
9101011# Uses assets from build stage to reduce build size
+33
PUBLISH.md
···11+# Publishing a new version of the PDS distro
22+33+Below are the steps to publish a new version of the PDS distribution. The distribution is hosted by GitHub Container Registry, supported by the `build-and-push-ghcr` workflow. We use git tags to generate Docker tags on the resulting images.
44+55+1. Update the @atproto/pds dependency in the `service/` directory.
66+77+ We're using version `0.4.999` as an example. The latest version of the [`@atproto/pds` package](https://www.npmjs.com/package/@atproto/pds) must already be published on npm.
88+ ```sh
99+ $ cd service/
1010+ $ pnpm update @atproto/pds@0.4.999
1111+ $ cd ..
1212+ ```
1313+1414+2. Commit the change directly to `main`.
1515+1616+ As soon as this is committed and pushed, the workflow to build the Docker image will start running.
1717+ ```sh
1818+ $ git add service/
1919+ $ git commit -m "pds v0.4.999"
2020+ $ git push
2121+ ```
2222+2323+3. Smoke test the new Docker image.
2424+2525+ The new Docker image built by GitHub can be found [here](https://github.com/bluesky-social/pds/pkgs/container/pds). You can use the `sha-`prefixed tag to deploy this image to a test PDS for smoke testing.
2626+2727+4. Finally, tag the latest Docker image version.
2828+2929+ The Docker image will be tagged as `latest`, `0.4.999`, and `0.4`. Our self-hosters generally use the `0.4` tag, and their PDS distribution will be updated automatically over night in many cases. The Docker tags are generated automatically from git tags.
3030+ ```sh
3131+ $ git tag v0.4.999
3232+ $ git push --tags
3333+ ```
+131-41
README.md
···2233Welcome to the repository for the official Bluesky PDS (Personal Data Server). This repository includes container images and documentation designed to assist technical people with hosting a Bluesky PDS.
4455-Head over to the [AT Protocol PDS Admins Discord](https://discord.gg/e7hpHxRfBP) to chat with other folks hosting instances and get important updates about the PDS distribution!
55+Head over to the [ATProto Touchers Discord](https://discord.atprotocol.dev/) to chat with other folks hosting instances and get important updates about the PDS distribution!
6677## Table of Contents
88···10101111<!-- toc -->
12121313-- [FAQ](#faq)
1414- * [What is Bluesky?](#what-is-bluesky)
1515- * [What is AT Protocol?](#what-is-at-protocol)
1616- * [Where is the code?](#where-is-the-code)
1717- * [What is the current status of federation?](#what-is-the-current-status-of-federation)
1818-- [Self-hosting PDS](#self-hosting-pds)
1919- * [Preparation for self-hosting PDS](#preparation-for-self-hosting-pds)
2020- * [Open your cloud firewall for HTTP and HTTPS](#open-your-cloud-firewall-for-http-and-https)
2121- * [Configure DNS for your domain](#configure-dns-for-your-domain)
2222- * [Check that DNS is working as expected](#check-that-dns-is-working-as-expected)
2323- * [Installer on Ubuntu 20.04/22.04 and Debian 11/12](#installer-on-ubuntu-20042204-and-debian-1112)
2424- * [Verifying that your PDS is online and accessible](#verifying-that-your-pds-is-online-and-accessible)
2525- * [Creating an account using pdsadmin](#creating-an-account-using-pdsadmin)
2626- * [Creating an account using an invite code](#creating-an-account-using-an-invite-code)
2727- * [Using the Bluesky app with your PDS](#using-the-bluesky-app-with-your-pds)
2828- * [Setting up SMTP](#setting-up-smtp)
2929- * [Updating your PDS](#updating-your-pds)
3030- * [Environment Variables](#environment-variables)
1313+- [PDS](#pds)
1414+ - [Table of Contents](#table-of-contents)
1515+ - [FAQ](#faq)
1616+ - [What is Bluesky?](#what-is-bluesky)
1717+ - [What is AT Protocol?](#what-is-at-protocol)
1818+ - [Where is the code?](#where-is-the-code)
1919+ - [What is the current status of federation?](#what-is-the-current-status-of-federation)
2020+ - [Self-hosting a PDS](#self-hosting-a-pds)
2121+ - [Deploying a PDS onto a VPS](#deploying-a-pds-onto-a-vps)
2222+ - [Open your cloud firewall for HTTP and HTTPS](#open-your-cloud-firewall-for-http-and-https)
2323+ - [Configure DNS for your domain](#configure-dns-for-your-domain)
2424+ - [Check that DNS is working as expected](#check-that-dns-is-working-as-expected)
2525+ - [Installing on Ubuntu 20.04/22.04/24.04 and Debian 11/12/13](#installing-on-ubuntu-200422042404-and-debian-111213)
2626+ - [Verifying that your PDS is online and accessible](#verifying-that-your-pds-is-online-and-accessible)
2727+ - [Creating an account using pdsadmin](#creating-an-account-using-pdsadmin)
2828+ - [Creating an account using an invite code](#creating-an-account-using-an-invite-code)
2929+ - [Using the Bluesky app with your PDS](#using-the-bluesky-app-with-your-pds)
3030+ - [Setting up SMTP](#setting-up-smtp)
3131+ - [Common SMTP issues](#common-smtp-issues)
3232+ - [Logging](#logging)
3333+ - [Updating your PDS](#updating-your-pds)
3434+ - [Environment Variables](#environment-variables)
3535+ - [License](#license)
31363237<!-- tocstop -->
3338···52575358### What is the current status of federation?
54595555-As of Spring 2024, the AT Protocol network is open to federation!
6060+The AT Protocol network is open to federation!
56615762✅ Federated domain handles (e.g. `@nytimes.com`)
5863···66716772✅ Federated moderation (labeling)
68736969-## Self-hosting PDS
7474+## Self-hosting a PDS
70757176Self-hosting a Bluesky PDS means running your own Personal Data Server that is capable of federating with the wider Bluesky social network.
72777373-### Preparation for self-hosting PDS
7878+### Deploying a PDS onto a VPS
74797575-Launch a server on any cloud provider, [Digital Ocean](https://digitalocean.com/) and [Vultr](https://vultr.com/) are two popular choices.
8080+This README provides instructions for deploying a PDS using our install script onto a Virtual Private Server. [Digital Ocean](https://digitalocean.com/) and [Vultr](https://vultr.com/) are two popular choices for VPS hosting.
76817782Ensure that you can ssh to your server and have root access.
7883···8489**Server Recommendations**
8590| | |
8691| ---------------- | ------------ |
8787-| Operating System | Ubuntu 22.04 |
9292+| Operating System | Ubuntu 24.04 |
8893| Memory (RAM) | 1 GB |
8994| CPU Cores | 1 |
9095| Storage | 20 GB SSD |
···131136132137These should all return your server's public IP.
133138134134-### Installer on Ubuntu 20.04/22.04 and Debian 11/12
139139+### Installing on Ubuntu 20.04/22.04/24.04 and Debian 11/12/13
135140136136-On your server via ssh, download the installer script using wget:
141141+Note that this script assumes a relatively "fresh" VPS that is not also concurrently hosting a web server or anything else on port 80/443. If you intend to run a PDS alongside an existing webserver on the same VPS, you will not want to use this install script.
142142+143143+On your server, download the install script using `curl`:
137144138145```bash
139139-wget https://raw.githubusercontent.com/bluesky-social/pds/main/installer.sh
146146+curl https://raw.githubusercontent.com/bluesky-social/pds/main/installer.sh > installer.sh
140147```
141148142142-or download it using curl:
149149+And then run the installer using `bash`. You will need `sudo` permissions to continue:
143150144151```bash
145145-curl https://raw.githubusercontent.com/bluesky-social/pds/main/installer.sh >installer.sh
152152+sudo bash installer.sh
146153```
147154148148-And then run the installer using bash:
155155+The install script is interactive and will prompt for input during the install process. You will need to provide your public DNS address, an admin email address (which does not need to be from the same domain), and be prompted to create a PDS user account with its own email address and handle. If you plan to reuse an existing AT handle, you can skip user account creation, though if it is your first time deploying a PDS you may want to create an account using your domain like `account.your-domain.net` for testing purposes.
149156150150-```bash
151151-sudo bash installer.sh
157157+Upon completion of a successful installation, you'll receive output similar to the following:
158158+159159+```
160160+========================================================================
161161+PDS installation successful!
162162+------------------------------------------------------------------------
163163+164164+Check service status : sudo systemctl status pds
165165+Watch service logs : sudo docker logs -f pds
166166+Backup service data : /pds
167167+PDS Admin command : pdsadmin
168168+169169+Required Firewall Ports
170170+------------------------------------------------------------------------
171171+Service Direction Port Protocol Source
172172+------- --------- ---- -------- ----------------------
173173+HTTP TLS verification Inbound 80 TCP Any
174174+HTTP Control Panel Inbound 443 TCP Any
175175+176176+Required DNS entries
177177+------------------------------------------------------------------------
178178+Name Type Value
179179+------- --------- ---------------
180180+your-domain.net A your-ip-address
181181+*.your-domain.net A your-ip-address
182182+183183+Detected public IP of this server: your-ip-address
184184+185185+To see pdsadmin commands, run "pdsadmin help"
186186+187187+========================================================================
188188+```
189189+190190+And, following account creation:
191191+192192+```
193193+Account created successfully!
194194+-----------------------------
195195+Handle : handle.your-domain.net
196196+DID : did:plc:your-did
197197+Password : your-password
198198+-----------------------------
199199+Save this password, it will not be displayed again.
152200```
153201154202### Verifying that your PDS is online and accessible
155203156204> [!TIP]
157157-> The most common problems with getting PDS content consumed in the live network are when folks substitute the provided Caddy configuration for nginx, apache, or similar reverse proxies. Getting TLS certificates, WebSockets, and virtual server names all correct can be tricky. We are not currently providing tech support for other configurations.
205205+> The most common problems with getting PDS content consumed in the live network usually result from users trying to port the provided Caddy configuration to Nginx, Apache, or other reverse proxies. Getting TLS certificates, WebSockets, and virtual server names provisioned can be challenging. We are not currently providing tech support for other configurations.
158206159159-You can check if your server is online and healthy by requesting the healthcheck endpoint.
207207+After installation, your PDS should be live and accessible on the web. You can check if your server is online and healthy by making a request to `https://your-domain.net/xrpc/_health` (the healthcheck endpoint). You should see a JSON response with a version, like:
160208161161-You can visit `https://example.com/xrpc/_health` in your browser. You should see a JSON response with a version, like:
209209+Visit `https://your-domain.net/xrpc/_health` in your browser. You should see a JSON response with a version, like:
162210163211```
164212{"version":"0.2.2-beta.2"}
···170218wsdump "wss://example.com/xrpc/com.atproto.sync.subscribeRepos?cursor=0"
171219```
172220173173-Note that there will be no events output on the WebSocket until they are created in the PDS, so the above command may continue to run with no output if things are configured successfully.
221221+Note that there will be no events output on the WebSocket until they are created in the PDS, so the above command may continue to run with no output immediately post-installation.
174222175223### Creating an account using pdsadmin
176224177177-Using ssh on your server, use `pdsadmin` to create an account if you haven't already.
225225+You'll now have access to some additional command line tools on this server. Use `pdsadmin` to create an account if you haven't already:
178226179227```bash
180228sudo pdsadmin account create
···182230183231### Creating an account using an invite code
184232185185-Using ssh on your server, use `pdsadmin` to create an invite code.
233233+If needed, use `pdsadmin` to create an invite code:
186234187235```bash
188236sudo pdsadmin create-invite-code
···206254207255To be able to verify users' email addresses and send other emails, you need to set up an SMTP server.
208256209209-One way to do this is to use an email service. [Resend](https://resend.com/) and [SendGrid](https://sendgrid.com/) are two popular choices.
257257+As an alternative to running your own SMTP server, you can use an email service. [Resend](https://resend.com/) and [SendGrid](https://sendgrid.com/) are two popular choices.
210258211211-Create an account and API key on an email service, ensure your server allows access on the required ports, and set these variables in `/pds/pds.env` (example with Resend):
259259+Create an account and API key on an email service, ensure your server allows access on the required ports, and then you can add these configuration variables to `/pds/pds.env` on your server (example with Resend):
212260213261```
214262PDS_EMAIL_SMTP_URL=smtps://resend:<your api key here>@smtp.resend.com:465/
215263PDS_EMAIL_FROM_ADDRESS=admin@your.domain
216264```
217265266266+If you prefer to use a standard SMTP server (a local one or from your email provider), put your account's username and password in the URL:
267267+268268+```
269269+PDS_EMAIL_SMTP_URL=smtps://username:password@smtp.example.com/
270270+```
271271+272272+Alternatively, if you're running a local sendmail-compatible mail service like Postfix or Exim on the same host, you can configure the PDS to use the sendmail transport by using such URL:
273273+274274+```
275275+PDS_EMAIL_SMTP_URL=smtp:///?sendmail=true
276276+```
277277+218278_Note: Your PDS will need to be restarted with those variables. This varies depending on your setup. If you followed this installation guide, run `systemctl restart pds`. You might need to restart the server or recreate the container, depending on what you are using._
219279280280+#### Common SMTP issues
281281+282282+If you find that your test messages using cURL or other sources go out correctly, but you are not receiving emails from your PDS, you may need to URL encode your username and password on `/pds/pds.env` and restart the PDS service.
283283+284284+If the username and/or password contain special characters, the special characters will need to be [percent encoded](https://en.wikipedia.org/wiki/Percent-encoding). For some email services, the username will contain an extra `@` symbol that will also need to be percent encoded. For example, the URL `user&name@oci:p@ssword@smtphost:465` after percent encoding for the username and password fields would become `user%26name%40oci:p%40ssword@smtphost:465`.
285285+286286+If you are migrating an account, Bluesky's UI will ask you to confirm your email address. The confirmation code email is meant to come from your PDS. If you are encountering issues with SMTP and want to confirm the address before solving it, you can find the confirmation code on the `email_token` table on `accounts.sqlite`.
287287+288288+### Logging
289289+290290+By default, logs from the PDS are printed to `stdout` and end up in Docker's log. You can browse them by running:
291291+292292+```
293293+[sudo] docker logs pds
294294+```
295295+296296+Note: these logs are not persisted, so they will be lost after server reboot.
297297+298298+Alternatively, you can configure the logs to be printed to a file by setting `LOG_DESTINATION`:
299299+300300+```
301301+LOG_DESTINATION=/pds/pds.log
302302+```
303303+304304+You can also change the minimum level of logs to be printed (default: `info`):
305305+306306+```
307307+LOG_LEVEL=debug
308308+```
309309+220310### Updating your PDS
221311222222-It is recommended that you keep your PDS up to date with new versions, otherwise things may break. You can use the `pdsadmin` tool to update your PDS.
312312+It is recommended that you keep your PDS up to date with new versions. You can use the `pdsadmin` tool to update your PDS.
223313224314```bash
225315sudo pdsadmin update
+8-11
installer.sh
···11-#!/bin/bash
11+#!/usr/bin/env bash
22set -o errexit
33set -o nounset
44set -o pipefail
···3131 openssl
3232 sqlite3
3333 xxd
3434+ jq
3435"
3536# Docker packages.
3637REQUIRED_DOCKER_PACKAGES="
···9394 elif [[ "${DISTRIB_CODENAME}" == "jammy" ]]; then
9495 SUPPORTED_OS="true"
9596 echo "* Detected supported distribution Ubuntu 22.04 LTS"
9696- elif [[ "${DISTRIB_CODENAME}" == "mantic" ]]; then
9797+ elif [[ "${DISTRIB_CODENAME}" == "noble" ]]; then
9798 SUPPORTED_OS="true"
9898- echo "* Detected supported distribution Ubuntu 23.10 LTS"
9999+ echo "* Detected supported distribution Ubuntu 24.04 LTS"
99100 fi
100101 elif [[ "${DISTRIB_ID}" == "debian" ]]; then
101102 if [[ "${DISTRIB_CODENAME}" == "bullseye" ]]; then
···104105 elif [[ "${DISTRIB_CODENAME}" == "bookworm" ]]; then
105106 SUPPORTED_OS="true"
106107 echo "* Detected supported distribution Debian 12"
108108+ elif [[ "${DISTRIB_CODENAME}" == "trixie" ]]; then
109109+ SUPPORTED_OS="true"
110110+ echo "* Detected supported distribution Debian 13"
107111 fi
108112 fi
109113110114 if [[ "${SUPPORTED_OS}" != "true" ]]; then
111111- echo "Sorry, only Ubuntu 20.04, 22.04, Debian 11 and Debian 12 are supported by this installer. Exiting..."
115115+ echo "Sorry, only Ubuntu 20.04, 22.04, 24.04, and Debian 11, 12, and 13 are supported by this installer. Exiting..."
112116 exit 1
113117 fi
114118···214218 fi
215219216220 # Admin email
217217- if [[ -z "${PDS_ADMIN_EMAIL}" ]]; then
218218- read -p "Enter an admin email address (e.g. you@example.com): " PDS_ADMIN_EMAIL
219219- fi
220220- if [[ -z "${PDS_ADMIN_EMAIL}" ]]; then
221221- usage "No admin email specified"
222222- fi
223223-224221 if [[ -z "${PDS_ADMIN_EMAIL}" ]]; then
225222 read -p "Enter an admin email address (e.g. you@example.com): " PDS_ADMIN_EMAIL
226223 fi