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