this repo has no description
1# BSPDS Containerized Production Deployment 2 3> **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. 4 5This guide covers deploying BSPDS using containers with podman. 6 7- **Debian 13+**: Uses systemd quadlets (modern, declarative container management) 8- **Alpine 3.23+**: Uses OpenRC service script with podman-compose 9 10## Prerequisites 11 12- A VPS with at least 2GB RAM and 20GB disk 13- A domain name pointing to your server's IP 14- Root or sudo access 15 16## Quick Start (Docker/Podman Compose) 17 18If you just want to get running quickly: 19 20```sh 21cp .env.example .env 22 23# Edit .env with your values 24# Generate secrets: openssl rand -base64 48 25 26# Build and start 27podman-compose -f docker-compose.prod.yml up -d 28 29# Get initial certificate (after DNS is configured) 30podman-compose -f docker-compose.prod.yml run --rm certbot certonly \ 31 --webroot -w /var/www/acme -d pds.example.com 32 33# Restart nginx to load certificate 34podman-compose -f docker-compose.prod.yml restart nginx 35``` 36 37For production setups with proper service management, continue to either the Debian or Alpine section below. 38 39--- 40 41# Debian 13+ with Systemd Quadlets 42 43Quadlets are the modern way to run podman containers under systemd. 44 45## 1. Install Podman 46 47```bash 48apt update 49apt install -y podman 50``` 51 52## 2. Create Directory Structure 53 54```bash 55mkdir -p /etc/containers/systemd 56mkdir -p /srv/bspds/{postgres,minio,valkey,certs,acme,config} 57``` 58 59## 3. Create Environment File 60 61```bash 62cp /opt/bspds/.env.example /srv/bspds/config/bspds.env 63chmod 600 /srv/bspds/config/bspds.env 64``` 65 66Edit `/srv/bspds/config/bspds.env` and fill in your values. Generate secrets with: 67 68```bash 69openssl rand -base64 48 70``` 71 72For quadlets, also add `DATABASE_URL` with the full connection string (systemd doesn't support variable expansion). 73 74## 4. Install Quadlet Definitions 75 76Copy the quadlet files from the repository: 77 78```bash 79cp /opt/bspds/deploy/quadlets/*.pod /etc/containers/systemd/ 80cp /opt/bspds/deploy/quadlets/*.container /etc/containers/systemd/ 81``` 82 83Note: Systemd doesn't support shell-style variable expansion in `Environment=` lines. The quadlet files expect DATABASE_URL to be set in the environment file. 84 85## 5. Create nginx Configuration 86 87```bash 88cp /opt/bspds/deploy/nginx/nginx-quadlet.conf /srv/bspds/config/nginx.conf 89``` 90 91## 6. Build BSPDS Image 92 93```bash 94cd /opt 95git clone https://tangled.org/lewis.moe/bspds-sandbox bspds 96cd bspds 97podman build -t bspds:latest . 98``` 99 100## 7. Create Podman Secrets 101 102```bash 103source /srv/bspds/config/bspds.env 104echo "$DB_PASSWORD" | podman secret create bspds-db-password - 105echo "$MINIO_ROOT_PASSWORD" | podman secret create bspds-minio-password - 106``` 107 108## 8. Start Services and Initialize 109 110```bash 111systemctl daemon-reload 112systemctl start bspds-db bspds-minio bspds-valkey 113 114sleep 10 115 116# Create MinIO bucket 117podman run --rm --pod bspds \ 118 -e MINIO_ROOT_USER=minioadmin \ 119 -e MINIO_ROOT_PASSWORD=your-minio-password \ 120 docker.io/minio/mc:RELEASE.2025-07-16T15-35-03Z \ 121 sh -c "mc alias set local http://localhost:9000 \$MINIO_ROOT_USER \$MINIO_ROOT_PASSWORD && mc mb --ignore-existing local/pds-blobs" 122 123# Run migrations 124cargo install sqlx-cli --no-default-features --features postgres 125DATABASE_URL="postgres://bspds:your-db-password@localhost:5432/pds" sqlx migrate run --source /opt/bspds/migrations 126``` 127 128## 9. Obtain SSL Certificate 129 130Create temporary self-signed cert: 131 132```bash 133openssl req -x509 -nodes -days 1 -newkey rsa:2048 \ 134 -keyout /srv/bspds/certs/privkey.pem \ 135 -out /srv/bspds/certs/fullchain.pem \ 136 -subj "/CN=pds.example.com" 137 138systemctl start bspds-app bspds-nginx 139 140# Get real certificate 141podman run --rm \ 142 -v /srv/bspds/certs:/etc/letsencrypt:Z \ 143 -v /srv/bspds/acme:/var/www/acme:Z \ 144 docker.io/certbot/certbot:v5.2.2 certonly \ 145 --webroot -w /var/www/acme -d pds.example.com --agree-tos --email you@example.com 146 147# Link certificates 148ln -sf /srv/bspds/certs/live/pds.example.com/fullchain.pem /srv/bspds/certs/fullchain.pem 149ln -sf /srv/bspds/certs/live/pds.example.com/privkey.pem /srv/bspds/certs/privkey.pem 150 151systemctl restart bspds-nginx 152``` 153 154## 10. Enable All Services 155 156```bash 157systemctl enable bspds-db bspds-minio bspds-valkey bspds-app bspds-nginx 158``` 159 160## 11. Configure Firewall 161 162```bash 163apt install -y ufw 164ufw allow ssh 165ufw allow 80/tcp 166ufw allow 443/tcp 167ufw enable 168``` 169 170## 12. Certificate Renewal 171 172Add to root's crontab (`crontab -e`): 173 174``` 1750 0 * * * podman run --rm -v /srv/bspds/certs:/etc/letsencrypt:Z -v /srv/bspds/acme:/var/www/acme:Z docker.io/certbot/certbot:v5.2.2 renew --quiet && systemctl reload bspds-nginx 176``` 177 178--- 179 180# Alpine 3.23+ with OpenRC 181 182Alpine uses OpenRC, not systemd. We'll use podman-compose with an OpenRC service wrapper. 183 184## 1. Install Podman 185 186```sh 187apk update 188apk add podman podman-compose fuse-overlayfs cni-plugins 189rc-update add cgroups 190rc-service cgroups start 191``` 192 193Enable podman socket for compose: 194 195```sh 196rc-update add podman 197rc-service podman start 198``` 199 200## 2. Create Directory Structure 201 202```sh 203mkdir -p /srv/bspds/{data,config} 204mkdir -p /srv/bspds/data/{postgres,minio,valkey,certs,acme} 205``` 206 207## 3. Clone Repository and Build 208 209```sh 210cd /opt 211git clone https://tangled.org/lewis.moe/bspds-sandbox bspds 212cd bspds 213podman build -t bspds:latest . 214``` 215 216## 4. Create Environment File 217 218```sh 219cp /opt/bspds/.env.example /srv/bspds/config/bspds.env 220chmod 600 /srv/bspds/config/bspds.env 221``` 222 223Edit `/srv/bspds/config/bspds.env` and fill in your values. Generate secrets with: 224 225```sh 226openssl rand -base64 48 227``` 228 229## 5. Set Up Compose and nginx 230 231Copy the production compose and nginx configs: 232 233```sh 234cp /opt/bspds/docker-compose.prod.yml /srv/bspds/docker-compose.yml 235cp /opt/bspds/nginx.prod.conf /srv/bspds/config/nginx.conf 236``` 237 238Edit `/srv/bspds/docker-compose.yml` to adjust paths if needed: 239- Update volume mounts to use `/srv/bspds/data/` paths 240- Update nginx cert paths to match `/srv/bspds/data/certs/` 241 242Edit `/srv/bspds/config/nginx.conf` to update cert paths: 243- Change `/etc/nginx/certs/live/${PDS_HOSTNAME}/` to `/etc/nginx/certs/` 244 245## 6. Create OpenRC Service 246 247```sh 248cat > /etc/init.d/bspds << 'EOF' 249#!/sbin/openrc-run 250 251name="bspds" 252description="BSPDS AT Protocol PDS (containerized)" 253 254command="/usr/bin/podman-compose" 255command_args="-f /srv/bspds/docker-compose.yml up" 256command_background=true 257pidfile="/run/${RC_SVCNAME}.pid" 258 259directory="/srv/bspds" 260 261depend() { 262 need net podman 263 after firewall 264} 265 266start_pre() { 267 set -a 268 . /srv/bspds/config/bspds.env 269 set +a 270} 271 272stop() { 273 ebegin "Stopping ${name}" 274 cd /srv/bspds 275 set -a 276 . /srv/bspds/config/bspds.env 277 set +a 278 podman-compose -f /srv/bspds/docker-compose.yml down 279 eend $? 280} 281EOF 282 283chmod +x /etc/init.d/bspds 284``` 285 286## 7. Initialize Services 287 288```sh 289# Start services 290rc-service bspds start 291 292sleep 15 293 294# Create MinIO bucket 295source /srv/bspds/config/bspds.env 296podman run --rm --network bspds_default \ 297 -e MINIO_ROOT_USER="$MINIO_ROOT_USER" \ 298 -e MINIO_ROOT_PASSWORD="$MINIO_ROOT_PASSWORD" \ 299 docker.io/minio/mc:RELEASE.2025-07-16T15-35-03Z \ 300 sh -c 'mc alias set local http://minio:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD && mc mb --ignore-existing local/pds-blobs' 301 302# Run migrations 303apk add rustup 304rustup-init -y 305source ~/.cargo/env 306cargo install sqlx-cli --no-default-features --features postgres 307 308# Get database container IP 309DB_IP=$(podman inspect bspds-db-1 --format '{{.NetworkSettings.Networks.bspds_default.IPAddress}}') 310DATABASE_URL="postgres://bspds:$DB_PASSWORD@$DB_IP:5432/pds" sqlx migrate run --source /opt/bspds/migrations 311``` 312 313## 8. Obtain SSL Certificate 314 315Create temporary self-signed cert: 316 317```sh 318openssl req -x509 -nodes -days 1 -newkey rsa:2048 \ 319 -keyout /srv/bspds/data/certs/privkey.pem \ 320 -out /srv/bspds/data/certs/fullchain.pem \ 321 -subj "/CN=pds.example.com" 322 323rc-service bspds restart 324 325# Get real certificate 326podman run --rm \ 327 -v /srv/bspds/data/certs:/etc/letsencrypt \ 328 -v /srv/bspds/data/acme:/var/www/acme \ 329 --network bspds_default \ 330 docker.io/certbot/certbot:v5.2.2 certonly \ 331 --webroot -w /var/www/acme -d pds.example.com --agree-tos --email you@example.com 332 333# Link certificates 334ln -sf /srv/bspds/data/certs/live/pds.example.com/fullchain.pem /srv/bspds/data/certs/fullchain.pem 335ln -sf /srv/bspds/data/certs/live/pds.example.com/privkey.pem /srv/bspds/data/certs/privkey.pem 336 337rc-service bspds restart 338``` 339 340## 9. Enable Service at Boot 341 342```sh 343rc-update add bspds 344``` 345 346## 10. Configure Firewall 347 348```sh 349apk add iptables ip6tables 350 351iptables -A INPUT -p tcp --dport 22 -j ACCEPT 352iptables -A INPUT -p tcp --dport 80 -j ACCEPT 353iptables -A INPUT -p tcp --dport 443 -j ACCEPT 354iptables -A INPUT -i lo -j ACCEPT 355iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 356iptables -P INPUT DROP 357 358ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT 359ip6tables -A INPUT -p tcp --dport 80 -j ACCEPT 360ip6tables -A INPUT -p tcp --dport 443 -j ACCEPT 361ip6tables -A INPUT -i lo -j ACCEPT 362ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT 363ip6tables -P INPUT DROP 364 365rc-update add iptables 366rc-update add ip6tables 367/etc/init.d/iptables save 368/etc/init.d/ip6tables save 369``` 370 371## 11. Certificate Renewal 372 373Add to root's crontab (`crontab -e`): 374 375``` 3760 0 * * * podman run --rm -v /srv/bspds/data/certs:/etc/letsencrypt -v /srv/bspds/data/acme:/var/www/acme docker.io/certbot/certbot:v5.2.2 renew --quiet && rc-service bspds restart 377``` 378 379--- 380 381# Verification and Maintenance 382 383## Verify Installation 384 385```sh 386curl -s https://pds.example.com/xrpc/_health | jq 387curl -s https://pds.example.com/.well-known/atproto-did 388``` 389 390## View Logs 391 392**Debian:** 393```bash 394journalctl -u bspds-app -f 395podman logs -f bspds-app 396``` 397 398**Alpine:** 399```sh 400podman-compose -f /srv/bspds/docker-compose.yml logs -f 401podman logs -f bspds-bspds-1 402``` 403 404## Update BSPDS 405 406```sh 407cd /opt/bspds 408git pull 409podman build -t bspds:latest . 410 411# Debian: 412systemctl restart bspds-app 413 414# Alpine: 415rc-service bspds restart 416``` 417 418## Backup Database 419 420**Debian:** 421```bash 422podman exec bspds-db pg_dump -U bspds pds > /var/backups/pds-$(date +%Y%m%d).sql 423``` 424 425**Alpine:** 426```sh 427podman exec bspds-db-1 pg_dump -U bspds pds > /var/backups/pds-$(date +%Y%m%d).sql 428```