A self contained Bluesky PDS
Find a file
2024-12-10 22:56:45 +00:00
assets removed cloudflare proxy note 2024-12-10 22:56:45 +00:00
base Seperate base image to speed up build times 2024-12-10 20:05:29 +00:00
.dockerignore Seperate base image to speed up build times 2024-12-10 20:05:29 +00:00
.gitignore bump 2024-12-03 17:50:09 -05:00
docker-compose.yaml building with makefile instead 2024-12-10 20:05:55 +00:00
Dockerfile Added support for non-root operation 2024-12-10 20:08:12 +00:00
entrypoint.sh Added support for non-root operation 2024-12-10 20:08:12 +00:00
example.env Added support for non-root operation 2024-12-10 20:08:12 +00:00
LICENSE add license 2024-12-02 20:35:09 -05:00
Makefile Added support for non-root operation 2024-12-10 20:08:12 +00:00
README.md removed cloudflare proxy note 2024-12-10 22:56:45 +00:00

bluesky-pds-docker

A self-contained Docker image for the Bluesky PDS (Personal Data Server) for use with Traefik. This image is pinned to v0.4.74.

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. A wildcard DNS assignment along with a wildcard SSL certificate is required. I use Cloudflare for this, see the screenshot below.

Cloudflare's DNS Proxy Service will not work for this setup because of the email verification requirement. You must use the DNS only service.

This is not intended for production, and I am not responsible for any data loss or security issues. This is a personal project, and I am not affiliated with Bluesky.

Deployment

Before changing images or upgrading, backup the files made in the volume.

Reqirements

I haven't verified these are the minimum requirements, but they are what I found to be necessary after starting this readme. I probably had some dependencies installed already.

Manjaro/Arch

sudo pacman -S jq

Debian/Ubunutu

sudo apt install make xxd

Setup

Generate secrets and add them to .env file. See example.env as an example.

# 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)

Docker Compose Setup

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

By default, the image uses 1000:1000 as the UID:GID for the user. This can be changed by setting the PUID and PGID environment variables.

The compose element hostname must be the same value as PDS_HOSTNAME.

Traefik

# Traefik Proxy
services:
  bluesky-pds:
    container_name: bluesky-pds
    hostname: example.com
    image: gravityfargo/bluesky-pds:0.4.74
    networks:
      - proxy
    environment:
      # Define variables here or in a .env file
      PDS_JWT_SECRET: ...
      PDS_ADMIN_PASSWORD: ...
      PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: ...
      PDS_HOSTNAME: example.com
      PDS_EMAIL_SMTP_URL: smtps://resend:<your api key here>@smtp.resend.com:465/
      PDS_EMAIL_FROM_ADDRESS: admin@your.domain
      PUID: 1005
      PGID: 1005
    volumes:
      - ./bluesky-pds:/pds
    labels:
      traefik.enable: "true"
      traefik.http.routers.bluesky-pds-insecure.entrypoints: http
      traefik.http.routers.bluesky-pds-insecure.rule: HostRegexp(`^.+\.${URL_NAME}\.${URL_SUFFIX}$`) || Host(`${PDS_HOSTNAME}`)
      # traefik.http.routers.bluesky-pds-insecure.middlewares: BlueskyHeaders@file
      traefik.http.routers.bluesky-pds-secure.entrypoints: https
      traefik.http.routers.bluesky-pds-secure.rule: HostRegexp(`^.+\.${URL_NAME}\.${URL_SUFFIX}$`) || Host(`${PDS_HOSTNAME}`)
      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

Standalone

I do not run this, but it should be possible.

# Standalone, you'll need to add a proxy in front of this with SSL.
services:
  bluesky-pds:
    container_name: bluesky-pds
    hostname: example.com
    image: gravityfargo/bluesky-pds:0.4.74
    environment:
      # Define variables here or in a .env file
      PDS_JWT_SECRET: ...
      PDS_ADMIN_PASSWORD: ...
      PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: ...
      PDS_HOSTNAME: example.com
      PDS_EMAIL_SMTP_URL: ""
      PDS_EMAIL_FROM_ADDRESS: ""
      PUID: 1005
      PGID: 1005
    volumes:
      - ./bluesky-pds:/pds

Middleware

I think file configs are cleaner than having a billion labels. This is not required, but it's nice to have.

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

Optional Proxy Network

docker network create --subnet=192.168.1.0/24 --ipv6 --attachable proxy
# /etc/docker/daemon.json
# {
#   "ipv6": true,
#   "fixed-cidr-v6": "2001:db8:1::/64"
# }

Cloudflare DNS

alt text

Protonmail SMTP

PDS_EMAIL_SMTP_URL: smtp://user@example.com:TOKEN@smtp.protonmail.ch:587/ PDS_EMAIL_FROM_ADDRESS: user@example.com

Development

The development enviornment must be behind a proxy to generate SSL certificates. This will not work otherwise. In my case, I use Traefik installed on a VPS that's dedicated to development.

I suggest buying a domain name for testing. I have one I use that's for a seperate project, but getting a throwaway domain is also an option. Millage may vary.

make generate-env

Then set the variables

  • PDS_HOSTNAME=example.com
  • URL_NAME=example
  • URL_SUFFIX=com

Build the base image. This will take a while.

make build-base

Make edits as needed. Then build the dev image for testing.

make build-tag TAG=dev

Run the dev image.

make run
# or
docker-compose up