Deployment
Target: single VPS, Docker Compose, Caddy for TLS. Managed databases (RDS, Cloud SQL, Supabase) cannot load custom Rust extensions and are out of scope.
Minimum requirements
- VPS with ≥1 vCPU / 2 GiB RAM (t3.small, Hetzner CX22, DO $12 droplet, etc.)
- Docker + Compose plugin
- DNS A record pointing at the VPS
- Ports 80/443 open
The stack (two containers)
Use (or adapt) the docker-compose.yml and Caddyfile that pg-web init (or this docs site) provides.
services:
postgres:
image: pgweb/postgres:latest
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: app
volumes:
- pgdata:/var/lib/postgresql/data
# caddy: (uncomment for prod)
# image: caddy:2
# ports: ["80:80", "443:443"]
# volumes: ["./Caddyfile:/etc/caddy/Caddyfile", ...]
First production deploy (summary)
- SSH to VPS, clone your app repo (or copy compose + Caddyfile).
- Create a
.envwith a strongPOSTGRES_PASSWORD. docker compose up -d.- From your laptop, open a background SSH tunnel:
ssh -L 5432:localhost:5432 user@vps. pg-web migrate apply --url "postgres://postgres:pass@localhost:5432/app"pg-web push --url "..."(orpg-web push --with-migrate).- Visit
https://yourdomain. Caddy handled Let's Encrypt.
Updating the site / app
Push new code with pg-web push --with-migrate (or the in-image CLI via docker compose exec postgres pg-web push once you have the repo on the VPS).
Zero-downtime for code changes — routes/templates/handlers are swapped in a single transaction inside Postgres.
Upgrading the pg-web image itself
On the VPS: docker compose pull && docker compose up -d, then ALTER EXTENSION pg_web_ext UPDATE; inside the container.
Full recipe, security checklist, CI example, and backup story: docs/DEPLOYMENT.md in the repo.
This docs site deploys exactly the same way. See
site/docker-compose.yml + site/Caddyfile and the README.md in site/ for the exact commands used to keep https://pg-web.dev live.