forked from
lewis.moe/bspds-sandbox
I've been saying "PDSes seem easy enough, they're what, some CRUD to a db? I can do that in my sleep". well i'm sleeping rn so let's go
1# Tranquil PDS Production Installation on OpenBSD
2> **Warning**: These instructions are untested and theoretical, written from the top of Lewis' head. They may contain errors or omissions. This warning will be removed once the guide has been verified.
3This guide covers installing Tranquil PDS on OpenBSD 7.8.
4## Prerequisites
5- A VPS with at least 2GB RAM and 20GB disk
6- A domain name pointing to your server's IP
7- A **wildcard TLS certificate** for `*.pds.example.com` (user handles are served as subdomains)
8- Root access (or doas configured)
9## Why nginx over relayd?
10OpenBSD's native `relayd` supports WebSockets but does **not** support HTTP/2. For a modern PDS deployment, we recommend nginx which provides HTTP/2, WebSocket support, and automatic OCSP stapling.
11## 1. System Setup
12```sh
13pkg_add curl git
14```
15## 2. Install Rust
16```sh
17pkg_add rust
18```
19OpenBSD ships Rust in ports. For the latest stable, use rustup:
20```sh
21pkg_add rustup
22rustup-init -y
23source ~/.cargo/env
24rustup default stable
25```
26## 3. Install postgres
27```sh
28pkg_add postgresql-server postgresql-client
29mkdir -p /var/postgresql/data
30chown _postgresql:_postgresql /var/postgresql/data
31su - _postgresql -c "initdb -D /var/postgresql/data -U postgres -A scram-sha-256"
32rcctl enable postgresql
33rcctl start postgresql
34psql -U postgres -c "CREATE USER tranquil_pds WITH PASSWORD 'your-secure-password';"
35psql -U postgres -c "CREATE DATABASE pds OWNER tranquil_pds;"
36psql -U postgres -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;"
37```
38## 4. Install minio
39OpenBSD doesn't have a minio package. Options:
40**Option A: Use an external S3-compatible service (recommended for production)**
41aws s3, backblaze b2, or upcloud managed object storage. Skip to step 5 and configure the S3 credentials in step 9.
42**Option B: Build minio from source**
43```sh
44pkg_add go
45mkdir -p /tmp/minio-build && cd /tmp/minio-build
46ftp -o minio.tar.gz https://github.com/minio/minio/archive/refs/tags/RELEASE.2025-10-15T17-29-55Z.tar.gz
47tar xzf minio.tar.gz
48cd minio-*
49go build -o minio .
50cp minio /usr/local/bin/
51mkdir -p /var/minio/data
52useradd -d /var/minio -s /sbin/nologin _minio
53chown -R _minio:_minio /var/minio
54cat > /etc/minio.conf << 'EOF'
55MINIO_ROOT_USER=minioadmin
56MINIO_ROOT_PASSWORD=your-minio-password
57EOF
58chmod 600 /etc/minio.conf
59cat > /etc/rc.d/minio << 'EOF'
60#!/bin/ksh
61daemon="/usr/local/bin/minio"
62daemon_user="_minio"
63daemon_flags="server /var/minio/data --console-address :9001"
64. /etc/rc.d/rc.subr
65rc_pre() {
66 . /etc/minio.conf
67 export MINIO_ROOT_USER MINIO_ROOT_PASSWORD
68}
69rc_cmd $1
70EOF
71chmod +x /etc/rc.d/minio
72rcctl enable minio
73rcctl start minio
74```
75Create the buckets:
76```sh
77ftp -o /usr/local/bin/mc https://dl.min.io/client/mc/release/openbsd-amd64/mc
78chmod +x /usr/local/bin/mc
79mc alias set local http://localhost:9000 minioadmin your-minio-password
80mc mb local/pds-blobs
81mc mb local/pds-backups
82```
83## 5. Install redis
84OpenBSD has redis in ports (valkey not available yet):
85```sh
86pkg_add redis
87rcctl enable redis
88rcctl start redis
89```
90## 6. Install deno (for frontend build)
91```sh
92curl -fsSL https://deno.land/install.sh | sh
93export PATH="$HOME/.deno/bin:$PATH"
94echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.profile
95```
96## 7. Clone and Build Tranquil PDS
97```sh
98mkdir -p /opt && cd /opt
99git clone https://tangled.org/lewis.moe/bspds-sandbox tranquil-pds
100cd tranquil-pds
101cd frontend
102deno task build
103cd ..
104cargo build --release
105```
106## 8. Install sqlx-cli and Run Migrations
107```sh
108cargo install sqlx-cli --no-default-features --features postgres
109export DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds"
110sqlx migrate run
111```
112## 9. Configure Tranquil PDS
113```sh
114mkdir -p /etc/tranquil-pds
115cp /opt/tranquil-pds/.env.example /etc/tranquil-pds/tranquil-pds.conf
116chmod 600 /etc/tranquil-pds/tranquil-pds.conf
117```
118Edit `/etc/tranquil-pds/tranquil-pds.conf` and fill in your values. Generate secrets with:
119```sh
120openssl rand -base64 48
121```
122## 10. Create rc.d Service
123```sh
124useradd -d /var/empty -s /sbin/nologin _tranquil_pds
125cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/
126mkdir -p /var/tranquil-pds
127cp -r /opt/tranquil-pds/frontend/dist /var/tranquil-pds/frontend
128chown -R _tranquil_pds:_tranquil_pds /var/tranquil-pds
129cat > /etc/rc.d/tranquil_pds << 'EOF'
130#!/bin/ksh
131daemon="/usr/local/bin/tranquil-pds"
132daemon_user="_tranquil_pds"
133daemon_logger="daemon.info"
134. /etc/rc.d/rc.subr
135rc_pre() {
136 export FRONTEND_DIR=/var/tranquil-pds/frontend
137 while IFS='=' read -r key value; do
138 case "$key" in
139 \#*|"") continue ;;
140 esac
141 export "$key=$value"
142 done < /etc/tranquil-pds/tranquil-pds.conf
143}
144rc_cmd $1
145EOF
146chmod +x /etc/rc.d/tranquil_pds
147rcctl enable tranquil_pds
148rcctl start tranquil_pds
149```
150## 11. Install and Configure nginx
151```sh
152pkg_add nginx
153cat > /etc/nginx/nginx.conf << 'EOF'
154worker_processes 1;
155events {
156 worker_connections 1024;
157}
158http {
159 include mime.types;
160 server {
161 listen 80;
162 listen [::]:80;
163 server_name pds.example.com;
164 location /.well-known/acme-challenge/ {
165 root /var/www/acme;
166 }
167 location / {
168 return 301 https://$host$request_uri;
169 }
170 }
171 server {
172 listen 443 ssl http2;
173 listen [::]:443 ssl http2;
174 server_name pds.example.com;
175 ssl_certificate /etc/ssl/pds.example.com.fullchain.pem;
176 ssl_certificate_key /etc/ssl/private/pds.example.com.key;
177 ssl_protocols TLSv1.2 TLSv1.3;
178 ssl_ciphers HIGH:!aNULL:!MD5;
179 ssl_prefer_server_ciphers on;
180 ssl_session_cache shared:SSL:10m;
181 location / {
182 proxy_pass http://127.0.0.1:3000;
183 proxy_http_version 1.1;
184 proxy_set_header Upgrade $http_upgrade;
185 proxy_set_header Connection "upgrade";
186 proxy_set_header Host $host;
187 proxy_set_header X-Real-IP $remote_addr;
188 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
189 proxy_set_header X-Forwarded-Proto $scheme;
190 proxy_read_timeout 86400;
191 }
192 }
193}
194EOF
195mkdir -p /var/www/acme
196rcctl enable nginx
197```
198## 12. Obtain Wildcard SSL Certificate
199User handles are served as subdomains (e.g., `alice.pds.example.com`), so you need a wildcard certificate.
200
201OpenBSD's native `acme-client` only supports HTTP-01 validation, which can't issue wildcard certs. You have a few options:
202
203**Option A: Use certbot with DNS validation (recommended)**
204```sh
205pkg_add certbot
206certbot certonly --manual --preferred-challenges dns \
207 -d pds.example.com -d '*.pds.example.com'
208```
209Follow the prompts to add TXT records to your DNS. Then update nginx.conf to point to the certbot certs.
210
211**Option B: Use a managed DNS provider with API**
212If your DNS provider has a certbot plugin, you can automate renewal.
213
214**Option C: Use acme.sh**
215[acme.sh](https://github.com/acmesh-official/acme.sh) supports many DNS providers for automated wildcard cert renewal.
216
217After obtaining the cert, update nginx to use it and restart:
218```sh
219rcctl restart nginx
220```
221## 13. Configure Packet Filter (pf)
222```sh
223cat >> /etc/pf.conf << 'EOF'
224pass in on egress proto tcp from any to any port { 22, 80, 443 }
225EOF
226pfctl -f /etc/pf.conf
227```
228## 14. Verify Installation
229```sh
230rcctl check tranquil_pds
231ftp -o - https://pds.example.com/xrpc/_health
232ftp -o - https://pds.example.com/.well-known/atproto-did
233```
234## Maintenance
235View logs:
236```sh
237tail -f /var/log/daemon
238```
239Update Tranquil PDS:
240```sh
241cd /opt/tranquil-pds
242git pull
243cd frontend && deno task build && cd ..
244cargo build --release
245rcctl stop tranquil_pds
246cp target/release/tranquil-pds /usr/local/bin/
247cp -r frontend/dist /var/tranquil-pds/frontend
248DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" sqlx migrate run
249rcctl start tranquil_pds
250```
251Backup database:
252```sh
253pg_dump -U postgres pds > /var/backups/pds-$(date +%Y%m%d).sql
254```
255
256## Custom Homepage
257
258Drop a `homepage.html` in `/var/tranquil-pds/frontend/` and it becomes your landing page. Go nuts with it. Account dashboard is at `/app/` so you won't break anything.
259
260```sh
261cat > /var/tranquil-pds/frontend/homepage.html << 'EOF'
262<!DOCTYPE html>
263<html>
264<head>
265 <title>Welcome to my PDS</title>
266 <style>
267 body { font-family: system-ui; max-width: 600px; margin: 100px auto; padding: 20px; }
268 </style>
269</head>
270<body>
271 <h1>Welcome to my uma musume shipping site!</h1>
272 <p>This is a <a href="https://atproto.com">AT Protocol</a> Personal Data Server.</p>
273 <p><a href="/app/">Sign in</a> or learn more at <a href="https://bsky.social">Bluesky</a>.</p>
274</body>
275</html>
276EOF
277```