This guide uses nginx, I could not find a way to get Bluesky PDS to proxy correctly under relayd as you need to proxy subdomains. As much as I love httpd+relayd I also cannot proxy-cache under httpd and my requests are much slower than nginx.
I will not go over how to setup a VPS, order a domain-name, setup said domain's nameservers and configure nginx for general-use. This guide assumes you already know/have this setup as a baseline setup. I am personally running this all from an OpenBSD.Amsterdam (2G RAM) VM (thank you, Mischa Peters) that every Thursday gets backed-up from my NAS VM at home running OpenBSD under OpenBSD VMM, my domain-providers nameservers are set to Cloudflare and in my Cloudflare portal I have set the domain to be this server's static-IP but proxied through Cloudflare and it's SSL is managed through Cloudflare but use acme-client as a backup.
May not be needed? I will say though for your Nameserver DNS config you need your usual Nameserver DNS for the bluesky domain and your other domains plus two TXT records. They should be:
1. Type: TXT - Name: _atproto.bsky - Content: "did=did:plc:<plc-id>"
2. Type: TXT - Name: _atproto.<bsky-username>.bsky - Content: ""did=did:plc:<plc-id>""
To build Bluesky PDS on OpenBSD it was rather complex.
First I created a chroot of the latest OpenBSD from basexx.tgz and compxx.tgz downloaded from a mirror of https://cdn.openbsd.org/pub/OpenBSD/`(uname -r)`/`(uname -m)` (compxx.tgz is needed to build Bluesky PDS Node.js dependencies from npx).
To create the chroot from the package-sets I did:
$ mkdir chroot
$ doas tar -C chroot -xzphf basexx.tgz
$ doas tar -C chroot -xzphf compxx.tgz
$ cd chroot/dev
$ doas ./MAKEDEV all
$ cd ..
$ doas cp /etc/{master.passwd,passwd,group} etc/
$ doas mkdir -p home/bluesky
$ doas chown bluesky:bluesky home/bluesky
$ doas cp /etc/{installurl,resolv.conf} etc/
$ doas chroot .
(chroot) # pwd_mkdb /etc/master.passwd
(chroot) # ldconfig /usr/local/lib
(chroot) # sysmerge
Then to install Bluesky PDS I ran these commands directly after running the previous commands.
(chroot) # pkg_add node
(chroot) # exit
$ doas git clone https://github.com/bluesky-social/pds.git
$ doas chown -R bluesky:daemon pds
$ doas chroot .
(chroot) # cd /pds/service
Before installing from npx pnpm I had to modify asdasd to work with OpenBSD.
Added to the bottom of the dependencies code-block at the top of the file pds/service/pnpm-lock.yaml.
'@img/sharp-wasm32':
specifier: ^0.33.5
version: 0.33.5
libvips:
specifier: ^0.0.2
version: 0.0.2
As well as adding to the bottom of the dependencies code-block in the small file pds/service/package.json.
"@img/sharp-wasm32": "^0.33.5",
"libvips": "^0.0.2"
If you do not do these two things then npx npm will error-out when compiling sharp and libvips on OpenBSD.
As we are having to force-install older versions of sharp and libvips it is paramount that you run your Bluesky PDS is run in an isolated chroot as an unprivileged user and it is backed-up outside of the chroot regularly.
Now we can install via npx pnpm.
(chroot) # npx pnpm install --production --no-frozen-lockfile
Next we need to setup the executable so it reads as bluesky under the processes.
Please don't forget that when you update Node.js in the future you will need to re-create this symbolic link.
(chroot) # ln -s /usr/local/bin/node /usr/local/bin/bluesky
Next we create the script located in the base of the chroot called bluesky that runs the Node.js executable named bluesky.
#!/bin/ksh
export PATH=$PATH:/usr/local/bin
# Run script from rcctl via chroot.
# Dependencies: node.
(bluesky --enable-source-maps --env-file=/pds/pds.env /pds/service/index.js) &
To start the service I created a service in /etc/rc.d/bskypds, it reads:
#!/bin/ksh
chroot_dir="/var/www/bsky.sissyisabella.com/chroot"
chroot_user="bluesky"
chroot_group="daemon"
chroot_cmd="chroot -u $chroot_user -g $chroot_group $chroot_dir"
bluesky_binary="/bluesky"
daemon="$chroot_cmd $bluesky_binary"
#daemon_user="bluesky"
daemon_logfile="/var/www/bsky.sissyisabella.com/log"
daemon_flags=""
. /etc/rc.d/rc.subr
pexp="${bluesky_binary} ${daemon_flags}"
rc_bg=YES
#rc_reload=NO
#pexp
#rc_start() {
# rc_exec "${daemon} ${daemon_flags} >> ${daemon_logfile} 2>&1"
#}
rc_cmd $1
For logfile rotation I have added two lines to my /etc/newsyslog.conf then I restarted the logger service by running doas rcctl restart syslogd.
/var/www/bsky.sissyisabella.com/chroot/pds/pds.log bluesky:daemon 644 7 250 * Z
/var/www/bsky.sissyisabella.com/log.txt root:isabella 644 7 250 * Z
You should hopefully then have a Bluesky PDS, you just need to configure your pds.env to your liking following the official Bluesky PDS documentation from GitHub and configure nginx to proxy-pass the Bluesky PDS correctly.
Sample of my /etc/nginx/sites/bsky.sissyisabella.com nginx config (how to):
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
listen [::]:80;
server_name bsky.sissyisabella.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name bsky.sissyisabella.com
# Your usual nginx vhost config such as SSL, logging, site-config, etc.
....
....
....
# Bluesky config
# Increase upload size to 2048 MB.
client_max_body_size 2048M;
# (I have proxy-caching in each location block on my own config but have removed it here so it's easier to read the code).
# (It won't affect the PDS proxying but is good to have, you can read below on how to set it up).
location /xrpc {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_pass http://127.0.0.1:10415;
}
location /.well-known/atproto-did {
proxy_pass http://127.0.0.1:10415/.well-known/atproto-did;
proxy_set_header Host $host;
}
location /blob/ {
# this adds a pretty url for blobs
# in the format of https://pds/blob/did/cid
rewrite ^/blob/(.*)/(.*)$ /xrpc/com.atproto.sync.getBlob?did=$1&cid=$2 last;
}
}
How to setup proxy-caching in nginx.
What is proxy-caching in nginx?
When proxy-caching is enabled, nginx saves responses in a disk cache and uses them to respond to clients without having to proxy requests for the same content every time.
In your /etc/nginx.conf:
# Your usual nginx.conf config.
...
...
http {
# Temp cache directories.
client_body_temp_path cache/client_body_temp;
proxy_temp_path cache/proxy_temp;
fastcgi_temp_path cache/fastcgi_temp;
uwsgi_temp_path cache/uwsgi_temp;
scgi_temp_path cache/scgi_temp;
# Caching.
proxy_cache_path nginx-cache levels=1:2 keys_zone=my_cache:12m max_size=3g inactive=90m use_temp_path=off;
# Allow for more vhosts.
server_names_hash_bucket_size 128;
include sites/*;
}
In your vhosts sites, for me that is setup as /etc/nginx/sites/<site-name>:
server {
listen 80;
listen [::]:80;
server_name sissyisabella.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name sissyisabella.com;
# Your usual nginx vhost config such as SSL, logging, site-config, proxy-caching, etc.
....
....
....
# Proxy caching.
proxy_cache my_cache;
# You will need to add this code for each location block that you want to enable proxy-caching on.
location / {
proxy_set_header Host $http_host;
proxy_cache_key $scheme://$host$uri$is_args$query_string;
proxy_cache_valid 200 10m;
proxy_cache_bypass $arg_should_bypass_cache;
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504 http_429;
proxy_cache_lock on;
}
}