A writeup about our edits to the included Caddy config.
Quick tips
You're probably here to check what PDS paths to reverse proxy, so here they are:
/xrpc/*
/.well-known/atproto-did
/.well-known/oauth-protected-resource
/.well-known/oauth-authorization-server
/oauth/*
/@atproto/*
/gate
Subdomains only need /.well-known/atproto-did
.
If you're here because you thought the PDS needed to be on your base domain to assign user handles from it, the environment variable PDS_SERVICE_HANDLE_DOMAINS
(in /pds/pds.env
) is your friend!
Some guides you might find useful
*(Please note you'll still need the new OAuth paths if you want to use OAuth.)
If you need to configure a PDS to work around existing services you're running, check out these:
- PDS alongside other services
- PDS with an existing website
- PDS without containerization
- PDS without docker
- Bluesky PDS on Enterprise Linux 9
- Nginx redirect for active handles
If you want to confirm that OAuth works on your PDS, here are some great ATmosphere sites you can log into to check:
- Smoke Signal, where you can create events
- Frontpage, where you can share links
- PinkSea, where you can draw
- picosky, where you can chat
- Linkat, where you can make a link card
- WhiteWind, where you can blog
Where we put website files
Our files are in /srv
. Another common location is /var/www
. You can use other locations, but you may have to configure permissions if you do so.
Our folder structure is like this:
srv
- default
- subdomains
-- <username>
Allowing Caddy to access the files
The default PDS installation uses Docker
. This means PDS-related things are in a container and can't mess with anything outside of it unless you let it know it's okay to do that.
You can do that by adding to /pds/compose.yml
. Under services:
caddy:
volumes:
, there are a bunch of paths and what path they'll be called in the container. We added /srv
there:
- type: bind
source: /srv
target: /srv
Caddy also restarts when you sudo systemctl restart pds
.
Our Caddyfile
The Caddyfile for the PDS is /pds/caddy/etc/caddy/Caddyfile
.
About the PDS paths:
- Most requests go to
/xrpc/
. - Handles are verified using
/.well-known/atproto-did
. - Logging in with OAuth uses
/oauth/
plus information in/.well-known/oauth-protected-resource
and/.well-known/oauth-authorization-server
. (See AT Protocol OAuth spec.) - I have no idea the specifics but OAuth also uses
/@atproto/
in some browsers. - hCaptcha uses
/gate/
.
About our blob paths (this is irrelevant to normal PDS use):
- If you look up a blob by DID+CID (user + hash), you'll always get the same blob, so they can be cached for outside use like linking atfile files.
- We are rewriting a convenient path (
/blob/<did>/<cid>
) and usingdefer
to replace the PDS's Cache-Control header so browsers will cache the files when using this path.
About our subdomains:
- We have separate websites for each handle on our PDS, and if someone doesn't have a website, it redirects to our base URL. Check out the many error handling options in the Caddy documentation.
- Nonexistent handles do error, since we don't have a wildcard certificate.
Our Caddyfile:
{
email our@email.here
on_demand_tls {
ask http://localhost:3000/tls-check
}
}
chicory.blue {
root * /srv/default
@pds {
path /xrpc/*
path /.well-known/atproto-did
path /.well-known/oauth-protected-resource
path /.well-known/oauth-authorization-server
path /oauth/*
path /@atproto/*
path /gate/*
}
@blobs {
path_regexp ^/blob/(.*)/(.*)$
}
header @blobs Cache-Control "public, max-age=15552000" {
defer
}
rewrite @blobs /xrpc/com.atproto.sync.getBlob?did={re.1}&cid={re.2}
reverse_proxy @pds http://localhost:3000
file_server
}
*.chicory.blue {
root * /srv/subdomains/{labels.2}
tls {
on_demand
}
@pds {
path /.well-known/atproto-did
}
handle_errors {
redir https://chicory.blue
}
reverse_proxy @pds http://localhost:3000
file_server
}
Credits
Thanks to fry69 (🦋) for helping me understand Docker!
Thanks to ducky.ws (🦋) and benharri.org (🦋) for explaining how to create the blob path and sharing the regex!
Thanks to mov.eax on Discord for suggesting edits to the confusing section about blob paths!