How to deploy & self-host BlueSky Social PDS on DigitalOcean

How to deploy & self-host BlueSky Social PDS on DigitalOcean

This is a detailed, up-to-date guide to deploying and self-hosting a BlueSky Social PDS (“Personal Data Server”) on DigitalOcean. I assume you already have a DigitalOcean account. If not, you’ll need one to spin up droplets, configure networking, and manage DNS.

This guide walks you from environment setup through DNS, installation, user creation, federation, and maintenance. I will point out possible pitfalls and alternatives (for example, if you want to co-host with other services or use a custom reverse proxy).

BlueSky runs on the AT Protocol (Authenticated Transfer, “ATProto”), and a PDS hosts your account data, signing keys, and serves as your node within the federated network.

Self-hosting gives you control over your data, portability, and independence from the default host infrastructure. The system is fairly lightweight (a PDS does not act like a full social media server) and is intended to be accessible to independent operators.

That said, the software is still evolving; compatibility, migration support, and federation policies can shift. As of now, federation is in early access and rate limits apply to new PDS hosts.

Before diving into steps, here is a rough map of what you will be deploying:

  • A Droplet (VM) on DigitalOcean (Ubuntu recommended)
  • Docker & Docker Compose environment
  • The BlueSky PDS container(s) (official distribution)
  • A web server / reverse proxy (Caddy by default, or your own) to handle TLS, routing, and domain verification
  • DNS records (A + wildcard) to resolve your domain and subdomains to your PDS
  • Optionally, SMTP settings (for email verification)
  • The pdsadmin tool for administrative tasks
  • Federation / crawl request so your PDS is discoverable to others

With that in mind, let’s move step by step.

Step 1: Provision the Droplet

  1. Log into your DigitalOcean dashboard.
  2. Create a new project (optional but helpful to group resources).
  3. Launch a Droplet. In the Marketplace, search for BlueSky Social PDS. DigitalOcean provides a one-click image for BlueSky PDS.
  4. Choose a size. The minimal recommended specs are modest: 1 CPU, 1 GB RAM, ~20 GB SSD is sufficient for a small number of accounts (1–20) as per official documentation.
  5. Choose region, SSH key (or password), set hostname, and create the Droplet.
  6. Once provisioned, note its public IPv4 address.

Because the DigitalOcean marketplace includes BlueSky PDS, part of the installer is triggered automatically (you’ll see a console prompt when first accessing the Droplet).

Step 2: DNS configuration

Your PDS needs a domain or subdomain you control. Let’s call it pds.dropletdrift.com (you’ll replace dropletdrift.com with your domain).

In your DNS provider:

  • Add an A record:
    Name: pds
    Value: your Droplet’s IPv4 address
    TTL: ~600 seconds (10 min) is reasonable
  • Add a wildcard A record:
    Name: * .pds (i.e. *.pds.dropletdrift.com)
    Value: same IPv4 address
    TTL: same

The wildcard is required so that new user subdomains (or user handles that map to subdomains) resolve properly.

Wait a few minutes (3–5 min, sometimes up to 10) for DNS propagation before continuing with the installation prompts.

You can verify DNS using dig, nslookup, or online tools like DNS Checker to confirm both pds.dropletdrift.com and, say, foo.pds.dropletdrift.com resolve to your IP.

Step 3: Run the PDS installer

SSH into your Droplet:

ssh root@pds.dropletdrift.com

If not yet fetched, you can get the installer script:

wget https://raw.githubusercontent.com/bluesky-social/pds/main/installer.sh

or

curl -fsSL https://raw.githubusercontent.com/bluesky-social/pds/main/installer.sh -o installer.sh

Then run:

sudo bash installer.sh

The installer will:

  • Detect your OS (Ubuntu or Debian)
  • Install Docker, Docker Compose, and dependencies
  • Ask for your public DNS (you’ll enter pds.dropletdrift.com)
  • Ask for an admin email (for notifications / support contact)
  • Set up container stack (PDS + Caddy reverse proxy + watchtower for updates)
  • Create a systemd service pds to manage the stack
  • Start containers

Wait until the prompt confirms “PDS installation successful.”

After that, you can check:

sudo systemctl status pds

or view logs:

sudo docker logs -f pds

If PDS is healthy, you should see logs indicating HTTP(s) endpoints listening, domain validation etc.

Step 4: Verify domain / DID verification

To use your domain for your handle, the PDS must be verified via DNS (or HTTP) for DID (decoupled identifier). The installer or the PDS will normally instruct you to place a record or endpoint.

You may need to add a TXT record such as:

_atproto   TXT   <value given by installer or pdsadmin>

Also, the path /.well-known/atproto-did must resolve via HTTP to a verification file or endpoint. Ensure your reverse proxy or Caddy is routing that path appropriately.

If both DNS and HTTP verification succeed, your domain becomes your handle domain.

Step 5: Create a user / invite code

Once the PDS is live, you need to create a user (or invite code) to log into BlueSky using your PDS.

You can use pdsadmin (the tool provided in the install) to create accounts or invite codes.

To create an invite code:

pdsadmin create-invite-code

It will print a code. Use that code in the BlueSky UI under “Custom hosting provider → Invite code.”

Alternatively, you can create an account via:

pdsadmin create-user <email> <username>

Adjust args as per documentation.

Note: The PDS must be part of the federation network (or crawlable) before others can discover your instance.

Step 6: Log in via BlueSky, set handle, test

  • Go to bsky.app (the BlueSky web client).
  • In the login / sign up interface, choose “Custom” or “Hosting provider”
  • Enter your PDS domain pds.dropletdrift.com
  • Use the username / invite code / password you set

Once logged in, set your profile, bio, etc.

If errors occur, check container logs in real time (docker logs -f pds) and inspect network requests to see HTTP error codes. SSL, path routing, WebSocket header proxying, and Host headers are common failure points.

If your handle shows “Invalid Handle”, you may need to trigger a re-crawl by executing:

pdsadmin request-crawl <your PDS domain>

This prompts the network relay to re-index your host.

Optional & advanced: co-hosting, custom reverse proxy, skipping Caddy

If your server already runs other services (websites, reverse proxies), the default installer’s Caddy instance may conflict (port 80/443). Some users disable or remove the built-in Caddy and set up a custom docker-compose.yml that exposes PDS on an internal port (e.g. 6010), then proxy via Nginx / Traefik etc.

Example of a trimmed docker-compose.yml (without Caddy / watchtower) from someone’s custom setup:

version: '3.9'
services:
  pds:
    container_name: pds
    image: ghcr.io/bluesky-social/pds:0.4
    restart: unless-stopped
    volumes:
      - type: bind
        source: /opt/pds
        target: /pds
    ports:
      - '6010:3000'
    env_file:
      - /opt/pds/pds.env

Then your external proxy (nginx, traefik) must route:

  • /xrpc/ to PDS
  • /.well-known/atproto-did to PDS
  • WebSocket proxying properly (header passthrough)

Using this approach, you can preserve existing ports and services. But it increases complexity.

Step 7: Federation, rate limits & maintenance

Your PDS must be crawlable by relay services so it becomes discoverable across the network. Use pdsadmin request-crawl, or follow the PDS Admin Discord / federation docs.

New PDS hosts are initially subject to rate limits (e.g. 10 accounts, certain event throughput) until reputation/trust is established.

To keep your PDS up to date, periodically run:

pdsadmin update

You may schedule a cron job (e.g. nightly at 3:00 am) to auto-update.

Back up your /pds directory (persistent data mounted in the host) regularly. Losing that means losing account data, media, keys.

Also watch for upstream changes: the PDS software is evolving, and backward compatibility is not guaranteed if you skip updates.

Troubleshooting tips & common pitfalls

  • DNS misconfiguration: forgot wildcard, or propagation delay
  • TLS / port conflicts: default Caddy listens on 80/443, which may clash
  • Missing HTTP proxying: wrong routing for /.well-known/atproto-did or /xrpc/ breaks DID verification or API calls
  • WebSocket header issues: your reverse proxy must preserve Host header and websocket upgrade
  • “Invalid Handle”: often a verification problem; trigger crawl or double-check DID setup
  • Container health failures: check logs, missing environment variables, missing persistent volume

You now have a self-hosted BlueSky PDS running on a DigitalOcean Droplet. You can log in via BlueSky apps using your custom domain, manage users, and participate in the AT Protocol network.

Next, you might:

  • Add custom reverse proxy / integrate with existing infrastructure
  • Monitor uptime & metrics
  • Scale storage or performance as necessary
  • Extend features (e.g. multiple users, email routing, custom domains per user)
  • Engage with the PDS admin community & stay current with updates

Was this helpful?

Thanks for your feedback!

Leave a comment

Your email address will not be published. Required fields are marked *