ATProto PDS #
Self-hosted Bluesky/ATProto PDS + pds-gatekeeper, as Terraform + Ansible.
- Terraform provisions a dedicated DigitalOcean droplet (
devict-pds). - Ansible installs Docker + renders the PDS compose stack.
- Secrets live in
/pds/pds.env(mode 0600). The three crypto secrets —PDS_JWT_SECRET,PDS_ADMIN_PASSWORD,PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX— are generated on first run and kept on disk; back them up (they're in 1Password). For a new deployment (or single-secret rotation), generate them withjust gen-secrets(seescripts/generate-pds-secrets.sh) and store the output in 1Password before first apply. - Gatekeeper is added as a sidecar service; enable features via role vars.
Prerequisites #
Create atprotopds/.env.local.op (1Password-backed, not committed) with:
TF_VAR_do_token=op://Private/env-devict-infra/DIGITALOCEAN_TOKEN
TF_VAR_ssh_public_key=op://Private/env-devict-infra/SSH_PUBLIC_KEY
PDS_HOSTNAME=op://Private/env-devict-infra/PDS_HOSTNAME
PDS_ADMIN_EMAIL=op://Private/env-devict-infra/PDS_ADMIN_EMAIL
PDS_EMAIL_SMTP_URL=op://Private/env-devict-infra/PDS_EMAIL_SMTP_URL
PDS_EMAIL_FROM_ADDRESS=op://Private/env-devict-infra/PDS_EMAIL_FROM_ADDRESS
PDS_HCAPTCHA_SITE_KEY=op://Private/env-devict-infra/PDS_HCAPTCHA_SITE_KEY
PDS_HCAPTCHA_SECRET_KEY=op://Private/env-devict-infra/PDS_HCAPTCHA_SECRET_KEY
TF_VAR_spaces_access_id=op://Private/env-devict-infra/SPACES_ACCESS_KEY_ID
TF_VAR_spaces_secret_key=op://Private/env-devict-infra/SPACES_SECRET_ACCESS_KEY
PDS_BLOBSTORE_S3_ACCESS_KEY_ID=op://Private/env-devict-infra/SPACES_ACCESS_KEY_ID
PDS_BLOBSTORE_S3_SECRET_ACCESS_KEY=op://Private/env-devict-infra/SPACES_SECRET_ACCESS_KEY
DIGITALOCEAN_TOKEN and SSH_PUBLIC_KEY are reused from devserver; create the
rest in 1Password. SMTP and hCaptcha keys are optional (only for 2FA email and
captcha-gated account creation).
Object storage (blobs): create a Spaces key in the DigitalOcean control panel
(API → Spaces Keys) with read/write access and store it as SPACES_ACCESS_KEY_ID
/ SPACES_SECRET_ACCESS_KEY in 1Password. Terraform provisions a private Spaces
bucket (devict-pds-blobs by default; see terraform/spaces.tf) and the same key
authenticates the PDS to read/write blobs. The PDS uses S3 storage automatically
when the access key is present, and falls back to local disk otherwise. To switch
an already-running PDS to S3, add the PDS_BLOBSTORE_S3_* block to /pds/pds.env
and restart (just ssh then sudo systemctl restart pds).
Usage #
just plan # terraform plan
just apply # terraform apply
just setup # ansible-playbook site.yaml
just deploy # re-render templates + restart (no full setup)
just ssh # ssh devict@<ip>
After install #
This PDS version (0.4.5009, shipping @atproto/pds 0.5.9) includes the built-in
account management UI under https://<pds_hostname>/ — create/invite/migrate
accounts from the browser without the goat CLI.
CLI alternative (ssh in):
sudo docker exec pds goat pds admin account create
See PLAN.md for the full design.