ryan.doesthings.online

Bluesky PDS Manual Install

Introduction

I have a functional Docker system with routing provided by Traefik v3. Because of this, I didn’t want to spin up a dedicated Ubuntu VM just for Bluesky. These are the bare minimum steps I needed to get the PDS container running in my environment.

Requirements

  • OpenSSL - Used to generate secrets
  • SMTP Provider - I like Postmark; They give you 100 E-mails/month free.

Files

generate_envs.sh

The names and values in generate_envs.sh were sourced from Bluesky’s PDS installer script and writes the following variables to pds.env.

My script is best reasonable effort, lol.

# Generated
PDS_HOSTNAME=
PDS_JWT_SECRET=
PDS_ADMIN_PASSWORD=
PDS_EMAIL_SMTP_URL=
PDS_EMAIL_FROM_ADDRESS=
PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX=

# Defaults
PDS_DATA_DIRECTORY=/var/lib/pds
PDS_BLOBSTORE_DISK_LOCATION=/var/lib/pds/blocks
PDS_BLOB_UPLOAD_LIMIT=52428800
PDS_DID_PLC_URL=https://plc.directory
PDS_BSKY_APP_VIEW_URL=https://api.bsky.app
PDS_BSKY_APP_VIEW_DID=did:web:api.bsky.app
PDS_REPORT_SERVICE_URL=https://mod.bsky.app
PDS_REPORT_SERVICE_DID=did:plc:ar7c4by46qjdydhdevvrndac
PDS_CRAWLERS=https://bsky.network
LOG_ENABLED=true

docker-compose.yml

You will need to update this compose file to meet the needs of your own setup.

services:
  pds:
    image: ghcr.io/bluesky-social/pds:latest
    container_name: pds
    restart: unless-stopped
    env_file:
      - pds.env
    volumes:
      - pds_data:/var/lib/pds
      - pds_blocks:/var/lib/pds/blocks
    networks:
      - traefik_traffic
    labels:
      - "traefik.enable=true"

      # This mess is required *only* if you are doing other stuff with the domain
      - "traefik.http.routers.pds.rule=(Host(`example.com`) || HostRegexp(`^.+\\.example\\.com$`)) && (PathPrefix(`/xrpc`) || PathPrefix(`/.well-known/atproto-did`))"

      - "traefik.http.routers.pds.entrypoints=websecure"
      - "traefik.http.routers.pds.tls=true"
      - "traefik.http.routers.pds.tls.certresolver=letsencrypt"
      - "traefik.http.routers.pds.service=pds-service"

      - "traefik.http.services.pds-service.loadbalancer.server.port=3000"
      - "traefik.http.services.pds-service.loadbalancer.healthcheck.path=/xrpc/_health"

volumes:
  pds_data:
  pds_blocks:

networks:
  traefik_traffic:
    external: true

Run this mess

Both your edited docker-compose.yml and the generated pds.env files should be placed in an appropriately named folder, I suggest “bluesky”.

$ cd ~/bluesky
$ docker compose up -d

Create an account

The easiest way to create your account is with an invite. You will need a copy of the pds.env file and export the PDS_ENV_FILE variable containing the path to it. If you can do this in your compose folder, that’ll work fine.

$ export PDS_ENV_FILE="~/bluesky/pds.env"
$ cd ~/bluesky
$ curl https://raw.githubusercontent.com/bluesky-social/pds/refs/heads/main/pdsadmin/create-invite-code.sh -o create-invite-code.sh
$ sh create-invite-code.sh

Notes

  • If you get a Server Error 500 when validating your E-mail, make sure you have the correct SMTP details for your provider Bluesky PDS SMTP documentation.
  • The default setup assumes users will by default be given a subdomain. Create a wildcard DNS entry to accommodate this.
  • Your users will automatically be verified by the atproto .well-known provided by the PDS. If a user wants to change their domain, they can do so via the TXT record method.
  • The Bluesky Debug Page was super helpful.
  • Thanks to @ameo.dev for his Note that got me going!