Skip to content

Installation

  • PostgreSQL 17 or newer. PostgreSQL 17 and 18 are tested; pgvis refuses to start against an older server. No libpq or other system Postgres library is needed — the driver is pure Rust.
  • Or SQLite — bundled into the binary, no system dependency.

Requires a current stable Rust toolchain (edition 2024; the repository’s rust-toolchain.toml pins the stable channel).

Terminal window
# Clone the repository
git clone https://github.com/pgvis/pgvis.git
cd pgvis
# Build release binary (includes Postgres + SQLite + MCP support)
cargo build --release --bin pgvis
# The binary is at ./target/release/pgvis
./target/release/pgvis --version

The pgvis binary always includes the PostgreSQL and SQLite backends. It has one feature:

FeatureDefaultDescription
mcp✓The pgvis mcp subcommand (stdio) and serve --mcp-http (Streamable HTTP)

Backend selection is a feature of the pgvis-lib crate, for embedding pgvis in your own application:

pgvis-lib featureDefaultDescription
postgres✓PostgreSQL backend (pure Rust driver, rustls for TLS)
sqlite✓SQLite backend (bundled, no system dependency)
mcp✓MCP server support (stdio + Streamable HTTP)

For example, a Postgres-only embedding:

pgvis-lib = { git = "https://github.com/pgvis/pgvis", default-features = false, features = ["postgres"] }

If you have Nix with flakes enabled:

Terminal window
# Build the binary
nix build github:pgvis/pgvis
# Run directly without installing (local experiment: no auth)
nix run github:pgvis/pgvis -- --dsn "postgres://localhost/mydb" serve --insecure-no-auth
# Development shell: Rust toolchain, PostgreSQL, pgcli,
# PostgREST, cargo-nextest, Node.js for the website
nix develop

The flake provides a complete development environment with all system dependencies.

Terminal window
docker run -e PGVIS_DSN="postgres://host.docker.internal/mydb" \
-e PGVIS_ANON_ROLE="web_anon" \
-p 3000:3000 \
ghcr.io/pgvis/pgvis serve --bind 0.0.0.0:3000
Terminal window
# Check version
pgvis --version
# Show help
pgvis --help
# Test against a database
pgvis --dsn "postgres://localhost/mydb" inspect | head -20
pgvis [OPTIONS] [COMMAND]
Options:
-d, --dsn <DSN> Database connection string [env: PGVIS_DSN]
-c, --config <FILE> Path to TOML config file [env: PGVIS_CONFIG]
-h, --help Print help
-V, --version Print version
Commands:
serve Start the HTTP server (REST + optional MCP)
mcp Run an MCP server over stdio
openapi Print the OpenAPI 3.0 document and exit
inspect Dump the introspected schema cache as JSON

Global options go before the command: pgvis --dsn … serve, not pgvis serve --dsn ….

pgvis serve [OPTIONS]
Options:
-b, --bind <ADDR> Bind address [default: 127.0.0.1:3000] [env: PGVIS_BIND]
--insecure-no-auth Serve without jwt_secret or anon_role (every request
runs as the DSN's role) [env: PGVIS_INSECURE_NO_AUTH]
-s, --schema <NAME> Schemas to expose (repeatable or comma-separated)
[default: public] [env: PGVIS_SCHEMAS]
--mcp-http Also serve MCP at /mcp endpoint via Streamable HTTP
--replica-dsn <DSN> Read replica DSNs (repeatable or comma-separated)
[env: PGVIS_REPLICA_DSNS]
--cache-enabled Enable the in-memory data cache [env: PGVIS_CACHE_ENABLED]
--cache-ttl <SECS> Cache TTL in seconds [env: PGVIS_CACHE_TTL]
--cache-max-entries <N> Maximum cache entries [env: PGVIS_CACHE_MAX_ENTRIES]
--cache-lists Also cache list queries [env: PGVIS_CACHE_LISTS]
--pubsub-enabled Enable pub/sub (LISTEN/NOTIFY) [env: PGVIS_PUBSUB_ENABLED]
--pubsub-channel-prefix <P> Pub/sub channel prefix [env: PGVIS_PUBSUB_CHANNEL_PREFIX]

serve refuses to start when neither jwt_secret nor anon_role is configured unless --insecure-no-auth is passed. It reloads the schema cache on SIGUSR1 (or NOTIFY pgrst, 'reload schema') and shuts down gracefully on SIGTERM/Ctrl-C.

pgvis mcp [OPTIONS]
Options:
-s, --schema <NAME> Schemas to expose [default: public] [env: PGVIS_SCHEMAS]
--read-only Expose only read tools (no create/update/delete/RPC)

Prints the OpenAPI 3.0 JSON document to stdout and exits. Pipe to a file or tool:

Terminal window
pgvis --dsn "$PGVIS_DSN" openapi > openapi.json
pgvis --dsn "$PGVIS_DSN" openapi | jq '.paths | keys'

Dumps the introspected schema cache as JSON — useful for debugging what pgvis sees:

Terminal window
pgvis --dsn "$PGVIS_DSN" inspect | jq '.tables | length'
pgvis --dsn "$PGVIS_DSN" inspect | jq '.tables[0].columns[].name'

pgvis auto-detects the backend from the connection string:

PatternBackend
postgres://... or postgresql://...PostgreSQL
sqlite:./path.db or sqlite::memory:SQLite
file:pathSQLite
*.db, *.sqlite, *.sqlite3SQLite
Anything elsePostgreSQL (default)
Terminal window
# Full URI
pgvis --dsn "postgres://user:password@localhost:5432/mydb" serve
# Unix socket
pgvis --dsn "postgres:///mydb?host=/var/run/postgresql" serve
# TLS required (certificate verified against the system trust store)
pgvis --dsn "postgres://user:password@db.example.com/mydb?sslmode=require" serve

TLS follows the DSN’s sslmode: disable, prefer (the default — TLS whenever the server offers it), or require. The server certificate is always verified against the platform trust store; set SSL_CERT_FILE to a CA bundle if your server’s CA isn’t in it. With prefer, a certificate that fails verification is a connection error — pgvis does not fall back to plaintext. verify-ca/verify-full and libpq’s sslrootcert are not supported. The password is redacted in pgvis’s logs.

Terminal window
# File path
pgvis --dsn "sqlite:./data/app.db" serve --schema main --insecure-no-auth
# In-memory (for testing)
pgvis --dsn "sqlite::memory:" serve --schema main --insecure-no-auth
# Bare file path (auto-detected by extension)
pgvis --dsn "./mydata.sqlite3" serve --schema main --insecure-no-auth

SQLite tables live in the main schema, so pass --schema main: with the default public, the OpenAPI document and MCP tool list come out empty.