A self contained Bluesky PDS
Find a file
2024-12-03 18:39:10 -05:00
.dockerignore init 2024-12-03 17:47:58 -05:00
.gitignore bump 2024-12-03 17:50:09 -05:00
docker-compose.yaml change image name 2024-12-03 17:49:43 -05:00
Dockerfile add bsdextrautils, combined some run stages 2024-12-03 17:49:19 -05:00
entrypoint.sh dynamically create the pds.env file 2024-12-03 17:48:54 -05:00
LICENSE add license 2024-12-02 20:35:09 -05:00
Makefile remove no-cache flag 2024-12-03 18:39:10 -05:00
README.md bump 2024-12-03 17:48:12 -05:00

bluesky-pds-docker

It is required to run the instance behind a proxy (like traefik) to generate SSL certificates. This will not work otherwise. The standard pds install includes caddy to handle this.

I can confirm it works behind Cloudflare's DNS proxy with Full (strict)

Deployment

# Generate secret environment variables
echo PDS_ADMIN_PASSWORD: $(openssl rand --hex 16)
echo PDS_JWT_SECRET: $(openssl rand --hex 16)
echo PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: $(openssl ecparam --name secp256k1 --genkey --noout --outform DER | tail --bytes=+8 | head --bytes=32 | xxd --plain --cols 32)

# pull and run the container
docker pull forgejo.gravityfargo.dev/gravityfargo/bluesky-pds
docker-compose up -d

# Create an account
docker exec -it bluesky-pds bash
pdsadmin account create
pdsadmin account list
# pdsadmin request-crawl bsky.network

In your browser:

Example Docker Compose

Full list of additional Environment Variables can be found in the packages/pds/src/config/env.ts

services:
  bluesky-pds:
    container_name: bluesky-pds
    image: forgejo.gravityfargo.dev/gravityfargo/bluesky-pds
    environment:
      PDS_JWT_SECRET: ...
      PDS_ADMIN_PASSWORD: ...
      PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: ...
      PDS_HOSTNAME: ...
    volumes:
      - ./config:/pds

Example Docker Compose with Traefik

services:
  bluesky-pds:
    container_name: bluesky-pds
    image: forgejo.gravityfargo.dev/gravityfargo/bluesky-pds:latest
    networks:
      - proxy
    env_file:
      PDS_ADMIN_PASSWORD: ""
      PDS_JWT_SECRET: ""
      PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: ""
      PDS_HOSTNAME: example.com
      PDS_EMAIL_SMTP_URL: smtps://smtp-relay.gmail.com:465/
      PDS_EMAIL_FROM_ADDRESS: gravityfargo@gmail.com

    volumes:
      - /bluesky-pds:/pds
    labels:
      traefik.enable: "true"
      traefik.http.**routers**.bluesky-pds-insecure.entrypoints: http
      traefik.http.routers.bluesky-pds-insecure.rule: HostRegexp(`^.+\.example\.com$`) || Host(`example.social`)
      traefik.http.routers.bluesky-pds-secure.entrypoints: https
      traefik.http.routers.bluesky-pds-secure.rule: HostRegexp(`^.+\.example\.com$`) || Host(`example.social`)
      traefik.http.routers.bluesky-pds-secure.tls: "true"
      traefik.http.services.bluesky-pds.loadbalancer.server.scheme: http
      traefik.http.services.bluesky-pds.loadbalancer.server.port: 3000
      traefik.http.routers.bluesky-pds-secure.middlewares: BlueskyHeaders@file
      traefik.http.routers.bluesky-pds-insecure.middlewares: BlueskyHeaders@file

Middleware

I think file configs are cleaner than having a billion labels.

http:
  middlewares:
    BlueskyHeaders:
      headers:
        accessControlAllowMethods:
          - GET
          - OPTIONS
          - PUT
          - POST
          - DELETE
        accessControlAllowHeaders: "*"
        accessControlAllowOriginList: "*"
        addVaryHeader: true
        stsSeconds: 63072000

Development Notes

pamac install jq