bluesky pds without docker

Here’s how I got a self-hosted PDS (personal data server) running without docker.

This can be useful if you want to run the PDS on an existing machine, among other situations. I came up with these steps by emulating what the installer script does.

My setup uses nginx and a wildcard tls cert for my pds domain.

Get the code

Clone the pds repo

git clone

Set up nginx

I use certbot to issue wildcard certs for my domains. See my wildcard cert script here. Note that you will need to set up credentials for your nameservers. I’m not aware of a way in nginx to issue certs on-demand like the example caddy config does.

Here’s my nginx config for the pds.

server {
listen 80;
server_name *;
return 302 https://$host$request_uri;

server {
listen 443 ssl http2;
server_name *;
ssl_certificate /etc/letsencrypt/live/;
ssl_certificate_key /etc/letsencrypt/live/;
include /etc/letsencrypt/options-ssl-nginx.conf;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

location / {
include proxy_params;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;

Configure your .env file

Adjust the top 4 options, filling in your domain and generating keys with the following commands:

Use this for the JWT_SECRET:

openssl rand --hex 16

Use this twice to generate the admin password and rotation key:

openssl ecparam --name secp256k1 --genkey --noout --outform DER | tail --bytes=+8 | head --bytes=32 | xxd --plain --cols 32
PDS_HOSTNAME="your domain here"
PDS_JWT_SECRET="generated secret"
PDS_ADMIN_PASSWORD="generated key"
PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX="another generated key"


Run the pds

Be sure to install the dependencies:

cd service
pnpm install --production --frozen-lockfile
mkdir -p data/blocks

This is the systemd user unit I use to run the pds. Add this to your user units with the following steps:

mkdir -p ~/.config/systemd/user
$EDITOR ~/.config/systemd/user/pds.service
# copy in the example below and adjust as needed
systemctl --user daemon-reload
systemctl --user enable --now pds
Description=atproto personal data server

ExecStart=/usr/bin/node --enable-source-maps index.js


View the logs from journalctl like this:

journalctl --user --output=cat --follow --unit pds | jq

You can run the pdsadmin commands by setting the PDS_ENV_FILE variable like this:

ben@odin ~/w/p/pdsadmin (main)> PDS_ENV_FILE=../service/.env bash list
Handle Email DID did:plc:g5isluhi3wkw557ucarjgtgy

For now you will need to request that your pds be added to the allowlist on the relay in the pds admin discord.


To update your pds, use git pull in the directory you cloned it in. Then update dependencies in the service subdirectory and restart the unit:

cd pds
git pull
cd service
pnpm install --production --frozen-lockfile
systemctl --user restart pds


3 responses to “bluesky pds without docker”

  1. Alors ça fonctionne bien ?

  2. pas encore déployé, je me familiarise d’abord avec la chose 😉

  3. holler if you need any help with the non-docker setup, i’ve also added some clarification on there

Leave a Reply