···11# BSPDS Production Installation on Alpine Linux
22> **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.
33+34This guide covers installing BSPDS on Alpine Linux 3.23 (current stable as of December 2025).
44-## Choose Your Installation Method
55-| Method | Best For |
66-|--------|----------|
77-| **Native (this guide)** | Maximum performance, minimal footprint, full control |
88-| **[Containerized](install-containers.md)** | Easier updates, isolation, reproducible deployments |
99-| **[Kubernetes](install-kubernetes.md)** | Multi-node, high availability, auto-scaling |
1010-This guide covers native installation. For containerized deployment with podman and systemd quadlets, see the [container guide](install-containers.md).
1111----
55+126## Prerequisites
137- A VPS with at least 2GB RAM and 20GB disk
148- A domain name pointing to your server's IP
99+- A **wildcard TLS certificate** for `*.pds.example.com` (user handles are served as subdomains)
1510- Root access
1611## 1. System Setup
1712```sh
···178173rc-update add nginx
179174rc-service nginx start
180175```
181181-## 12. Obtain SSL Certificate
176176+## 12. Obtain Wildcard SSL Certificate
177177+User handles are served as subdomains (e.g., `alice.pds.example.com`), so you need a wildcard certificate.
178178+179179+Wildcard certs require DNS-01 validation. For manual DNS validation (works with any provider):
182180```sh
183183-certbot --nginx -d pds.example.com
181181+certbot certonly --manual --preferred-challenges dns \
182182+ -d pds.example.com -d '*.pds.example.com'
184183```
185185-Set up auto-renewal:
184184+Follow the prompts to add TXT records to your DNS.
185185+186186+If your DNS provider has a certbot plugin, you can use that for auto-renewal:
186187```sh
187187-echo "0 0 * * * certbot renew --quiet" | crontab -
188188+apk add certbot-dns-cloudflare
189189+certbot certonly --dns-cloudflare \
190190+ --dns-cloudflare-credentials /etc/cloudflare.ini \
191191+ -d pds.example.com -d '*.pds.example.com'
192192+```
193193+194194+After obtaining the cert, update nginx to use it, then set up auto-renewal:
195195+```sh
196196+echo "0 0 * * * certbot renew --quiet && rc-service nginx reload" | crontab -
188197```
189198## 13. Configure Firewall
190199```sh
+72-28
docs/install-containers.md
···66## Prerequisites
77- A VPS with at least 2GB RAM and 20GB disk
88- A domain name pointing to your server's IP
99+- A **wildcard TLS certificate** for `*.pds.example.com` (user handles are served as subdomains)
910- Root or sudo access
1011## Quick Start (Docker/Podman Compose)
1112If you just want to get running quickly:
1213```sh
1314cp .env.example .env
1414-# Edit .env with your values
1515-# Generate secrets: openssl rand -base64 48
1616-# Build and start
1515+```
1616+1717+Edit `.env` with your values. Generate secrets with `openssl rand -base64 48`.
1818+1919+Build and start:
2020+```sh
1721podman-compose -f docker-compose.prod.yml up -d
1818-# Get initial certificate (after DNS is configured)
2222+```
2323+2424+Get initial certificate (after DNS is configured):
2525+```sh
1926podman-compose -f docker-compose.prod.yml run --rm certbot certonly \
2027 --webroot -w /var/www/acme -d pds.example.com
2121-# Restart nginx to load certificate
2228podman-compose -f docker-compose.prod.yml restart nginx
2329```
2430For production setups with proper service management, continue to either the Debian or Alpine section below.
···7480systemctl daemon-reload
7581systemctl start bspds-db bspds-minio bspds-valkey
7682sleep 10
7777-# Create MinIO bucket
8383+```
8484+8585+Create the minio bucket:
8686+```bash
7887podman run --rm --pod bspds \
7988 -e MINIO_ROOT_USER=minioadmin \
8089 -e MINIO_ROOT_PASSWORD=your-minio-password \
8190 docker.io/minio/mc:RELEASE.2025-07-16T15-35-03Z \
8291 sh -c "mc alias set local http://localhost:9000 \$MINIO_ROOT_USER \$MINIO_ROOT_PASSWORD && mc mb --ignore-existing local/pds-blobs"
8383-# Run migrations
9292+```
9393+9494+Run migrations:
9595+```bash
8496cargo install sqlx-cli --no-default-features --features postgres
8597DATABASE_URL="postgres://bspds:your-db-password@localhost:5432/pds" sqlx migrate run --source /opt/bspds/migrations
8698```
8787-## 9. Obtain SSL Certificate
8888-Create temporary self-signed cert:
9999+## 9. Obtain Wildcard SSL Certificate
100100+User handles are served as subdomains (e.g., `alice.pds.example.com`), so you need a wildcard certificate. Wildcard certs require DNS-01 validation.
101101+102102+Create temporary self-signed cert to start services:
89103```bash
90104openssl req -x509 -nodes -days 1 -newkey rsa:2048 \
91105 -keyout /srv/bspds/certs/privkey.pem \
92106 -out /srv/bspds/certs/fullchain.pem \
93107 -subj "/CN=pds.example.com"
94108systemctl start bspds-app bspds-nginx
9595-# Get real certificate
9696-podman run --rm \
109109+```
110110+111111+Get a wildcard certificate using DNS validation:
112112+```bash
113113+podman run --rm -it \
97114 -v /srv/bspds/certs:/etc/letsencrypt:Z \
9898- -v /srv/bspds/acme:/var/www/acme:Z \
99115 docker.io/certbot/certbot:v5.2.2 certonly \
100100- --webroot -w /var/www/acme -d pds.example.com --agree-tos --email you@example.com
101101-# Link certificates
116116+ --manual --preferred-challenges dns \
117117+ -d pds.example.com -d '*.pds.example.com' \
118118+ --agree-tos --email you@example.com
119119+```
120120+Follow the prompts to add TXT records to your DNS. Note: manual mode doesn't auto-renew.
121121+122122+For automated renewal, use a DNS provider plugin (e.g., cloudflare, route53).
123123+124124+Link certificates and restart:
125125+```bash
102126ln -sf /srv/bspds/certs/live/pds.example.com/fullchain.pem /srv/bspds/certs/fullchain.pem
103127ln -sf /srv/bspds/certs/live/pds.example.com/privkey.pem /srv/bspds/certs/privkey.pem
104128systemctl restart bspds-nginx
···200224chmod +x /etc/init.d/bspds
201225```
202226## 7. Initialize Services
227227+Start services:
203228```sh
204204-# Start services
205229rc-service bspds start
206230sleep 15
207207-# Create MinIO bucket
231231+```
232232+233233+Create the minio bucket:
234234+```sh
208235source /srv/bspds/config/bspds.env
209236podman run --rm --network bspds_default \
210237 -e MINIO_ROOT_USER="$MINIO_ROOT_USER" \
211238 -e MINIO_ROOT_PASSWORD="$MINIO_ROOT_PASSWORD" \
212239 docker.io/minio/mc:RELEASE.2025-07-16T15-35-03Z \
213240 sh -c 'mc alias set local http://minio:9000 $MINIO_ROOT_USER $MINIO_ROOT_PASSWORD && mc mb --ignore-existing local/pds-blobs'
214214-# Run migrations
241241+```
242242+243243+Run migrations:
244244+```sh
215245apk add rustup
216246rustup-init -y
217247source ~/.cargo/env
218248cargo install sqlx-cli --no-default-features --features postgres
219219-# Get database container IP
220249DB_IP=$(podman inspect bspds-db-1 --format '{{.NetworkSettings.Networks.bspds_default.IPAddress}}')
221250DATABASE_URL="postgres://bspds:$DB_PASSWORD@$DB_IP:5432/pds" sqlx migrate run --source /opt/bspds/migrations
222251```
223223-## 8. Obtain SSL Certificate
224224-Create temporary self-signed cert:
252252+## 8. Obtain Wildcard SSL Certificate
253253+User handles are served as subdomains (e.g., `alice.pds.example.com`), so you need a wildcard certificate. Wildcard certs require DNS-01 validation.
254254+255255+Create temporary self-signed cert to start services:
225256```sh
226257openssl req -x509 -nodes -days 1 -newkey rsa:2048 \
227258 -keyout /srv/bspds/data/certs/privkey.pem \
228259 -out /srv/bspds/data/certs/fullchain.pem \
229260 -subj "/CN=pds.example.com"
230261rc-service bspds restart
231231-# Get real certificate
232232-podman run --rm \
262262+```
263263+264264+Get a wildcard certificate using DNS validation:
265265+```sh
266266+podman run --rm -it \
233267 -v /srv/bspds/data/certs:/etc/letsencrypt \
234234- -v /srv/bspds/data/acme:/var/www/acme \
235235- --network bspds_default \
236268 docker.io/certbot/certbot:v5.2.2 certonly \
237237- --webroot -w /var/www/acme -d pds.example.com --agree-tos --email you@example.com
238238-# Link certificates
269269+ --manual --preferred-challenges dns \
270270+ -d pds.example.com -d '*.pds.example.com' \
271271+ --agree-tos --email you@example.com
272272+```
273273+Follow the prompts to add TXT records to your DNS. Note: manual mode doesn't auto-renew.
274274+275275+Link certificates and restart:
276276+```sh
239277ln -sf /srv/bspds/data/certs/live/pds.example.com/fullchain.pem /srv/bspds/data/certs/fullchain.pem
240278ln -sf /srv/bspds/data/certs/live/pds.example.com/privkey.pem /srv/bspds/data/certs/privkey.pem
241279rc-service bspds restart
···292330cd /opt/bspds
293331git pull
294332podman build -t bspds:latest .
295295-# Debian:
333333+```
334334+335335+Debian:
336336+```bash
296337systemctl restart bspds-app
297297-# Alpine:
338338+```
339339+340340+Alpine:
341341+```sh
298342rc-service bspds restart
299343```
300344## Backup Database
+20-11
docs/install-debian.md
···11# BSPDS Production Installation on Debian
22> **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.
33+34This guide covers installing BSPDS on Debian 13 "Trixie" (current stable as of December 2025).
44-## Choose Your Installation Method
55-| Method | Best For |
66-|--------|----------|
77-| **Native (this guide)** | Maximum performance, full control, simpler debugging |
88-| **[Containerized](install-containers.md)** | Easier updates, isolation, reproducible deployments |
99-| **[Kubernetes](install-kubernetes.md)** | Multi-node, high availability, auto-scaling |
1010-This guide covers native installation. For containerized deployment with podman and systemd quadlets, see the [container guide](install-containers.md).
1111----
55+126## Prerequisites
137- A VPS with at least 2GB RAM and 20GB disk
148- A domain name pointing to your server's IP
99+- A **wildcard TLS certificate** for `*.pds.example.com` (user handles are served as subdomains)
1510- Root or sudo access
1611## 1. System Setup
1712```bash
···168163nginx -t
169164systemctl reload nginx
170165```
171171-## 12. Obtain SSL Certificate
166166+## 12. Obtain Wildcard SSL Certificate
167167+User handles are served as subdomains (e.g., `alice.pds.example.com`), so you need a wildcard certificate.
168168+169169+Wildcard certs require DNS-01 validation. If your DNS provider has a certbot plugin:
172170```bash
173173-certbot --nginx -d pds.example.com
171171+apt install -y python3-certbot-dns-cloudflare
172172+certbot certonly --dns-cloudflare \
173173+ --dns-cloudflare-credentials /etc/cloudflare.ini \
174174+ -d pds.example.com -d '*.pds.example.com'
174175```
175175-Certbot automatically configures nginx for HTTP/2 and sets up auto-renewal.
176176+177177+For manual DNS validation (works with any provider):
178178+```bash
179179+certbot certonly --manual --preferred-challenges dns \
180180+ -d pds.example.com -d '*.pds.example.com'
181181+```
182182+Follow the prompts to add TXT records to your DNS. Note: manual mode doesn't auto-renew.
183183+184184+After obtaining the cert, update nginx to use it and reload.
176185## 13. Configure Firewall
177186```bash
178187apt install -y ufw
···44## Prerequisites
55- A VPS with at least 2GB RAM and 20GB disk
66- A domain name pointing to your server's IP
77+- A **wildcard TLS certificate** for `*.pds.example.com` (user handles are served as subdomains)
78- Root access (or doas configured)
89## Why nginx over relayd?
910OpenBSD'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.
···8081mc mb local/pds-blobs
8182```
8283## 5. Install redis
8383-OpenBSD has redis in ports (valkey may not be available yet):
8484+OpenBSD has redis in ports (valkey not available yet):
8485```sh
8586pkg_add redis
8687rcctl enable redis
···194195mkdir -p /var/www/acme
195196rcctl enable nginx
196197```
197197-## 12. Obtain SSL Certificate with acme-client
198198-OpenBSD's native acme-client works well:
198198+## 12. Obtain Wildcard SSL Certificate
199199+User handles are served as subdomains (e.g., `alice.pds.example.com`), so you need a wildcard certificate.
200200+201201+OpenBSD's native `acme-client` only supports HTTP-01 validation, which can't issue wildcard certs. You have a few options:
202202+203203+**Option A: Use certbot with DNS validation (recommended)**
199204```sh
200200-cat >> /etc/acme-client.conf << 'EOF'
201201-authority letsencrypt {
202202- api url "https://acme-v02.api.letsencrypt.org/directory"
203203- account key "/etc/acme/letsencrypt-privkey.pem"
204204-}
205205-domain pds.example.com {
206206- domain key "/etc/ssl/private/pds.example.com.key"
207207- domain full chain certificate "/etc/ssl/pds.example.com.fullchain.pem"
208208- sign with letsencrypt
209209-}
210210-EOF
211211-mkdir -p /etc/acme
212212-rcctl start nginx
213213-acme-client -v pds.example.com
214214-rcctl restart nginx
205205+pkg_add certbot
206206+certbot certonly --manual --preferred-challenges dns \
207207+ -d pds.example.com -d '*.pds.example.com'
215208```
216216-Set up auto-renewal in root's crontab:
209209+Follow the prompts to add TXT records to your DNS. Then update nginx.conf to point to the certbot certs.
210210+211211+**Option B: Use a managed DNS provider with API**
212212+If your DNS provider has a certbot plugin, you can automate renewal.
213213+214214+**Option C: Use acme.sh**
215215+[acme.sh](https://github.com/acmesh-official/acme.sh) supports many DNS providers for automated wildcard cert renewal.
216216+217217+After obtaining the cert, update nginx to use it and restart:
217218```sh
218218-crontab -e
219219-```
220220-Add:
221221-```
222222-0 0 * * * acme-client pds.example.com && rcctl reload nginx
219219+rcctl restart nginx
223220```
224221## 13. Configure Packet Filter (pf)
225222```sh
226223cat >> /etc/pf.conf << 'EOF'
227227-# BSPDS rules
228224pass in on egress proto tcp from any to any port { 22, 80, 443 }
229225EOF
230226pfctl -f /etc/pf.conf