govandLetta Code 9ee7e97324 feat: template pds.env with Ansible-managed config
Reads installer-generated secrets from pds.env and re-templates it with
config from vars/main.yml (S3, SMTP, blob limits, etc.). Secrets are
preserved across runs; only config changes trigger a restart.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta Code <noreply@letta.com>
2026-07-31 18:01:10 -04:00
2026-07-31 17:00:42 -04:00
2026-07-31 17:00:42 -04:00
2026-07-31 17:00:42 -04:00
2026-07-30 23:20:48 +00:00
2026-07-31 17:00:42 -04:00

atproto-pds-ansible

Ansible playbook for deploying a self-hosted Bluesky PDS with Caddy (TLS) and Watchtower (auto-updates).

What it deploys

  • PDS — Bluesky Personal Data Server (ghcr.io/bluesky-social/pds:0.4)
  • Caddy — Reverse proxy with automatic TLS via Let's Encrypt
  • Watchtower — Pulls new PDS image versions hourly and recreates containers

Requirements

  • A server running Ubuntu 20.04/22.04/24.04 or Debian 11/12/13
  • SSH access as the sysadmin user (with sudo privileges)
  • A domain pointed at the server (A record for the hostname + wildcard *. subdomain for handles)
  • Ports 80 and 443 open to the internet
  • Ansible installed locally

Setup

cp inventory.yml.example inventory.yml       # set your server IP
cp vars/main.yml.example vars/main.yml       # set your hostname and admin email

Edit inventory.yml:

all:
  hosts:
    pds:
      ansible_host: 203.0.113.10

Edit vars/main.yml:

pds_hostname: bsky.example.com
pds_admin_email: you@example.com

Deploy

ansible-playbook playbook.yml

What it does

First run: the official PDS installer bootstraps Docker, generates secrets, and creates the initial database. Ansible then templates compose.yaml and Caddyfile over the installer's defaults and runs docker compose up -d.

Subsequent runs: the installer is skipped (guarded by account.sqlite). Ansible only applies changes if the templates have changed. docker compose up -d is a no-op when nothing changed.

Variables

Variable Required Default Description
pds_hostname yes — Public DNS name for the PDS
pds_admin_email yes — Admin contact email (used by Caddy for Let's Encrypt)
pds_data_dir no /pds Data directory (must be /pds — the installer enforces this)
pds_image_tag no 0.4 PDS Docker image tag

Auto-updates

Watchtower checks every hour (WATCHTOWER_POLL_INTERVAL=3600) for new 0.4.x images on ghcr.io/bluesky-social/pds:0.4. When a new image is found, it pulls it and recreates the container. Old images are cleaned up automatically.

Post-deploy

Check that the PDS is healthy:

curl https://your-hostname/xrpc/_health

Create an account:

pdsadmin account create

Notes

  • pds.env (containing JWT secret, admin password, PLC rotation key) is generated by the installer and not managed by Ansible. Edit it directly on the server to add SMTP or change settings.
  • The installer creates a pds.service systemd unit. It's left in place — it just runs docker compose up -d on boot. Docker's restart: unless-stopped policy also handles this independently.
S
Description
Config for atproto PDS services using ansible
Readme MIT
126 KiB
1 Stars 8 Watchers 0 Forks
Languages
Jinja 100%