So I want to run my own PDS but Docker is the devil and it must not be permitted to spread. I was very excited to see this post which is almost all I, and you, need to know!...unless you, like me, were immediately rendered wary by this nugget:
I’m pretty idiosyncratic when it comes to this: I have my own TypeScript DSL for generating nginx configs.
Oof.
So here are the parts a tired, old, graybeard dinosaur like myself had to self-discover in addition to the above linked post. You do need to go read the above linked post, though. Everything here is in reference to that post at the points where I hit speed bumps. If you have literally no idea what you're doing, probably don't look at that post or this post, do what the official thing says with the sinful Docker behavior and be in a supported configuration. I am not an example. No great deed is recorded here.
It's also worth saying: I do not do nodejs shenanigans with my day. I have done them when forced, but I do not have a fingertip vocabulary for it, so if I'm explaining something that feels obvious to you, a galaxy brain nodejs wonk: congrats, gold star, do not @ me about it, I'm very impressive in my own ways.
Literally Step 0
When possible I run apps like this as their own user and out of their home folder unless there's a compelling reason not to. In this case, it's up to you where you want the files to live, but you do need to have some directories, and the OP did not mention that. What I did before I installed node was:
# useradd -m pds
# su - pds
$ mkdir -p pds data/blocks
$ cd pds
Does that make /home/pds/pds
, yeah, it does. I don't really like it either, but also I don't care that much and it's already done. If it bothers you, put things whever you want. I leave it as an exercise to the reader to figure out all the changes you'll need to make in the other places.
sigh Installing node I guess
I took the OP's advice and used Volta cause...I mean I kinda hate node and I didn't want to think about how I'd install it otherwise or whether I'd install the right version or whatever. A user-local self-contained install of node suited me just fine. I mention it here only because you should be aware that it means the node
binary is in a user-local path and we'll be accounting for that later in the systemd unit. If you're using system-provided nodejs or something, you'll have to deal with that in whatever way suits you.
The index.mjs
file
Hooray, a new file extension, that means....something! Who cares what it means, I guess, it goes here: /home/pds/pds/index.mjs
.
The .env
file
It probably goes without saying to you node jocks or whatever, but this is literally a file called .env
which in this case is placed at /home/pds/pds/.env
. Config files, who needs 'em, not these cloud sickos. If you're following along with my folders, you'll need to change these two lines to match the paths we're using:
PDS_DATA_DIRECTORY=/home/pds/data
PDS_BLOBSTORE_DISK_LOCATION=/home/pds/data/blocks
The nginx config
You need to add this to your base nginx.conf
in the http
section:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
And this is the config file I put in sites.d
(or sites-enabled
or conf.d
or symlinked from somwhere or however you like or organize these things, live your best life). It includes things to make letsencrypt http-01 validation work, you can take them out if you like. All http requests will be redirected to https and https will proxy all those requests to the app. Log paths and root folders and whatever, do what you want, this is where mine are.
server {
listen <YOUR IPV4 ADDRESS HERE>:80;
listen [<YOUR IPV6 ADDRESS HERE>]:80;
access_log /var/log/nginx/pds.access.log;
error_log /var/log/nginx/pds.error.log;
server_name <YOUR PDS DOMAIN HERE>;
root "/usr/share/nginx/letsencrypt";
index index.html;
#rewrite ^ https://$server_name$request_uri permanent;
if ($request_method !~ ^(GET|HEAD|POST)$) {
return 444;
}
location /.well-known/acme-challenge {
alias /usr/share/nginx/letsencrypt/.well-known/acme-challenge;
try_files $uri =404;
}
location / {
return 301 https://<YOUR PDS DOMAIN HERE>$request_uri;
}
}
server {
listen <YOUR IPV4 ADDRESS HERE>:443 ssl;
listen [<YOUR IPV6 ADDRESS HERE>]:443 ssl;
http2 on;
access_log /var/log/nginx/pds.access.log;
error_log /var/log/nginx/pds.error.log;
server_name <YOUR PDS DOMAIN HERE>;
server_name *.<YOUR PDS DOMAIN HERE>;
ssl_certificate PATH/TO/YOUR/cert.pem;
ssl_certificate_key PATH/TO/YOUR//key.pem;
ssl_prefer_server_ciphers on;
ssl_dhparam PATH/TO/YOUR/dhparam-4096.pem;
ssl_ecdh_curve secp384r1;
ssl_session_timeout 10m;
ssl_session_cache shared:SSL:10m;
#ssl_session_tickets off;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header X-XSS-Protection "1; mode=block";
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; ";
client_max_body_size 200M;
location = /robots.txt { access_log off; log_not_found off; }
location = /favicon.ico { access_log off; log_not_found off; }
location ~ /\.ht {
deny all;
}
location / {
proxy_pass http://127.0.0.1:2583;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
}
}
You may or may not want all those ssl parameters and tricksy headers, but if you don't know what they do then maybe I know better than you, probably just leave them alone. If you don't have a pre-existing dhparam file, first off how dare you, second:
# openssl dhparam -out dhparam-4096.pem 4096
It may take a little bit, your encryption quality will thank you later. To the reader that wants to jaw at me about how many bits I'm using, it's fine, we're all fine here, make it however you want.
SSL certificates
I don't use certbot
, I leaned into acme.sh a long time ago and I don't regret it even a little. If you don't use either, probably just do what the OP says, but either way, I'd encourage you to go ahead and get a cert that matches both your base pds domain and also the wildcard subdomain for your pds domain. It may or may not be required depending on how you plan to use your PDS, but one of the ways handles are validated is through an HTTP request to a well-known URI, and handles on your PDS will, by default, be subdomains of the PDS. Care, don't care, up to you. The nginx config above will direct wildcard subdomains to the pds virtual host but if your cert doesn't have the wildcard also, then browsers and whatnot will get real angry.
In acme.sh
it will look like this and do note that wildcard certs require a dns validation method, I will not be explaining what that means to you here:
# acme.sh --issue -w /usr/share/nginx/letsencrypt/ -d <YOUR PDS DOMAIN HERE> -d *.<YOUR PDS DOMAIN HERE> --keylength ec-384 --dns <YOUR FAVORITE DNS METHOD>
# acme.sh --install-cert -d <YOUR PDS DOMAIN HERE> -d *.<YOUR PDS DOMAN HERE> --key-file PATH/TO/YOUR/key.pem --fullchain-file PATH/TO/YOUR//cert.pem
The systemd unit file
I mean, you weren't gonna run it out of a screen session like some kind of monster, were you? WERE YOU?!
[Unit]
Description=ATProtocol PDS
After=network.target
[Service]
Environment=VOLTA_HOME=/home/pds/.volta
Environment=PATH=/home/pds/.volta/bin:$PATH
Environment=NODE_ENV=production
WorkingDirectory=/home/pds/pds
Type=simple
User=pds
Group=pds
ExecStart=/home/pds/.volta/bin/node index.mjs
Restart=on-failure
[Install]
WantedBy=multi-user.target
Why do my handles say "INVALID HANDLE" in bsky.app
Man, who knows, not me. But you will probably find this site helpful, I did: https://bsky-debug.app/handle
...Profit
I think that's everything. Who knows, I guess. Probably more than you had!