Architecture
How it works
The web server is not next to the database. It lives inside it.
The idea
A typical stack runs an app server whose main job is moving data between Postgres and a template engine. pg-web deletes that tier: a Rust extension (built with pgrx) starts HTTP listeners as Postgres background workers. Requests are handled by SQL functions in the same process tree that owns the data.
Browser
│
▼
Caddy (TLS termination — the one thing kept outside)
│
▼
PostgreSQL
├─ HTTP background workers (pg_web_ext, Rust/pgrx)
├─ Your SQL handlers (called via SPI — no TCP hop)
└─ Your data
The request lifecycle
- A worker accepts the request and opens one SPI transaction.
- The router matches the path against
pgweb.routes(static and[id]-capture routes). - Your handler function runs:
(req json) → json.reqcarries body, query, method, path. - If the route has a template, Tera renders the JSON into HTML; otherwise the body returns as-is.
- Clean response → commit. Any error → rollback, and an error page. One request, one transaction — always.
Concurrency without threads
Each worker is single-threaded (SQL execution must stay on its own
backend), so pg-web runs K workers behind SO_REUSEPORT and
lets the kernel balance connections. A slow handler stalls one worker,
not the site: in our head-of-line-blocking benchmark, fast requests
behind a deliberately slow one went from p99 ≈ 217 ms with one worker to
≈ 2 ms with four. The harness that produced those numbers lives in the
repo (bench/) and runs as a regression gate.
How your files become an app
The extension never touches the filesystem. The pg-web CLI
reads your project — pages/, components/,
public/, migrations/ — and upserts it into
framework tables. The live server sees new rows on the next lookup, so a
push deploys in seconds with no restart. You never compile Rust.
Honest non-goals
- Not an ORM. SQL is the interface, on purpose.
- Not a SPA framework. Hypermedia first; HTMX for interactivity.
- Not for managed Postgres that blocks custom extensions (RDS, Cloud SQL…). pg-web ships its own image.
- Not a TLS terminator. Caddy (or any proxy) fronts it.
- Not finished. Phase 1 (synchronous core) is shipped; auth, jobs, and realtime are staged next.