···67This particular PDS thrives under harsh conditions. It is a dandelion growing through the cracks in the sidewalk concrete.
89-It has full compatibility with Bluesky's reference PDS: same endpoints, same behavior, same client compatibility. Everything works: repo operations, blob storage, firehose, OAuth, handle resolution, account migration, the lot.
10-11-Another excellent PDS is [Cocoon](https://tangled.org/hailey.at/cocoon), written in go.
1213## What's different about Tranquil PDS
1415-It is a superset of the reference PDS, including: passkeys and 2FA (WebAuthn/FIDO2, TOTP, backup codes, trusted devices), SSO login and signup, did:web support (PDS-hosted subdomains or bring-your-own), multi-channel communication (email, discord, telegram, signal) for verification and alerts, granular OAuth scopes with a consent UI showing human-readable descriptions, app passwords with granular permissions (read-only, post-only, or custom scopes), account delegation (letting others manage an account with configurable permission levels), automatic backups (configurable retention and frequency, one-click restore), and a built-in web UI for account management, OAuth consent, repo browsing, and admin.
1617-The PDS itself is a single small binary with no node/npm runtime. It requires postgres. Blobs are stored on the local filesystem by default (S3 optional). Valkey is optional (supported as an alternative to the built-in cache).
1819## Quick Start
2021```bash
22cp .env.example .env
23-podman compose up -d
24just run
25```
26···4142### Quick Deploy (Docker/Podman Compose)
4344-Edit `.env.prod` with your values. Generate secrets with `openssl rand -base64 48`.
4546```bash
47-cp .env.prod.example .env.prod
48podman-compose -f docker-compose.prod.yaml up -d
49```
5051### Installation Guides
5253-| Guide | Best For |
54-|-------|----------|
55-| [Debian](docs/install-debian.md) | Debian 13+ with systemd |
56-| [Containers](docs/install-containers.md) | Podman with quadlets or OpenRC |
57-| [Kubernetes](docs/install-kubernetes.md) | You know what you're doing |
5859## Maintainers to ping
60
···67This particular PDS thrives under harsh conditions. It is a dandelion growing through the cracks in the sidewalk concrete.
89+It has full compatibility with Bluesky's reference PDS.
001011## What's different about Tranquil PDS
1213+It is a superset of the reference PDS, including: passkeys and 2FA (WebAuthn/FIDO2, TOTP, backup codes, trusted devices), SSO login and signup, did:web support (PDS-hosted subdomains or bring-your-own), multi-channel communication (email, discord, telegram, signal) for verification and alerts, granular OAuth scopes with a consent UI showing human-readable descriptions, app passwords with granular permissions (read-only, post-only, or custom scopes), account delegation (letting others manage an account with configurable permission levels), and a built-in web UI for account management, repo browsing, and admin.
1415+The PDS itself is a single binary with no nodeJS runtime. However, at time of writing, Tranquil requires postgres running separately. Blobs are stored on the local filesystem by default (S3 optional). Valkey is also optional (as an alternative to the built-in cache).
1617## Quick Start
1819```bash
20cp .env.example .env
21+podman compose up db -d
22just run
23```
24···3940### Quick Deploy (Docker/Podman Compose)
4142+Edit `.env` with your values. Generate secrets with `openssl rand -base64 48`.
4344```bash
45+cp .env.example .env
46podman-compose -f docker-compose.prod.yaml up -d
47```
4849### Installation Guides
5051+- [Debian](docs/install-debian.md)
52+- [Containers](docs/install-containers.md)
53+- [Kubernetes](docs/install-kubernetes.md)
005455## Maintainers to ping
56
+42-48
docs/install-containers.md
···1-# Tranquil PDS Containerized Production Deployment
23This guide covers deploying Tranquil PDS using containers with podman.
4···78## Prerequisites
910-- A VPS with at least 2GB RAM
11- Disk space for blobs (depends on usage; plan for ~1GB per active user as a baseline)
12- A domain name pointing to your server's IP
13- A **wildcard TLS certificate** for `*.pds.example.com` (user handles are served as subdomains)
14-- Root or sudo access
1516-## Quick Start (Docker/Podman Compose)
1718If you just want to get running quickly:
19···38ln -sf live/pds.example.com/privkey.pem certs/privkey.pem
39podman-compose -f docker-compose.prod.yaml restart nginx
40```
00004142For production setups with proper service management, continue to either the Debian or Alpine section below.
4344-## Standalone Containers (No Compose)
4546If you already have postgres running on the host (eg. from the [Debian install guide](install-debian.md)), you can run just the app containers.
47···9192---
9394-# Debian 13+ with Systemd Quadlets
9596-Quadlets are the modern way to run podman containers under systemd.
9798-## Install Podman
99100```bash
101apt update
102apt install -y podman
103```
104105-## Create Directory Structure
106107```bash
108mkdir -p /etc/containers/systemd
109mkdir -p /srv/tranquil-pds/{postgres,blobs,backups,certs,acme,config}
110```
111112-## Create Environment File
113114```bash
115cp /opt/tranquil-pds/.env.example /srv/tranquil-pds/config/tranquil-pds.env
···121openssl rand -base64 48
122```
123124-For quadlets, also add `DATABASE_URL` with the full connection string (systemd doesn't support variable expansion).
125-126-## Install Quadlet Definitions
127128Copy the quadlet files from the repository:
129```bash
···136137Optional quadlets for valkey and minio are also available in `deploy/quadlets/` if you need them.
138139-Note: Systemd doesn't support shell-style variable expansion in `Environment=` lines. The quadlet files expect DATABASE_URL to be set in the environment file.
140-141-## Create nginx Configuration
142143```bash
144cp /opt/tranquil-pds/nginx.frontend.conf /srv/tranquil-pds/config/nginx.conf
145```
146147-## Clone and Build Images
148149```bash
150cd /opt
···154podman build -t tranquil-pds-frontend:latest ./frontend
155```
156157-## Create Podman Secrets
158159```bash
160source /srv/tranquil-pds/config/tranquil-pds.env
161echo "$DB_PASSWORD" | podman secret create tranquil-pds-db-password -
162```
163164-## Start Services and Initialize
165166```bash
167systemctl daemon-reload
···169sleep 10
170```
171172-Run migrations:
173-```bash
174-cargo install sqlx-cli --no-default-features --features postgres
175-DATABASE_URL="postgres://tranquil_pds:your-db-password@localhost:5432/pds" sqlx migrate run --source /opt/tranquil-pds/migrations
176-```
177-178-## Obtain Wildcard SSL Certificate
179180User handles are served as subdomains (eg. `alice.pds.example.com`), so you need a wildcard certificate. Wildcard certs require DNS-01 validation.
181···209systemctl restart tranquil-pds-nginx
210```
211212-## Enable All Services
213214```bash
215systemctl enable tranquil-pds-db tranquil-pds-app tranquil-pds-frontend tranquil-pds-nginx
216```
217218-## Configure Firewall
219220```bash
221apt install -y ufw
···225ufw enable
226```
227228-## Certificate Renewal
229230Add to root's crontab (`crontab -e`):
231```
···234235---
236237-# Alpine 3.23+ with OpenRC
238239-Alpine uses OpenRC, not systemd. We'll use podman-compose with an OpenRC service wrapper.
240241-## Install Podman
242243```sh
244apk update
···253rc-service podman start
254```
255256-## Create Directory Structure
257258```sh
259mkdir -p /srv/tranquil-pds/{data,config}
260mkdir -p /srv/tranquil-pds/data/{postgres,blobs,backups,certs,acme}
261```
262263-## Clone Repository and Build Images
264265```sh
266cd /opt
···270podman build -t tranquil-pds-frontend:latest ./frontend
271```
272273-## Create Environment File
274275```sh
276cp /opt/tranquil-pds/.env.example /srv/tranquil-pds/config/tranquil-pds.env
···282openssl rand -base64 48
283```
284285-## Set Up Compose and nginx
286287Copy the production compose and nginx configs:
288```sh
···297Edit `/srv/tranquil-pds/config/nginx.conf` to update cert paths:
298- Change `/etc/nginx/certs/live/${PDS_HOSTNAME}/` to `/etc/nginx/certs/`
299300-## Create OpenRC Service
301302```sh
303cat > /etc/init.d/tranquil-pds << 'EOF'
304#!/sbin/openrc-run
305name="tranquil-pds"
306-description="Tranquil PDS AT Protocol PDS (containerized)"
307command="/usr/bin/podman-compose"
308command_args="-f /srv/tranquil-pds/docker-compose.yml up"
309command_background=true
···331chmod +x /etc/init.d/tranquil-pds
332```
333334-## Initialize Services
335336Start services:
337```sh
···349DATABASE_URL="postgres://tranquil_pds:$DB_PASSWORD@$DB_IP:5432/pds" sqlx migrate run --source /opt/tranquil-pds/migrations
350```
351352-## Obtain Wildcard SSL Certificate
353354User handles are served as subdomains (eg. `alice.pds.example.com`), so you need a wildcard certificate. Wildcard certs require DNS-01 validation.
355···381rc-service tranquil-pds restart
382```
383384-## Enable Service at Boot
385386```sh
387rc-update add tranquil-pds
388```
389390-## Configure Firewall
391392```sh
393apk add iptables ip6tables
···409/etc/init.d/ip6tables save
410```
411412-## Certificate Renewal
413414Add to root's crontab (`crontab -e`):
415```
···418419---
420421-# Verification and Maintenance
422423-## Verify Installation
424425```sh
426curl -s https://pds.example.com/xrpc/_health | jq
427curl -s https://pds.example.com/.well-known/atproto-did
428```
429430-## View Logs
431432**Debian:**
433```bash
···462rc-service tranquil-pds restart
463```
464465-## Backup Database
466467**Debian:**
468```bash
···474podman exec tranquil-pds-db-1 pg_dump -U tranquil_pds pds > /var/backups/pds-$(date +%Y%m%d).sql
475```
476477-## Custom Homepage
478479The frontend container serves `homepage.html` as the landing page. To customize it, either:
480
···1+# Tranquil PDS containerized production deployment
23This guide covers deploying Tranquil PDS using containers with podman.
4···78## Prerequisites
910+- A server :p
11- Disk space for blobs (depends on usage; plan for ~1GB per active user as a baseline)
12- A domain name pointing to your server's IP
13- A **wildcard TLS certificate** for `*.pds.example.com` (user handles are served as subdomains)
14+- Root/sudo/doas access
1516+## Quickstart (docker/podman compose)
1718If you just want to get running quickly:
19···38ln -sf live/pds.example.com/privkey.pem certs/privkey.pem
39podman-compose -f docker-compose.prod.yaml restart nginx
40```
41+42+The end!!!
43+44+Or wait, you want more? Perhaps a deployment that comes back on server restart?
4546For production setups with proper service management, continue to either the Debian or Alpine section below.
4748+## Standalone containers (no compose)
4950If you already have postgres running on the host (eg. from the [Debian install guide](install-debian.md)), you can run just the app containers.
51···9596---
9798+# Debian with systemd quadlets
99100+Quadlets are a nice way to run podman containers under systemd.
101102+## Install podman
103104```bash
105apt update
106apt install -y podman
107```
108109+## Create the directory structure
110111```bash
112mkdir -p /etc/containers/systemd
113mkdir -p /srv/tranquil-pds/{postgres,blobs,backups,certs,acme,config}
114```
115116+## Create an environment file
117118```bash
119cp /opt/tranquil-pds/.env.example /srv/tranquil-pds/config/tranquil-pds.env
···125openssl rand -base64 48
126```
127128+## Install quadlet definitions
00129130Copy the quadlet files from the repository:
131```bash
···138139Optional quadlets for valkey and minio are also available in `deploy/quadlets/` if you need them.
140141+## Create nginx configuration
00142143```bash
144cp /opt/tranquil-pds/nginx.frontend.conf /srv/tranquil-pds/config/nginx.conf
145```
146147+## Clone and build images
148149```bash
150cd /opt
···154podman build -t tranquil-pds-frontend:latest ./frontend
155```
156157+## Create podman secrets
158159```bash
160source /srv/tranquil-pds/config/tranquil-pds.env
161echo "$DB_PASSWORD" | podman secret create tranquil-pds-db-password -
162```
163164+## Start services and initialize
165166```bash
167systemctl daemon-reload
···169sleep 10
170```
171172+## Obtain a wildcard SSL cert
000000173174User handles are served as subdomains (eg. `alice.pds.example.com`), so you need a wildcard certificate. Wildcard certs require DNS-01 validation.
175···203systemctl restart tranquil-pds-nginx
204```
205206+## Enable all services
207208```bash
209systemctl enable tranquil-pds-db tranquil-pds-app tranquil-pds-frontend tranquil-pds-nginx
210```
211212+## Configure firewall if you're into that sort of thing
213214```bash
215apt install -y ufw
···219ufw enable
220```
221222+## Cert renewal
223224Add to root's crontab (`crontab -e`):
225```
···228229---
230231+# Alpine with OpenRC
232233+Alpine uses OpenRC, not systemd. So instead of quadlets we'll use podman-compose with an OpenRC service wrapper.
234235+## Install podman
236237```sh
238apk update
···247rc-service podman start
248```
249250+## Create the directory structure
251252```sh
253mkdir -p /srv/tranquil-pds/{data,config}
254mkdir -p /srv/tranquil-pds/data/{postgres,blobs,backups,certs,acme}
255```
256257+## Clone the repo and build images
258259```sh
260cd /opt
···264podman build -t tranquil-pds-frontend:latest ./frontend
265```
266267+## Create an environment file
268269```sh
270cp /opt/tranquil-pds/.env.example /srv/tranquil-pds/config/tranquil-pds.env
···276openssl rand -base64 48
277```
278279+## Set up compose and nginx
280281Copy the production compose and nginx configs:
282```sh
···291Edit `/srv/tranquil-pds/config/nginx.conf` to update cert paths:
292- Change `/etc/nginx/certs/live/${PDS_HOSTNAME}/` to `/etc/nginx/certs/`
293294+## Create OpenRC service
295296```sh
297cat > /etc/init.d/tranquil-pds << 'EOF'
298#!/sbin/openrc-run
299name="tranquil-pds"
300+description="Tranquil PDS AT Protocol PDS"
301command="/usr/bin/podman-compose"
302command_args="-f /srv/tranquil-pds/docker-compose.yml up"
303command_background=true
···325chmod +x /etc/init.d/tranquil-pds
326```
327328+## Initialize services
329330Start services:
331```sh
···343DATABASE_URL="postgres://tranquil_pds:$DB_PASSWORD@$DB_IP:5432/pds" sqlx migrate run --source /opt/tranquil-pds/migrations
344```
345346+## Obtain wildcard SSL cert
347348User handles are served as subdomains (eg. `alice.pds.example.com`), so you need a wildcard certificate. Wildcard certs require DNS-01 validation.
349···375rc-service tranquil-pds restart
376```
377378+## Enable service at boot time
379380```sh
381rc-update add tranquil-pds
382```
383384+## Configure firewall if you're into that sort of thing
385386```sh
387apk add iptables ip6tables
···403/etc/init.d/ip6tables save
404```
405406+## Cert renewal
407408Add to root's crontab (`crontab -e`):
409```
···412413---
414415+# Verification and maintenance
416417+## Verify installation
418419```sh
420curl -s https://pds.example.com/xrpc/_health | jq
421curl -s https://pds.example.com/.well-known/atproto-did
422```
423424+## View logs
425426**Debian:**
427```bash
···456rc-service tranquil-pds restart
457```
458459+## Backup database
460461**Debian:**
462```bash
···468podman exec tranquil-pds-db-1 pg_dump -U tranquil_pds pds > /var/backups/pds-$(date +%Y%m%d).sql
469```
470471+## Custom homepage
472473The frontend container serves `homepage.html` as the landing page. To customize it, either:
474
+21-25
docs/install-debian.md
···1-# Tranquil PDS Production Installation on Debian
23-This guide covers installing Tranquil PDS on Debian 13.
00045## Prerequisites
67-- A VPS with at least 2GB RAM
8-- Disk space for blobs (depends on usage; plan for ~1GB per active user as a baseline)
9- A domain name pointing to your server's IP
10- A wildcard TLS certificate for `*.pds.example.com` (user handles are served as subdomains)
11-- Root or sudo access
1213-## System Setup
1415```bash
16apt update && apt upgrade -y
17apt install -y curl git build-essential pkg-config libssl-dev
18```
1920-## Install Rust
2122```bash
23curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
···38sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;"
39```
4041-## Create Blob Storage Directories
4243```bash
44mkdir -p /var/lib/tranquil/blobs /var/lib/tranquil/backups
···54echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc
55```
5657-## Clone and Build Tranquil PDS
5859```bash
60cd /opt
···66cargo build --release
67```
6869-## Install sqlx-cli and Run Migrations
70-71-```bash
72-cargo install sqlx-cli --no-default-features --features postgres
73-export DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds"
74-sqlx migrate run
75-```
76-77## Configure Tranquil PDS
7879```bash
···87openssl rand -base64 48
88```
8990-## Install Frontend Files
9192```bash
93mkdir -p /var/www/tranquil-pds
···95chown -R www-data:www-data /var/www/tranquil-pds
96```
9798-## Create Systemd Service
99100```bash
101useradd -r -s /sbin/nologin tranquil-pds
···127systemctl start tranquil-pds
128```
129130-## Install and Configure nginx
131132```bash
133apt install -y nginx certbot python3-certbot-nginx
···264systemctl reload nginx
265```
266267-## Obtain Wildcard SSL Certificate
268269User handles are served as subdomains (eg., `alice.pds.example.com`), so you need a wildcard certificate.
270···289systemctl reload nginx
290```
291292-## Configure Firewall
293294```bash
295apt install -y ufw
···299ufw enable
300```
301302-## Verify Installation
303304```bash
305systemctl status tranquil-pds
···323systemctl stop tranquil-pds
324cp target/release/tranquil-pds /usr/local/bin/
325cp -r frontend/dist/* /var/www/tranquil-pds/
326-DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" sqlx migrate run
327systemctl start tranquil-pds
328```
00329330Backup database:
331```bash
332sudo -u postgres pg_dump pds > /var/backups/pds-$(date +%Y%m%d).sql
333```
334335-## Custom Homepage
336337Drop a `homepage.html` in `/var/www/tranquil-pds/` and it becomes your landing page. Account dashboard is at `/app/` so you won't break anything.
338
···1+# Tranquil PDS production installation on debian
23+This guide covers installing Tranquil PDS on Debian.
4+5+It is a "compile the thing on the server itself" -style guide.
6+This cop-out is because Tranquil isn't built and released via CI as of yet.
78## Prerequisites
910+- A server :p
11+- Disk space enough for blobs (depends on usage; plan for ~1GB per active user as a baseline)
12- A domain name pointing to your server's IP
13- A wildcard TLS certificate for `*.pds.example.com` (user handles are served as subdomains)
14+- Root/sudo/doas access
1516+## System setup
1718```bash
19apt update && apt upgrade -y
20apt install -y curl git build-essential pkg-config libssl-dev
21```
2223+## Install rust
2425```bash
26curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
···41sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;"
42```
4344+## Create blob storage directories
4546```bash
47mkdir -p /var/lib/tranquil/blobs /var/lib/tranquil/backups
···57echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc
58```
5960+## Clone and build Tranquil PDS
6162```bash
63cd /opt
···69cargo build --release
70```
710000000072## Configure Tranquil PDS
7374```bash
···82openssl rand -base64 48
83```
8485+## Install frontend files
8687```bash
88mkdir -p /var/www/tranquil-pds
···90chown -R www-data:www-data /var/www/tranquil-pds
91```
9293+## Create systemd service
9495```bash
96useradd -r -s /sbin/nologin tranquil-pds
···122systemctl start tranquil-pds
123```
124125+## Install and configure nginx
126127```bash
128apt install -y nginx certbot python3-certbot-nginx
···259systemctl reload nginx
260```
261262+## Obtain a wildcard SSL cert
263264User handles are served as subdomains (eg., `alice.pds.example.com`), so you need a wildcard certificate.
265···284systemctl reload nginx
285```
286287+## Configure firewall if you're into that sort of thing
288289```bash
290apt install -y ufw
···294ufw enable
295```
296297+## Verify installation
298299```bash
300systemctl status tranquil-pds
···318systemctl stop tranquil-pds
319cp target/release/tranquil-pds /usr/local/bin/
320cp -r frontend/dist/* /var/www/tranquil-pds/
0321systemctl start tranquil-pds
322```
323+324+Tranquil should auto-migrate if there are any new migrations to be applied to the db, so you don't need to worry.
325326Backup database:
327```bash
328sudo -u postgres pg_dump pds > /var/backups/pds-$(date +%Y%m%d).sql
329```
330331+## Custom homepage
332333Drop a `homepage.html` in `/var/www/tranquil-pds/` and it becomes your landing page. Account dashboard is at `/app/` so you won't break anything.
334
+2-2
docs/install-kubernetes.md
···1-# Tranquil PDS on Kubernetes
23If you're reaching for kubernetes for this app, you're experienced enough to know how to spin up:
4···1920Health check: `GET /xrpc/_health`
2122-## Custom Homepage
2324Mount a ConfigMap with your `homepage.html` into the container's frontend directory and it becomes your landing page. Go nuts with it. Account dashboard is at `/app/` so you won't break anything.
25
···1+# Tranquil PDS on kubernetes
23If you're reaching for kubernetes for this app, you're experienced enough to know how to spin up:
4···1920Health check: `GET /xrpc/_health`
2122+## Custom homepage
2324Mount a ConfigMap with your `homepage.html` into the container's frontend directory and it becomes your landing page. Go nuts with it. Account dashboard is at `/app/` so you won't break anything.
25
+2-2
frontend/public/homepage.html
···420 and alerts, granular OAuth scopes with human-readable descriptions,
421 app passwords with configurable permissions (read-only, post-only,
422 or custom scopes), account delegation with permission levels and
423- audit logging, and a built-in web UI for account management, OAuth
424- consent, repo browsing, and admin.
425 </p>
426 </div>
427 </section>
···420 and alerts, granular OAuth scopes with human-readable descriptions,
421 app passwords with configurable permissions (read-only, post-only,
422 or custom scopes), account delegation with permission levels and
423+ audit logging, and a built-in web UI for account management,
424+ repo browsing, and admin.
425 </p>
426 </div>
427 </section>