Add PDS bluesky docker setup write-up

This commit is contained in:
Quentin Rouland 2025-01-27 11:37:56 +01:00
parent 184e120ef0
commit 69f47eff78
3 changed files with 261 additions and 2 deletions

View File

@ -1,7 +1,7 @@
source: "."
destination: _site
site:
title: QRouland DevBlog
title: DevBlog
description: QRouland DevBlog
base_url: http://blog.qrouland.com
posts:

View File

@ -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 %}

View File

@ -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 [Blueskys 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: <SOME PATH IN HOST>/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=<YOUR DOMAIN>
# PDS_SERVICE_HANDLE_DOMAINS: A suffix for the domain to be used with the service (e.g., .domain.com)
PDS_SERVICE_HANDLE_DOMAINS=.<YOUR DOMAIN>
# PDS_JWT_SECRET: A secret key for signing JWT tokens, needed for secure authentication
PDS_JWT_SECRET=<SECRET HERE>
# PDS_ADMIN_PASSWORD: The admin password for accessing the PDS service (use a strong password generated by a password manager)
PDS_ADMIN_PASSWORD=<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=<KEY HEX HERE>
# 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://<USERNAME>:<PASSWORD>@<SMTP HOSTNAME>"
# PDS_EMAIL_FROM_ADDRESS: The email address used to send emails (e.g., noreply@domain.com)
PDS_EMAIL_FROM_ADDRESS=<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=<PDS HOSTNAME>
# 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=<PDS HOSTNAME>
# LETSENCRYPT_EMAIL: The email used for Let's Encrypt certificate registration (e.g., admin@domain.com)
LETSENCRYPT_EMAIL=<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://<USERNAME>:<PASSWORD>@<SMTP HOSTNAME>```. 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 ```<PDS HOSTNAME>``` over HTTPS (e.g., https://pds.domain.com) through the reverse proxy:
```ini
VIRTUAL_HOST=<PDS HOSTNAME>
VIRTUAL_PORT=3000
LETSENCRYPT_HOST=<PDS HOSTNAME>
LETSENCRYPT_EMAIL=<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://<PDS HOSTNAME>>/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://<PDS HOSTNAME>/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.<DOMAIN>)
```sh
$ PDS_ENV_FILE=/path/to/pds.env ./account.sh create <EMAIL ADDRESS> myhandle.<DOMAIN>
Account created successfully!
-----------------------------
Handle : myhandle.<DOMAIN>
DID : <DID>
Password : <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 `<PDS HOSTNAME>` (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.<DOMAIN>``` with ```did=<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 ```<DOMAIN>/.well-known/atproto-did``` (e.g. https://domain.com/.well-known/atproto-did) that contain your ```<DID>```.
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 ```<DID>``` value running the following :
```sh
$ mkdir <WEB DATA PATH IN HOST>/.well-known
$ echo <DID> > <WEB DATA PATH IN HOST>/.well-known/atproto-did
```
For reference, heres a sample Docker configuration for serving my homepage:
```yaml
version: '2'
services:
web:
image: nginx
container_name: nginx
restart: always
expose:
- 80
volumes:
- <WEB DATA PATH IN HOST>:/usr/share/nginx/html:ro
environment:
- VIRTUAL_HOST=<DOMAIN>
- LETSENCRYPT_HOST=<DOMAIN>
- LETSENCRYPT_EMAIL=<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 <YOUR DID> -p <YOUR PASSWORD> --pds-host <PDS HOSTNAME>
$ goat account update-handle <YOUR DOMAIN>
```
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)