First Deployment #
This runbook creates the first production PDS deployment. It assumes the repository has already been cloned locally and that production secrets are stored outside Git.
1. Local Preflight #
From this repository:
scripts/preflight.sh --local
Optional remote check:
ssh -i <ssh-key-path> <vm-user>@<vm-public-ip> 'bash -s' < scripts/preflight.sh
The remote preflight should warn about missing Docker before installation. Warnings are expected before provisioning.
2. Cloudflare DNS #
Create or confirm:
pds.3drx.top A <vm-public-ip> proxied
Keep _atproto.3drx.top unset until the account DID is known.
Ensure Cloudflare WebSockets are enabled for the zone.
3. Cloudflare Origin Certificate #
Create an Origin CA certificate for pds.3drx.top.
Store the certificate and private key outside Git. Suggested VM paths:
/pds/certs/cloudflare-origin.pem
/pds/certs/cloudflare-origin.key
Permissions:
sudo chown root:root /pds/certs/cloudflare-origin.*
sudo chmod 0644 /pds/certs/cloudflare-origin.pem
sudo chmod 0600 /pds/certs/cloudflare-origin.key
Set Cloudflare SSL/TLS mode to Full (strict) after the origin certificate is installed.
4. VM Preparation #
SSH to the VM:
ssh -i <ssh-key-path> <vm-user>@<vm-public-ip>
Create /pds on the root disk:
sudo mkdir -p /pds
sudo chown root:root /pds
sudo chmod 0755 /pds
Add swap because the VM has about 1 GB RAM:
sudo fallocate -l 2G /swapfile
sudo chmod 0600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
Install Docker and the Docker Compose plugin using the official Docker Debian instructions. Do not commit package manager logs or generated configuration.
5. R2 Buckets and Credentials #
Create:
- live blob bucket, for example
<pds-blob-bucket> - backup bucket or prefix, for example
<pds-backup-bucket>
Create scoped credentials:
- live blob credentials: read/write to the live blob bucket
- backup credentials: read/write to the backup bucket or prefix
Store real credentials only in ignored files on the VM or a password manager.
Validate live blob credentials from a safe shell after exporting the real values from an ignored env file or password manager:
scripts/verify-r2-blobstore.sh
6. Install PDS #
Use the official PDS installer as the base deployment path.
Deployment choices:
PDS_HOSTNAME=pds.3drx.top- invite required: enabled
- public signup: disabled
- initial account: one account only
After installation, update /pds/pds.env using config/pds.env.example as the reference. Add the R2 live blobstore settings and keep real secrets out of Git.
For this deployment, place the real shared R2 credential only in /etc/pds-r2.env and apply it with:
sudo /usr/local/sbin/apply-r2-config.sh
7. Configure Origin TLS in Caddy #
Use config/caddy-origin-ca.Caddyfile.example as a reference. Configure Caddy to serve pds.3drx.top with the Cloudflare Origin CA certificate and key.
Restart PDS/Caddy according to the installed service layout.
8. Configure Handle #
Get the created account DID from installer output, pdsadmin, or another atproto tool.
Add:
_atproto.3drx.top TXT did=did:plc:<your-did>
Then change the account handle to 3drx.top using the Bluesky app or an atproto CLI.
9. Install Backup Timer #
Copy templates and scripts to production paths:
sudo install -m 0755 scripts/backup-pds.sh /usr/local/sbin/backup-pds.sh
sudo install -m 0644 config/systemd/pds-backup.service.example /etc/systemd/system/pds-backup.service
sudo install -m 0644 config/systemd/pds-backup.timer.example /etc/systemd/system/pds-backup.timer
Create an ignored env file on the VM, for example /etc/pds-backup.env, based on config/pds-backup.env.example.
Enable the timer:
sudo systemctl daemon-reload
sudo systemctl enable --now pds-backup.timer
Run one manual backup:
sudo systemctl start pds-backup.service
sudo journalctl -u pds-backup.service -n 100 --no-pager
10. Acceptance Checks #
scripts/healthcheck.sh
Verify:
https://pds.3drx.top/xrpc/_healthreturns JSON- WebSocket connection opens
- image uploads land in R2
- account handle is
@3drx.top - manual backup archive exists in R2