Skip to main content

Production Deployment

Deploy Pry behind a reverse proxy with HTTPS, secrets in a manager (gopass), and monitoring. This page is a deployment guide — the operational runbook lives in the DEPLOYMENT.md of the pryscraper repo.

Architecture

Internet → Cloudflare (optional) → nginx (443) → pry (127.0.0.1:8005)
→ flaresolverr (127.0.0.1:8192, internal only)
LayerChoice
ContainerDocker (docker-compose.yml)
Orchestrationdocker compose
Reverse proxynginx
Auto-deployForgejo webhook → deploy script
Secretsgopass (or 1Password/vault)

Reverse proxy (nginx)

All host ports in the compose file bind to 127.0.0.1 — put nginx in front for external access:

server {
listen 443 ssl http2;
server_name pry.example.com;

ssl_certificate /etc/letsencrypt/live/pry.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/pry.example.com/privkey.pem;

location / {
proxy_pass http://127.0.0.1:8005;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Connection "";
# WebSocket streaming (job progress) needs upgrade headers:
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 300s;
}
}

HTTPS

  • Issue certificates with acme.sh or certbot (Let's Encrypt).
  • Redirect all HTTP → HTTPS.
  • Optional: put Cloudflare in front (DDoS protection, CDN). Note that Pry's own auth already fail-closes: a keyless instance rejects every non-loopback source with 401, so double-check the proxy X-Forwarded-For handling when combining proxies.

Secrets (gopass)

No secrets live in .env in production. Seed the secrets manager:

gopass insert -m pry/jwt_secret
gopass insert -m pry/api_key
gopass insert -m pry/x402_wallet
gopass insert -m pry/x402_facilitator

The default PRY_SECRET_BACKEND=gopass resolves every secret from the store under pry/<name> (see Configuration). Docker loads non-secret env via env_file: .env / --env-file; secrets stay in gopass.

Rotation policy (from SECURITY.md): API tokens quarterly, DB passwords quarterly, signing keys on personnel change. Any leak triggers rotation within 1 hour.

Hardening checklist

  • Set a strong PRY_API_KEY (fail-closed auth for every request)
  • Bind API to 127.0.0.1 and proxy through nginx (never expose 8002/8005 directly)
  • Keep all compose host ports loopback-only
  • Secrets in gopass — never in .env in production
  • Enable the /metrics endpoint and scrape it with Prometheus
  • Set resource limits (compose defaults: 2 GB / 2 CPU for pry)
  • Keep dependencies updated (renovate bot; pip-audit weekly)
  • Restrict admin/configuration endpoints: POST /v1/config, /v1/config/profile/tor, /v1/proxy/configure require the key
  • Firewall: only nginx ports public; everything else localhost

Schema migrations (Alembic)

The container entrypoint runs migrations before starting uvicorn:

  1. alembic stamp head — idempotent; marks the DB up-to-date without running migrations (preserves existing data when schema already matches).
  2. On stamp failure: alembic upgrade head — applies pending migrations.
  3. exec "$@" — chains to uvicorn.

PRY_SKIP_MIGRATIONS=1 skips the migration step (read-only debug runs).

docker exec pry alembic current
docker exec pry alembic upgrade head # apply pending

Monitoring

EndpointPurpose
GET /healthHealth + cache stats + active sessions
GET /liveLiveness probe
GET /readyReadiness probe
GET /metricsPrometheus metrics
WebSocket streamingReal-time job progress

Stack: Prometheus → Grafana dashboards; Loki for log aggregation.

Deploy / rollback

# Manual deploy
git pull origin main
docker build -t pryscraper:latest .
docker stop pryscraper && docker rm pryscraper
docker run -d --name pryscraper --restart unless-stopped \
--network host -p 8005:8005 pryscraper:latest

# Health check
curl -fsS http://localhost:8005/health

# Rollback
git checkout <last-good-commit>
docker build -t pryscraper:latest .
docker restart pryscraper

# Logs
docker logs pryscraper --tail 100 -f

On merge to main, a Forgejo webhook can trigger the deploy script: pull → rebuild → swap container → health check → rollback on failure.

Backup

  • Code: Forgejo (source of truth)
  • Data: daily DB snapshot + PRY_DATA_DIR (quality, monitors, sessions, vault)
  • Config: secrets manager

Next steps