Welcome 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.
Head over to the ATProto Touchers Discord to chat with other folks hosting instances and get important updates about the PDS distribution!
- PDS
- Table of Contents
 - FAQ
 - Self-hosting a PDS
- Deploying a PDS onto a VPS
 - Open your cloud firewall for HTTP and HTTPS
 - Configure DNS for your domain
 - Check that DNS is working as expected
 - Installing on Ubuntu 20.04/22.04/24.04 and Debian 11/12/13
 - Verifying that your PDS is online and accessible
 - Creating an account using pdsadmin
 - Creating an account using an invite code
 - Using the Bluesky app with your PDS
 - Setting up SMTP
 - Logging
 - Updating your PDS
 
 - License
 
 
Bluesky is a social media application built on AT Protocol.
Please visit the Bluesky website for more information.
The Authenticated Transfer Protocol, aka atproto, is a protocol for large-scale distributed social applications.
Please visit the AT Protocol docs for additional information.
The AT Protocol network is open to federation!
âś… Federated domain handles (e.g. @nytimes.com)
âś… Federated feed generators (custom algorithms)
âś… Federated relays (event firehose)
âś… Federated app views (API service)
âś… Federated data (PDS hosting)
âś… Federated moderation (labeling)
Self-hosting a Bluesky PDS means running your own Personal Data Server that is capable of federating with the wider Bluesky social network.
This README provides instructions for deploying a PDS using our install script onto a Virtual Private Server. Digital Ocean and Vultr are two popular choices for VPS hosting.
Ensure that you can ssh to your server and have root access.
Server Requirements
- Public IPv4 address
 - Public DNS name
 - Public inbound internet access permitted on port 80/tcp and 443/tcp
 
Server Recommendations
| Operating System | Ubuntu 24.04 | 
| Memory (RAM) | 1 GB | 
| CPU Cores | 1 | 
| Storage | 20 GB SSD | 
| Architectures | amd64, arm64 | 
| Number of users | 1-20 | 
Note: It is a good security practice to restrict inbound ssh access (port 22/tcp) to your own computer's public IP address. You can check your current public IP address using ifconfig.me.
One of the most common sources of misconfiguration is not opening firewall ports correctly. Please be sure to double check this step.
In your cloud provider's console, the following ports should be open to inbound access from the public internet.
- 80/tcp (Used only for TLS certification verification)
 - 443/tcp (Used for all application requests)
 
Note: there is no need to set up TLS or redirect requests from port 80 to 443 because the Caddy web server, included in the Docker compose file, will handle this for you.
From your DNS provider's control panel, set up a domain with records pointing to your server.
| Name | Type | Value | TTL | 
|---|---|---|---|
example.com | 
A | 
12.34.56.78 | 
600 | 
*.example.com | 
A | 
12.34.56.78 | 
600 | 
Note:
- Replace 
example.comwith your domain name. - Replace 
12.34.56.78with your server's IP address. - Some providers may use the 
@symbol to represent the root of your domain. - The wildcard record is required when allowing users to create new accounts on your PDS.
 - The TTL can be anything but 600 (10 minutes) is reasonable
 
Use a service like DNS Checker to verify that you can resolve domain names.
Examples to check (record type A):
example.comrandom.example.comtest123.example.com
These should all return your server's public IP.
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.
On your server, download the install script using curl:
curl https://raw.githubusercontent.com/bluesky-social/pds/main/installer.sh > installer.shAnd then run the installer using bash. You will need sudo permissions to continue:
sudo bash installer.shThe 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.
Upon completion of a successful installation, you'll receive output similar to the following:
========================================================================
PDS installation successful!
------------------------------------------------------------------------
Check service status      : sudo systemctl status pds
Watch service logs        : sudo docker logs -f pds
Backup service data       : /pds
PDS Admin command         : pdsadmin
Required Firewall Ports
------------------------------------------------------------------------
Service                Direction  Port   Protocol  Source
-------                ---------  ----   --------  ----------------------
HTTP TLS verification  Inbound    80     TCP       Any
HTTP Control Panel     Inbound    443    TCP       Any
Required DNS entries
------------------------------------------------------------------------
Name                         Type       Value
-------                      ---------  ---------------
your-domain.net              A          your-ip-address
*.your-domain.net            A          your-ip-address
Detected public IP of this server: your-ip-address
To see pdsadmin commands, run "pdsadmin help"
========================================================================
And, following account creation:
Account created successfully!
-----------------------------
Handle   : handle.your-domain.net
DID      : did:plc:your-did
Password : your-password
-----------------------------
Save this password, it will not be displayed again.
Tip
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.
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:
Visit https://your-domain.net/xrpc/_health in your browser. You should see a JSON response with a version, like:
{"version":"0.2.2-beta.2"}
You'll also need to check that WebSockets are working, for the rest of the network to pick up content from your PDS. You can test by installing a tool like wsdump and running a command like:
wsdump "wss://example.com/xrpc/com.atproto.sync.subscribeRepos?cursor=0"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.
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:
sudo pdsadmin account createIf needed, use pdsadmin to create an invite code:
sudo pdsadmin create-invite-codeWhen creating an account using the app, enter this invite code.
You can use the Bluesky app to connect to your PDS.
- Get the Bluesky app
 - Enter the URL of your PDS (e.g. 
https://example.com/) 
Note: because the subdomain TLS certificate is created on-demand, it may take 10-30s for your handle to be accessible. If you aren't seeing your first post/profile, wait 30s and try to make another post.
To be able to verify users' email addresses and send other emails, you need to set up an SMTP server.
As an alternative to running your own SMTP server, you can use an email service. Resend and SendGrid are two popular choices.
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):
PDS_EMAIL_SMTP_URL=smtps://resend:<your api key here>@smtp.resend.com:465/
[email protected]
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:
PDS_EMAIL_SMTP_URL=smtps://username:[email protected]/
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:
PDS_EMAIL_SMTP_URL=smtp:///?sendmail=true
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.
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.
If the username and/or password contain special characters, the special characters will need to be percent encoded.  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.
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.
By default, logs from the PDS are printed to stdout and end up in Docker's log. You can browse them by running:
[sudo] docker logs pds
Note: these logs are not persisted, so they will be lost after server reboot.
Alternatively, you can configure the logs to be printed to a file by setting LOG_DESTINATION:
LOG_DESTINATION=/pds/pds.log
You can also change the minimum level of logs to be printed (default: info):
LOG_LEVEL=debug
It is recommended that you keep your PDS up to date with new versions. You can use the pdsadmin tool to update your PDS.
sudo pdsadmin updateThis project is dual-licensed under MIT and Apache 2.0 terms:
- MIT license (LICENSE-MIT.txt or http://opensource.org/licenses/MIT)
 - Apache License, Version 2.0, (LICENSE-APACHE.txt or http://www.apache.org/licenses/LICENSE-2.0)
 
Downstream projects and end users may choose either license individually, or both together, at their discretion. The motivation for this dual-licensing is the additional software patent assurance provided by Apache 2.0.