2026-02-23 13:41:18 +00:00
2026-02-19 15:54:21 -05:00
2026-02-21 23:48:21 -05:00
2026-02-21 23:48:21 -05:00
2026-02-22 18:05:58 +00:00
2026-02-19 15:54:21 -05:00
2026-02-23 13:41:18 +00:00

bluesky-pds

  • Category: Apps
  • Status: 0
  • Image: ghcr.io/bluesky-social/pds
  • Healthcheck: Yes
  • Backups: No
  • Email: No
  • Tests: No
  • SSO: No

Quickstart

  1. setup a server with abra and deploy coop-cloud/traefik

  2. abra app new bluesky-pds (do not use --secrets yet, see below)

  3. Generate secrets:

    The JWT secret and admin password can be generated automatically:

    abra app secret generate YOURAPPDOMAIN pds_jwt_secret v1
    abra app secret generate YOURAPPDOMAIN pds_admin_password v1
    

    The PLC rotation key is a secp256k1 private key and must be generated manually:

    openssl ecparam --name secp256k1 --genkey --noout --outform DER | \
      tail --bytes=+8 | head --bytes=32 | xxd --plain --cols 32
    

    Then store it as a secret:

    abra app secret insert YOURAPPDOMAIN pds_plc_rotation_key v1 <THE_KEY_HEX>
    
  4. abra app deploy YOURAPPDOMAIN

Verify the PDS is running: curl https://YOURAPPDOMAIN/xrpc/_health

Account management

Create an account on your PDS (use the admin password you stored during secret generation):

abra app run YOURAPPDOMAIN app -- \
  goat pds admin account create \
    --admin-password YOUR_ADMIN_PASSWORD \
    --handle user.YOURAPPDOMAIN \
    --email user@example.com \
    --password yourpassword

Create an invite code:

abra app run YOURAPPDOMAIN app -- \
  goat pds admin create-invites \
    --admin-password YOUR_ADMIN_PASSWORD

Usage

Once you've created an account (see above), you can log in with any ATProto-compatible client:

  1. Open bsky.app (or another client like Graysky, Sky.app, etc.)
  2. On the login screen, tap Hosting provider (or Custom PDS depending on the client)
  3. Enter your PDS hostname: YOURAPPDOMAIN
  4. Log in with the handle and password you used when creating the account

Your handle will be user.YOURAPPDOMAIN by default (a subdomain handle). You can switch to a custom domain handle — see Handle configuration below.

Handle configuration

User handles on a PDS can work in two ways:

  1. Subdomain handles (e.g. user.pds.example.com): The default. Requires a wildcard DNS record (*.pds.example.com) pointing to your server. TLS is handled automatically by the Caddy sidecar (see below).

  2. Domain handles (e.g. user.com): Users can use their own domain as a handle by adding a DNS TXT record at _atproto.user.com with the value did=did:plc:<their-did>. This works without any additional server configuration.

DNS setup

At minimum, create an A record pointing your PDS domain to your server:

pds.example.com    A    <server-ip>

For subdomain handles, also add a wildcard record:

*.pds.example.com  A    <server-ip>

TLS architecture (Caddy sidecar)

This recipe uses a Caddy sidecar for TLS instead of letting Traefik terminate TLS directly. This is needed because Bluesky subdomain handles require TLS certificates for each user.pds.example.com subdomain, and Traefik cannot issue on-demand per-subdomain certificates.

The architecture:

  1. Traefik receives TLS connections on port 443 and does TCP passthrough (no TLS termination) for traffic matching DOMAIN and *.DOMAIN, forwarding the raw TLS stream to Caddy.
  2. Caddy terminates TLS using on-demand certificates — it automatically obtains a Let's Encrypt certificate for each subdomain the first time a connection arrives, using the TLS-ALPN-01 challenge.
  3. Caddy reverse proxies the decrypted HTTP traffic to the PDS on port 3000.

This matches how the upstream PDS is designed to work (it ships with Caddy), adapted for Co-op Cloud's Traefik-based routing. The PDS exposes a /tls-check endpoint that Caddy consults before issuing a certificate, preventing abuse.

Note: The first request to a new subdomain handle may take 10-30 seconds while Caddy obtains the TLS certificate from Let's Encrypt. Subsequent requests are instant.

No changes to the Traefik recipe are needed — the TCP passthrough is configured entirely via deploy labels on the Caddy service in this recipe's compose.yml.

About

A Bluesky PDS (Personal Data Server) is a self-hosted server for ATProto. This is a co-op cloud recipe for a PDS as implemented by bluesky, although other pds implementations exist such as cocoon, tranquil-pds, pegasus and rsky-pds.

recipe maintained by @notplants

Description
No description provided
Readme 51 KiB
Languages
Shell 100%