From 69f47eff785e1b3f8ecd2a99013a1981b6a5d5f6 Mon Sep 17 00:00:00 2001 From: Quentin Rouland Date: Mon, 27 Jan 2025 11:37:56 +0100 Subject: [PATCH] Add PDS bluesky docker setup write-up --- _cobalt.yml | 2 +- posts.md | 2 +- posts/2025-01-27-writeup-1-bluesky-pds.md | 259 ++++++++++++++++++++++ 3 files changed, 261 insertions(+), 2 deletions(-) create mode 100644 posts/2025-01-27-writeup-1-bluesky-pds.md diff --git a/_cobalt.yml b/_cobalt.yml index 09c15e5..fa4f563 100644 --- a/_cobalt.yml +++ b/_cobalt.yml @@ -1,7 +1,7 @@ source: "." destination: _site site: - title: QRouland DevBlog + title: DevBlog description: QRouland DevBlog base_url: http://blog.qrouland.com posts: diff --git a/posts.md b/posts.md index 3969f81..c094cf0 100644 --- a/posts.md +++ b/posts.md @@ -5,5 +5,5 @@ permalink: /posts --- {% for post in collections.posts.pages %} -### [{{ post.published_date | date: "%Y-%m-%d" }} - {{ post.title }}](/{{ post.permalink }}) +### [{% if post.published_date %}{{ post.published_date | date: "%Y-%m-%d" }} - {% endif %} {{ post.title }}](/{{ post.permalink }}) {% endfor %} diff --git a/posts/2025-01-27-writeup-1-bluesky-pds.md b/posts/2025-01-27-writeup-1-bluesky-pds.md new file mode 100644 index 0000000..a8d6fc8 --- /dev/null +++ b/posts/2025-01-27-writeup-1-bluesky-pds.md @@ -0,0 +1,259 @@ +--- +permalink: /posts/writeup/1 +title: Hosting Bluesky PDS on my Server Using Docker +categories: +- Write Up +tags: +- write up +- bluesky +- docker +published_date: 2025-01-27 10:37:07.201403915 +0000 +layout: default.liquid +is_draft: false +--- +## Self-hosting Bluesky PDS Using Docker + +I'm not usually a big user of social networks, but with my new blog, I wanted a way to share updates on social media. +I went with Bluesky for its decentralized nature, which aligns with my interest in the growing trend of decentralized social platforms. + +In this write-up, I will document the steps I took to install [Bluesky’s PDS (Personal Data Server)](https://github.com/bluesky-social/pds) on my setup, an Ubuntu server running Docker. + +Thanks to this [write-up](https://mattdyson.org/blog/2024/11/self-hosting-bluesky-pds), which was a huge help to do mine. +While most of it was very useful, some parts differed since my Docker setup is different. +Also, I encountered difficulties when trying to change my handle to the root domain, so I used a different method to achieve this. +As a result, I hope this guide will provide additional value and be helpful to others. + + + +### Installing the PDS Using Docker + +The starting point is the official bluesky [compose.yaml](https://github.com/bluesky-social/pds/blob/main/compose.yaml). However, my environment already include other services, including an [NGINX proxy with ACME Companion](https://github.com/nginx-proxy/acme-companion) to automatically generate and renew TLS certificates for my http services (see [Basic usage](https://github.com/nginx-proxy/acme-companion/wiki/Basic-usage)). + +Therefore, I modify this ```compose.yaml``` to reuse my proxy setup to provide a reverse proxy for the PDS container, eliminating the need for the reverse proxy (Caddy) provided in the initial file. I also remove Watchtower, as I use my own scripts to handle image updates. + + +Finally, my ```compose.yaml``` is pretty simple file looks like this: + +```yaml +version: '3.9' +services: + pds: + container_name: pds + image: ghcr.io/bluesky-social/pds:0.4 + network_mode: host + restart: unless-stopped + volumes: + - type: bind + source: /pds # Change this for + target: /pds + env_file: + - pds.env +``` + +Most of the setup is to adapt with environment variables in the environment file ```pds.env```: + +```ini +# PDS_HOSTNAME: The domain name of your PDS service (e.g., domain.com) +PDS_HOSTNAME= +# PDS_SERVICE_HANDLE_DOMAINS: A suffix for the domain to be used with the service (e.g., .domain.com) +PDS_SERVICE_HANDLE_DOMAINS=. +# PDS_JWT_SECRET: A secret key for signing JWT tokens, needed for secure authentication +PDS_JWT_SECRET= +# PDS_ADMIN_PASSWORD: The admin password for accessing the PDS service (use a strong password generated by a password manager) +PDS_ADMIN_PASSWORD= +# PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX: A private key for cryptographic operations in hex format +PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX= +# PDS_DATA_DIRECTORY: Directory where data will be stored (e.g., /pds) +PDS_DATA_DIRECTORY=/pds +# PDS_BLOBSTORE_DISK_LOCATION: Location for storing blob data (e.g., /pds/blocks) +PDS_BLOBSTORE_DISK_LOCATION=/pds/blocks +# PDS_BLOB_UPLOAD_LIMIT: The maximum upload size for blobs in bytes (e.g., 50 MB = 52428800 bytes) +PDS_BLOB_UPLOAD_LIMIT=52428800 +# PDS_EMAIL_SMTP_URL: The URL for the SMTP server used to send emails (e.g., for email verification) +PDS_EMAIL_SMTP_URL="smtp://:@" +# PDS_EMAIL_FROM_ADDRESS: The email address used to send emails (e.g., noreply@domain.com) +PDS_EMAIL_FROM_ADDRESS= +# LOG_ENABLED: Set to 'true' to enable logging +LOG_ENABLED=true +# PDS_DID_PLC_URL: URL for the DID (Decentralized Identifier) PLC service +PDS_DID_PLC_URL=https://plc.directory +# PDS_BSKY_APP_VIEW_URL: URL for the BlueSky app view service +PDS_BSKY_APP_VIEW_URL=https://api.bsky.app +# PDS_BSKY_APP_VIEW_DID: DID for the BlueSky app view service +PDS_BSKY_APP_VIEW_DID=did:web:api.bsky.app +# PDS_REPORT_SERVICE_URL: URL for the report service +PDS_REPORT_SERVICE_URL=https://mod.bsky.app +# PDS_REPORT_SERVICE_DID: DID for the report service +PDS_REPORT_SERVICE_DID=did:plc:ar7c4by46qjdydhdevvrndac +# PDS_CRAWLERS: URL for the crawlers to scrape data from (e.g., bsky.network) +PDS_CRAWLERS=https://bsky.network +# VIRTUAL_HOST: The virtual host for your PDS service (e.g., pds.domain.com) +VIRTUAL_HOST= +# VIRTUAL_PORT: The port number for the virtual host (e.g., 3000) +VIRTUAL_PORT=3000 +# LETSENCRYPT_HOST: The host for generating a Let's Encrypt SSL certificate (e.g., pds.domain.com) +LETSENCRYPT_HOST= +# LETSENCRYPT_EMAIL: The email used for Let's Encrypt certificate registration (e.g., admin@domain.com) +LETSENCRYPT_EMAIL= + +``` +Most variable comments are self-explanatory, but here are some additional hints for a few of them. + +To generate the ```PDS_JWT_SECRET``` , you can use the following command: +```sh +$ openssl rand --hex 16 +``` + +For ```PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX```, you can use +```sh +$ openssl ecparam --name secp256k1 --genkey --noout --outform DER | tail --bytes=+8 | head --bytes=32 | xxd --plain --cols 32 +``` + +Since I run my own mail server on my domain, I use my SMTP server by specifying the information in the format ```smtp://:@```. If you don't have your own mail server, as mentioned in the official documentation, you can use services like [resend](https://resend.com/) as an alternative. + + +As I mention before, as I have already have the [NGINX proxy with ACME Companion set up](https://github.com/nginx-proxy/acme-companion), I only need to add the following lines to make the PDS container's port 3000 accessible on `````` over HTTPS (e.g., https://pds.domain.com) through the reverse proxy: + +```ini +VIRTUAL_HOST= +VIRTUAL_PORT=3000 +LETSENCRYPT_HOST= +LETSENCRYPT_EMAIL= +``` + +The container is now ready to run. To start everything, use the following command to verify is everything looks find: + +```sh +$ docker-compose up +``` + +#### Ensuring Everything the PDS Running as Expected + +As directed in the documentation [https://atproto.com/guides/self-hosting](https://atproto.com/guides/self-hosting), to check that everything is online and working, you can visit `https://>/xrpc/_health` in your browser. You should see a JSON response with the version, like: + +```json +{"version":"0.2.2-beta.2"} +``` + +Additionally, you can use an online WebSocket tester (e.g. [piehost.com](https://piehost.com/websocket-tester)) +and entered the following URL: `wss:///xrpc/com.atproto.sync.subscribeRepos?cursor=0` +If everything is configured correctly, the test should indicate that the connection has been successfully established. + + + +### Get your Root Domain (e.g domain.com) as your Bluesky Handle + +You cannot directly create a handle for the root domain (e.g., domain.com). Therefore, we need to create an account with a different handle (e.g., handle.domain.com) and then verify the root domain handle ownership using [Decentralised Identifier (DID)](https://atproto.com/specs/did) . We can verify using DNS or HTTP server verification. It seems you can use either, but I did both to be sure and can't confirm if only one is enough. + + +#### Create account + +Since the administrative tools are included in the Docker image, they do not need to be installed separately, as follows: + +```sh +$ git clone https://github.com/bluesky-social/pds/ bluesky-pds +$ cd bluesky-pds/pds-admin +``` + +Then, create an account using a temporary handle (e.g. myhandle.) + +```sh +$ PDS_ENV_FILE=/path/to/pds.env ./account.sh create myhandle. +Account created successfully! +----------------------------- +Handle : myhandle. +DID : +Password : +----------------------------- +``` + +Make sure to record the handle, DID, and password for future use. + + +I should be able, at this step, to connect from the Bluesky client (e.g., [https://bsky.app/](https://bsky.app/)) using your custom domain and verify your email address. + +Go to [https://bsky.app/](https://bsky.app/) > **Sign In** >In **Hosting Provider**, select **Custom** > enter your `` (e.g., pds.domain.com) and then use your credentials (email/password) to log in. + +Then, you should be connected and able to verify your email address in the account settings by receiving a code, if your SMTP setup is correct. + + +Now, to validate our root domain, we first need to set up a Domain Ownership Verification method to confirm that we own the domain for the handle we want (e.g., domain.com). + +#### Domain Ownership Verification using DNS + +To do the verification of the ownership of your domain using DNS add a DNS TXT record ```_atproto.``` with ```did=```. + +#### Domain Ownership Verification using HTTP + +To do the verification of the ownership of your domain using HTTP you need make accecible a file on http server at ```/.well-known/atproto-did``` (e.g. https://domain.com/.well-known/atproto-did) that contain your ``````. + +As I already have an Nginx server running to serve your homepage, I simply added a file at the path ```/.well-known/atproto-did``` in the root directory of my site, containing my `````` value running the following : + +```sh +$ mkdir /.well-known +$ echo > /.well-known/atproto-did +``` + +For reference, here’s a sample Docker configuration for serving my homepage: + +```yaml +version: '2' +services: + + web: + image: nginx + container_name: nginx + restart: always + expose: + - 80 + volumes: + - :/usr/share/nginx/html:ro + environment: + - VIRTUAL_HOST= + - LETSENCRYPT_HOST= + - LETSENCRYPT_EMAIL= +``` + + +#### Validation + + +To verify if the DNS or HTTP Domain Ownership Verification is set up correctly, +you can check on [https://bsky-debug.app/handle](https://bsky-debug.app/handle) by entering your handle. + +#### Update Handle + +We can now update your handle to your root domain ! + +However, I encountered an issue when trying to verify my handle directly in the Bluesky app ([https://bsky.app/](https://bsky.app/)). After logging into my account, I was unable to update my handle to the root domain due to errors. In **Settings > Account > Change Handle > I have my own domain**, selecting **Verify DNS Record** or **Verify Text File** would return an error. + +However, I was able to use the [Go AT protocol CLI tool (goat)](https://github.com/bluesky-social/indigo/tree/main/cmd/goat) to successfully update my handle as follow. + +Installing ```goat``` (require the Go toolchain): +```sh +$ go install github.com/bluesky-social/indigo/cmd/goat@latest +``` + +Then, update your handle using the following commands: + +```sh +$ goat account login -u -p --pds-host +$ goat account update-handle +``` +Once connected to your Bluesky account, your handle should now display as your root domain. + +### Finally + +Once the handle update is complete, you can restart the server in detached mode with the command below. + +```sh +$ docker-compose up -d +``` + +### References + +* [Bluesky PDS](https://github.com/bluesky-social/pds/) +* [Atproto Self Hosting Documentation](https://atproto.com/guides/self-hosting) +* [Matt Dysion Write Up](https://mattdyson.org/blog/2024/11/self-hosting-bluesky-pds) +* [https://bsky-debug.app/handle](https://bsky-debug.app/handle) +* [Go AT protocol CLI tool (goat)](https://github.com/bluesky-social/indigo/tree/main/cmd/goat) \ No newline at end of file