Our Personal Data Server from scratch!
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

1# Tranquil PDS Production Installation on Debian 2 3This guide covers installing Tranquil PDS on Debian 13. 4 5## Prerequisites 6 7- A VPS with at least 2GB RAM and 20GB disk 8- A domain name pointing to your server's IP 9- A wildcard TLS certificate for `*.pds.example.com` (user handles are served as subdomains) 10- Root or sudo access 11 12## System Setup 13 14```bash 15apt update && apt upgrade -y 16apt install -y curl git build-essential pkg-config libssl-dev 17``` 18 19## Install Rust 20 21```bash 22curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 23source ~/.cargo/env 24rustup default stable 25``` 26 27This installs the latest stable Rust. 28 29## Install postgres 30 31```bash 32apt install -y postgresql postgresql-contrib 33systemctl enable postgresql 34systemctl start postgresql 35sudo -u postgres psql -c "CREATE USER tranquil_pds WITH PASSWORD 'your-secure-password';" 36sudo -u postgres psql -c "CREATE DATABASE pds OWNER tranquil_pds;" 37sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE pds TO tranquil_pds;" 38``` 39 40## Install minio 41 42```bash 43curl -O https://dl.min.io/server/minio/release/linux-amd64/minio 44chmod +x minio 45mv minio /usr/local/bin/ 46mkdir -p /var/lib/minio/data 47useradd -r -s /sbin/nologin minio-user 48chown -R minio-user:minio-user /var/lib/minio 49cat > /etc/default/minio << 'EOF' 50MINIO_ROOT_USER=minioadmin 51MINIO_ROOT_PASSWORD=your-minio-password 52MINIO_VOLUMES="/var/lib/minio/data" 53MINIO_OPTS="--console-address :9001" 54EOF 55cat > /etc/systemd/system/minio.service << 'EOF' 56[Unit] 57Description=MinIO Object Storage 58After=network.target 59[Service] 60User=minio-user 61Group=minio-user 62EnvironmentFile=/etc/default/minio 63ExecStart=/usr/local/bin/minio server $MINIO_VOLUMES $MINIO_OPTS 64Restart=always 65LimitNOFILE=65536 66[Install] 67WantedBy=multi-user.target 68EOF 69systemctl daemon-reload 70systemctl enable minio 71systemctl start minio 72``` 73 74Create the buckets (wait a few seconds for minio to start): 75```bash 76curl -O https://dl.min.io/client/mc/release/linux-amd64/mc 77chmod +x mc 78mv mc /usr/local/bin/ 79mc alias set local http://localhost:9000 minioadmin your-minio-password 80mc mb local/pds-blobs 81mc mb local/pds-backups 82``` 83 84## Install valkey 85 86```bash 87apt install -y valkey 88systemctl enable valkey-server 89systemctl start valkey-server 90``` 91 92## Install deno (for frontend build) 93 94```bash 95curl -fsSL https://deno.land/install.sh | sh 96export PATH="$HOME/.deno/bin:$PATH" 97echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc 98``` 99 100## Clone and Build Tranquil PDS 101 102```bash 103cd /opt 104git clone https://tangled.org/tranquil.farm/tranquil-pds tranquil-pds 105cd tranquil-pds 106cd frontend 107deno task build 108cd .. 109cargo build --release 110``` 111 112## Install sqlx-cli and Run Migrations 113 114```bash 115cargo install sqlx-cli --no-default-features --features postgres 116export DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" 117sqlx migrate run 118``` 119 120## Configure Tranquil PDS 121 122```bash 123mkdir -p /etc/tranquil-pds 124cp /opt/tranquil-pds/.env.example /etc/tranquil-pds/tranquil-pds.env 125chmod 600 /etc/tranquil-pds/tranquil-pds.env 126``` 127 128Edit `/etc/tranquil-pds/tranquil-pds.env` and fill in your values. Generate secrets with: 129```bash 130openssl rand -base64 48 131``` 132 133## Install Frontend Files 134 135```bash 136mkdir -p /var/www/tranquil-pds 137cp -r /opt/tranquil-pds/frontend/dist/* /var/www/tranquil-pds/ 138chown -R www-data:www-data /var/www/tranquil-pds 139``` 140 141## Create Systemd Service 142 143```bash 144useradd -r -s /sbin/nologin tranquil-pds 145cp /opt/tranquil-pds/target/release/tranquil-pds /usr/local/bin/ 146 147cat > /etc/systemd/system/tranquil-pds.service << 'EOF' 148[Unit] 149Description=Tranquil PDS - AT Protocol PDS 150After=network.target postgresql.service minio.service 151[Service] 152Type=simple 153User=tranquil-pds 154Group=tranquil-pds 155EnvironmentFile=/etc/tranquil-pds/tranquil-pds.env 156ExecStart=/usr/local/bin/tranquil-pds 157Restart=always 158RestartSec=5 159[Install] 160WantedBy=multi-user.target 161EOF 162 163systemctl daemon-reload 164systemctl enable tranquil-pds 165systemctl start tranquil-pds 166``` 167 168## Install and Configure nginx 169 170```bash 171apt install -y nginx certbot python3-certbot-nginx 172 173cat > /etc/nginx/sites-available/tranquil-pds << 'EOF' 174server { 175 listen 80; 176 listen [::]:80; 177 server_name pds.example.com *.pds.example.com; 178 179 location /.well-known/acme-challenge/ { 180 root /var/www/acme; 181 } 182 183 location / { 184 return 301 https://$host$request_uri; 185 } 186} 187 188server { 189 listen 443 ssl; 190 listen [::]:443 ssl; 191 http2 on; 192 server_name pds.example.com *.pds.example.com; 193 194 ssl_certificate /etc/letsencrypt/live/pds.example.com/fullchain.pem; 195 ssl_certificate_key /etc/letsencrypt/live/pds.example.com/privkey.pem; 196 197 client_max_body_size 10G; 198 199 root /var/www/tranquil-pds; 200 201 location /xrpc/ { 202 proxy_pass http://127.0.0.1:3000; 203 proxy_http_version 1.1; 204 proxy_set_header Upgrade $http_upgrade; 205 proxy_set_header Connection "upgrade"; 206 proxy_set_header Host $host; 207 proxy_set_header X-Real-IP $remote_addr; 208 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 209 proxy_set_header X-Forwarded-Proto $scheme; 210 proxy_read_timeout 86400; 211 proxy_send_timeout 86400; 212 proxy_buffering off; 213 proxy_request_buffering off; 214 } 215 216 location /oauth/ { 217 proxy_pass http://127.0.0.1:3000; 218 proxy_http_version 1.1; 219 proxy_set_header Host $host; 220 proxy_set_header X-Real-IP $remote_addr; 221 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 222 proxy_set_header X-Forwarded-Proto $scheme; 223 proxy_read_timeout 300; 224 proxy_send_timeout 300; 225 } 226 227 location /.well-known/ { 228 proxy_pass http://127.0.0.1:3000; 229 proxy_http_version 1.1; 230 proxy_set_header Host $host; 231 proxy_set_header X-Real-IP $remote_addr; 232 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 233 proxy_set_header X-Forwarded-Proto $scheme; 234 } 235 236 location = /metrics { 237 proxy_pass http://127.0.0.1:3000; 238 proxy_http_version 1.1; 239 proxy_set_header Host $host; 240 } 241 242 location = /health { 243 proxy_pass http://127.0.0.1:3000; 244 proxy_http_version 1.1; 245 proxy_set_header Host $host; 246 } 247 248 location = /robots.txt { 249 proxy_pass http://127.0.0.1:3000; 250 proxy_http_version 1.1; 251 proxy_set_header Host $host; 252 } 253 254 location = /logo { 255 proxy_pass http://127.0.0.1:3000; 256 proxy_http_version 1.1; 257 proxy_set_header Host $host; 258 } 259 260 location ~ ^/u/[^/]+/did\.json$ { 261 proxy_pass http://127.0.0.1:3000; 262 proxy_http_version 1.1; 263 proxy_set_header Host $host; 264 proxy_set_header X-Real-IP $remote_addr; 265 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 266 proxy_set_header X-Forwarded-Proto $scheme; 267 } 268 269 location /assets/ { 270 expires 1y; 271 add_header Cache-Control "public, immutable"; 272 try_files $uri =404; 273 } 274 275 location /app/ { 276 try_files $uri $uri/ /index.html; 277 } 278 279 location = / { 280 try_files /homepage.html /index.html; 281 } 282 283 location / { 284 try_files $uri $uri/ /index.html; 285 } 286} 287EOF 288 289ln -sf /etc/nginx/sites-available/tranquil-pds /etc/nginx/sites-enabled/ 290rm -f /etc/nginx/sites-enabled/default 291mkdir -p /var/www/acme 292nginx -t 293systemctl reload nginx 294``` 295 296## Obtain Wildcard SSL Certificate 297 298User handles are served as subdomains (eg., `alice.pds.example.com`), so you need a wildcard certificate. 299 300Wildcard certs require DNS-01 validation. If your DNS provider has a certbot plugin: 301```bash 302apt install -y python3-certbot-dns-cloudflare 303certbot certonly --dns-cloudflare \ 304 --dns-cloudflare-credentials /etc/cloudflare.ini \ 305 -d pds.example.com -d '*.pds.example.com' 306``` 307 308For manual DNS validation (works with any provider): 309```bash 310certbot certonly --manual --preferred-challenges dns \ 311 -d pds.example.com -d '*.pds.example.com' 312``` 313 314Follow the prompts to add TXT records to your DNS. Note: manual mode doesn't auto-renew. 315 316After obtaining the cert, reload nginx: 317```bash 318systemctl reload nginx 319``` 320 321## Configure Firewall 322 323```bash 324apt install -y ufw 325ufw allow ssh 326ufw allow 80/tcp 327ufw allow 443/tcp 328ufw enable 329``` 330 331## Verify Installation 332 333```bash 334systemctl status tranquil-pds 335curl -s https://pds.example.com/xrpc/_health | jq 336curl -s https://pds.example.com/.well-known/atproto-did 337``` 338 339## Maintenance 340 341View logs: 342```bash 343journalctl -u tranquil-pds -f 344``` 345 346Update Tranquil PDS: 347```bash 348cd /opt/tranquil-pds 349git pull 350cd frontend && deno task build && cd .. 351cargo build --release 352systemctl stop tranquil-pds 353cp target/release/tranquil-pds /usr/local/bin/ 354cp -r frontend/dist/* /var/www/tranquil-pds/ 355DATABASE_URL="postgres://tranquil_pds:your-secure-password@localhost:5432/pds" sqlx migrate run 356systemctl start tranquil-pds 357``` 358 359Backup database: 360```bash 361sudo -u postgres pg_dump pds > /var/backups/pds-$(date +%Y%m%d).sql 362``` 363 364## Custom Homepage 365 366Drop 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. 367 368```bash 369cat > /var/www/tranquil-pds/homepage.html << 'EOF' 370<!DOCTYPE html> 371<html> 372<head> 373 <title>Welcome to my PDS</title> 374 <style> 375 body { font-family: system-ui; max-width: 600px; margin: 100px auto; padding: 20px; } 376 </style> 377</head> 378<body> 379 <h1>Welcome to my secret PDS</h1> 380 <p>This is a <a href="https://atproto.com">AT Protocol</a> Personal Data Server.</p> 381 <p><a href="/app/">Sign in</a> or learn more at <a href="https://bsky.social">Bluesky</a>.</p> 382</body> 383</html> 384EOF 385```