# Google Source: https://docs.pgconsole.com/authentication/google Allow users to sign in with their Google accounts. ## Prerequisites * A Google Cloud project * [external\_url](/configuration/config#general) ## Setup ### 1. Create OAuth Credentials 1. Go to the [Google Cloud Console](https://console.cloud.google.com/) 2. Select or create a project 3. Navigate to **APIs & Services** > **Credentials** 4. Click **Create Credentials** > **OAuth client ID** 5. If prompted, configure the OAuth consent screen first: * Choose **Internal** to restrict sign-in to your Google Workspace organization only * Choose **External** to allow any Google account to sign in 6. Select **Web application** as the application type 7. Add authorized redirect URI: ``` <>/api/auth/google/callback ``` 8. Click **Create** 9. Copy the **Client ID** and **Client Secret** ### 2. Configure pgconsole Add the Google provider to your `pgconsole.toml`: ```toml pgconsole.toml theme={null} [general] external_url = "https://pgconsole.example.com" [auth] # openssl rand -hex 32 jwt_secret = "your-secret-key-at-least-32-characters-long" signin_expiry = "7d" [[auth.providers]] type = "google" client_id = "your-client-id.apps.googleusercontent.com" client_secret = "your-client-secret" # Google email is used as the user identifier in groups and IAM rules [[groups]] id = "dev-team" name = "Development Team" members = ["alice@example.com", "bob@example.com"] [[iam]] connection = "production" permissions = ["read"] members = ["user:carol@example.com", "group:dev-team"] ``` See [OAuth Providers](/configuration/config#oauth-providers) in the configuration reference for all provider fields. ### 3. Restart pgconsole Restart the application for changes to take effect. A "Sign in with Google" button will appear on the login page. # Keycloak Source: https://docs.pgconsole.com/authentication/keycloak Allow users to sign in with Keycloak, an open source identity and access management solution. ## Prerequisites * A running Keycloak instance * Admin access to create clients * [external\_url](/configuration/config#general) ## Setup ### 1. Create a Client in Keycloak 1. Open your Keycloak admin console 2. Select the realm you want to use (or create a new one) 3. Go to **Clients** > **Create client** 4. Configure the client: * **Client type**: `openid-connect` * **Client ID**: `pgconsole` (or your preferred name) 5. Click **Next** 6. Enable **Client authentication** 7. Click **Next** 8. Add valid redirect URI: ``` <>/api/auth/keycloak/callback ``` 9. Click **Save** 10. Go to the **Client scopes** tab and verify `email` and `profile` are listed under **Assigned default client scopes** (Keycloak assigns these by default for OpenID Connect clients) ### 2. Get Client Credentials 1. Go to the **Credentials** tab 2. Copy the **Client Secret** ### 3. Get Issuer URL The issuer URL follows this pattern: ``` https://keycloak.example.com/realms/your-realm ``` ### 4. Configure pgconsole Add the Keycloak provider to your `pgconsole.toml`: ```toml pgconsole.toml theme={null} [general] external_url = "https://pgconsole.example.com" [auth] # openssl rand -hex 32 jwt_secret = "your-secret-key-at-least-32-characters-long" signin_expiry = "7d" [[auth.providers]] type = "keycloak" client_id = "pgconsole" client_secret = "your-keycloak-client-secret" issuer_url = "https://keycloak.example.com/realms/your-realm" # Keycloak email is used as the user identifier in groups and IAM rules [[groups]] id = "dev-team" name = "Development Team" members = ["alice@example.com", "bob@example.com"] [[iam]] connection = "production" permissions = ["read"] members = ["user:carol@example.com", "group:dev-team"] ``` See [OAuth Providers](/configuration/config#oauth-providers) in the configuration reference for all provider fields. ### 5. Restart pgconsole Restart the application for changes to take effect. A "Sign in with Keycloak" button will appear on the login page. # Okta Source: https://docs.pgconsole.com/authentication/okta Allow users to sign in with Okta, a cloud identity and access management platform. ## Prerequisites * An Okta organization (e.g. `https://your-org.okta.com`) * Admin access to create applications * [external\_url](/configuration/config#general) ## Setup ### 1. Create an Application in Okta 1. Open your Okta admin console 2. Go to **Applications** > **Create App Integration** 3. Select **OIDC - OpenID Connect** as the sign-in method 4. Select **Web Application** as the application type 5. Click **Next** 6. Configure the application: * **App integration name**: `pgconsole` (or your preferred name) * **Grant type**: `Authorization Code` (default) * **Sign-in redirect URIs**: ``` <>/api/auth/okta/callback ``` * **Sign-out redirect URIs**: leave blank 7. Under **Assignments**, control who can access pgconsole: * **Limit access to selected groups** - Only assigned users/groups can sign in. Select the groups that should have access. * **Skip group assignment for now** - Enables [Federation Broker Mode](https://support.okta.com/help/s/article/Federation-Broker-Mode). When no users or groups are assigned, Okta allows any user who can authenticate (including users from federated Identity Providers) to access the application. 8. Click **Save** ### 2. Get Client Credentials 1. On the application's **General** tab, find the **Client Credentials** section 2. Copy the **Client ID** and **Client secret** ### 3. Get Issuer URL Most setups use the default custom authorization server: ``` https://your-org.okta.com/oauth2/default ``` Other options: * **Org authorization server**: `https://your-org.okta.com` * **Named custom authorization server**: `https://your-org.okta.com/oauth2/` ### 4. Configure pgconsole Add the Okta provider to your `pgconsole.toml`: ```toml pgconsole.toml theme={null} [general] external_url = "https://pgconsole.example.com" [auth] # openssl rand -hex 32 jwt_secret = "your-secret-key-at-least-32-characters-long" signin_expiry = "7d" [[auth.providers]] type = "okta" client_id = "0oaXXXXXXXXXXXXXX" client_secret = "your-okta-client-secret" issuer_url = "https://your-org.okta.com/oauth2/default" # Okta email is used as the user identifier in groups and IAM rules [[groups]] id = "dev-team" name = "Development Team" members = ["alice@example.com", "bob@example.com"] [[iam]] connection = "production" permissions = ["read"] members = ["user:carol@example.com", "group:dev-team"] ``` See [OAuth Providers](/configuration/config#oauth-providers) in the configuration reference for all provider fields. ### 5. Restart pgconsole Restart the application for changes to take effect. A "Continue with Okta" button will appear on the login page. ## Troubleshooting ### You are not allowed to access this app This error appears when a user tries to sign in. It comes from Okta before any token is issued. Two layers of Okta policies can cause it: **Application Authentication Policy** — Controls who can sign in to the application. 1. Go to **Applications** > select your app > **Sign On** tab 2. Click the linked authentication policy name 3. Check the policy rules — the user must match at least one rule that allows access 4. To grant access, add the user/group to an existing **Allow** rule, or create a new rule with the action set to **Allowed** **Authorization Server Access Policy** — Controls which clients can request tokens. This applies when using a custom authorization server (e.g. `/oauth2/default`). The default custom authorization server ships with a default policy and rule that allows all clients, the Authorization Code grant, and all scopes. No changes are needed unless your organization has modified these defaults. If the defaults have been restricted: 1. Go to **Security** > **API** > select your authorization server > **Access Policies** tab 2. Verify at least one rule allows your pgconsole app with the `Authorization Code` grant type and `openid`, `email`, `profile` scopes # Overview Source: https://docs.pgconsole.com/authentication/overview pgconsole supports multiple authentication methods to secure access to your database console. ## Available Methods | Method | Description | Best For | | ---------------------------------------------------------------------------------------------------------- | ----------------------------- | ---------------------------------------------- | | Email/Password | Built-in local authentication | Simple setups, small teams | | SSO ([Google](/authentication/google), [Okta](/authentication/okta), [Keycloak](/authentication/keycloak)) | OAuth providers | Organizations with existing identity providers | ## Configuration Authentication is configured in your `pgconsole.toml` file. Add an `[auth]` section and `[[users]]` entries: ```toml pgconsole.toml theme={null} [auth] # openssl rand -hex 32 jwt_secret = "your-secret-key-at-least-32-characters-long" signin_expiry = "7d" # h=hours, d=days, w=weeks [[users]] email = "admin@example.com" password = "your-secure-password" [[users]] email = "developer@example.com" password = "another-password" [[users]] email = "carol@example.com" # SSO-only, no password ``` Users with a `password` field can sign in with email/password. Users without a `password` field are SSO-only — they must sign in via an OAuth provider. See [Authentication](/configuration/config#authentication) and [Users](/configuration/config#users) in the configuration reference for all available fields. ## Disabling Authentication To run pgconsole without authentication (not recommended for production), simply omit the `[auth]` section from your configuration file. # pgconsole.toml Reference Source: https://docs.pgconsole.com/configuration/config pgconsole uses a TOML configuration file for all settings. Pass the config file with the `--config` flag. Without `--config`, pgconsole starts in [demo mode](/getting-started/quickstart#demo-mode). ```bash theme={null} docker run -p 9876:9876 -v /path/to/pgconsole.toml:/etc/pgconsole.toml pgplex/pgconsole ``` ```bash theme={null} npx @pgplex/pgconsole --config /path/to/pgconsole.toml ``` ```bash theme={null} pgconsole --config /path/to/pgconsole.toml ``` ## Complete Example ```toml pgconsole.toml theme={null} [general] external_url = "https://pgconsole.example.com" [general.banner] text = "System maintenance scheduled for Sunday 2am UTC" link = "https://status.example.com" color = "#7c3aed" [general.audit] retention_days = 30 [branding] logo = "https://example.com/your-logo.svg" logo_link = "https://internal.example.com" [[labels]] id = "prod" name = "Production" color = "#ef4444" [[labels]] id = "staging" name = "Staging" color = "#f59e0b" [[connections]] id = "local" name = "Local Dev" host = "localhost" port = 5432 database = "postgres" username = "postgres" password = "postgres" ssl_mode = "prefer" [[connections]] id = "production" name = "Production DB" host = "db.example.com" port = 5432 database = "app" username = "readonly" password = "secret" ssl_mode = "verify-full" ssl_ca = "/path/to/ca.crt" ssl_cert = "/path/to/client.crt" ssl_key = "/path/to/client.key" labels = ["prod"] lock_timeout = "5s" statement_timeout = "30s" [auth] jwt_secret = "your-secret-key-at-least-32-characters-long" signin_expiry = "7d" [[auth.providers]] type = "google" client_id = "your-client-id" client_secret = "your-client-secret" [[auth.providers]] type = "keycloak" client_id = "pgconsole" client_secret = "your-client-secret" issuer_url = "https://keycloak.example.com/realms/your-realm" [[auth.providers]] type = "okta" client_id = "0oaXXXXXXXXXXXXXX" client_secret = "your-okta-client-secret" issuer_url = "https://your-org.okta.com/oauth2/default" [[users]] email = "admin@example.com" password = "your-password" owner = true [[users]] email = "alice@example.com" [[groups]] id = "dev-team" name = "Development Team" members = ["admin@example.com", "alice@example.com"] [[groups]] id = "dba" name = "Database Administrators" members = ["admin@example.com"] [[iam]] connection = "*" permissions = ["read"] members = ["*"] [[iam]] connection = "local" permissions = ["read", "write", "ddl", "admin"] members = ["user:admin@example.com", "group:dba"] [[ai.providers]] id = "gpt4" name = "GPT-4o" vendor = "openai" model = "gpt-4o" api_key = "sk-..." [[agents]] id = "alice-claude" token = "pgc_mcp_xxxxxxxxxxxxxxxx" on_behalf_of = "alice@example.com" permissions = ["read"] ``` ## General | Field | Description | Required | | -------------- | ----------------------------------------------------------------------------------------------- | ---------------- | | `external_url` | Public URL of the application. See [Configure External Access](/configuration/external-access). | Required for SSO | ```toml pgconsole.toml theme={null} [general] external_url = "https://pgconsole.example.com" ``` ### Announcement Banner Optional banner displayed at the top of the page. The banner cannot be dismissed by users. | Field | Description | Required | | ------- | ------------------------------------------------------ | -------- | | `text` | Banner message text | Yes | | `link` | URL that makes the banner clickable (opens in new tab) | | | `color` | Hex color code for the banner background | | ```toml pgconsole.toml theme={null} [general.banner] text = "System maintenance scheduled for Sunday 2am UTC" link = "https://status.example.com" color = "#7c3aed" ``` ## Audit Log Controls in-memory retention for the [Audit Log](/features/audit-log) page. Audit events are always emitted to stdout; this section only controls how long entries stay available in the running pgconsole process. | Field | Description | Required | | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------- | -------- | | `retention_days` | Positive integer number of days to keep audit entries in memory. When omitted, entries are retained indefinitely until server restart. | | ```toml pgconsole.toml theme={null} [general.audit] retention_days = 30 ``` ## Branding Replace the pgconsole logo with your own. | Field | Description | Required | | ----------- | -------------------------------------------------------- | -------- | | `logo` | URL to your logo image | Yes | | `logo_link` | Where the logo links to (absolute path or `http(s)` URL) | | When `logo_link` is omitted, the logo links to `/`. ```toml pgconsole.toml theme={null} [branding] logo = "https://example.com/your-logo.svg" logo_link = "https://internal.example.com" ``` ## Labels Labels for tagging connections (e.g. Production, Staging). Referenced by the `labels` field in `[[connections]]`. Repeat for multiple labels. | Field | Description | Required | | ------- | ----------------- | -------- | | `id` | Unique identifier | Yes | | `name` | Display name | Yes | | `color` | Hex color code | Yes | ```toml pgconsole.toml theme={null} [[labels]] id = "prod" name = "Production" color = "#ef4444" ``` ## Connections Database connections. Repeat for multiple connections. If connecting to a database on your host machine from Docker, use `host.docker.internal` instead of `localhost`. | Field | Description | Required | Default | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------- | -------- | -------------- | | `id` | Unique identifier | Yes | | | `name` | Display name | Yes | | | `host` | PostgreSQL host | Yes | | | `port` | PostgreSQL port | | `5432` | | `database` | Database name | Yes | | | `username` | Database user | Yes | | | `password` | Database password | | | | `ssl_mode` | `disable`, `prefer`, `require`, or `verify-full` | | `prefer` | | `ssl_ca` | Path to CA certificate | | | | `ssl_cert` | Path to client certificate | | | | `ssl_key` | Path to client private key | | | | `labels` | Array of label IDs | | `[]` | | `color` | Hex color (e.g. `#dc2626`) that tints the header to flag the environment (e.g. red for production) | | | | `lock_timeout` | [`lock_timeout`](https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-LOCK-TIMEOUT), e.g. `"5s"` | | System default | | `statement_timeout` | [`statement_timeout`](https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-STATEMENT-TIMEOUT), e.g. `"30s"` | | System default | | `lazy` | Skip connection test on startup | | `false` | ```toml pgconsole.toml theme={null} [[connections]] id = "production" name = "Production DB" host = "db.example.com" port = 5432 database = "app" username = "readonly" password = "secret" ssl_mode = "verify-full" ssl_ca = "/path/to/ca.crt" ssl_cert = "/path/to/client.crt" ssl_key = "/path/to/client.key" labels = ["prod"] color = "#dc2626" lock_timeout = "5s" statement_timeout = "30s" ``` ## Authentication To run without authentication, omit the `[auth]` section entirely. | Field | Description | Required | | --------------- | ---------------------------------------- | -------- | | `jwt_secret` | Secret key for JWT tokens (min 32 chars) | Yes | | `signin_expiry` | Session duration (`h`/`d`/`w`) | Yes | ```toml pgconsole.toml theme={null} [auth] jwt_secret = "your-secret-key-at-least-32-characters-long" signin_expiry = "7d" ``` ### OAuth Providers OAuth providers are configured as an array of `[[auth.providers]]` entries. Each entry requires a `type` field. Repeat for multiple providers. | Field | Description | Required | | --------------- | ----------------------------------------------- | -------- | | `type` | Provider type: `google`, `keycloak`, or `okta` | Yes | | `client_id` | OAuth client ID | Yes | | `client_secret` | OAuth client secret | Yes | | `issuer_url` | Issuer URL (required for `keycloak` and `okta`) | | ```toml pgconsole.toml theme={null} # Google [[auth.providers]] type = "google" client_id = "your-client-id.apps.googleusercontent.com" client_secret = "your-client-secret" # Keycloak [[auth.providers]] type = "keycloak" client_id = "pgconsole" client_secret = "your-client-secret" issuer_url = "https://keycloak.example.com/realms/your-realm" # Okta [[auth.providers]] type = "okta" client_id = "0oaXXXXXXXXXXXXXX" client_secret = "your-okta-client-secret" issuer_url = "https://your-org.okta.com/oauth2/default" ``` ## Users User entries. Repeat for multiple users. Users with a `password` can sign in with basic auth. Users without a `password` are SSO-only. | Field | Description | Required | | ---------- | ------------------------------------------------------ | -------- | | `email` | User email or identifier | Yes | | `password` | Login password (omit for SSO-only users) | | | `owner` | Marks the user as an owner (shown with an Owner badge) | | ```toml pgconsole.toml theme={null} [[users]] email = "admin@example.com" password = "your-password" owner = true [[users]] email = "alice@example.com" # SSO-only user - no password ``` ### Owner Role Users with `owner = true` are marked with an Owner badge in the UI. If no user has `owner = true`, the first user entry automatically becomes the owner. ## Groups User groups for organizing users. Repeat for multiple groups. | Field | Description | Required | | --------- | -------------------- | -------- | | `id` | Unique identifier | Yes | | `name` | Display name | Yes | | `members` | Array of user emails | Yes | Members are user emails matching `[[users]]` entries. ```toml pgconsole.toml theme={null} [[groups]] id = "dev-team" name = "Development Team" members = ["admin@example.com", "alice@example.com"] ``` ## Access Control (IAM) Rules for controlling access to connections. IAM is opt-in: with no `[[iam]]` rules defined, all authenticated users have full access, and enforcement begins once you define the first rule. See [Database Access Control](/features/database-access-control) for a full guide on permissions, patterns, and examples. | Field | Description | Required | | ------------- | -------------------------------------------------------------------------------------- | -------- | | `connection` | Connection ID or `*` for all | Yes | | `permissions` | Array: `read`, `write`, `ddl`, `admin`, `explain`, `execute`, `export`, or `*` for all | Yes | | `members` | Array: `user:`, `group:`, or `*` for all users | Yes | ```toml pgconsole.toml theme={null} [[iam]] connection = "*" permissions = ["read"] members = ["*"] [[iam]] connection = "production" permissions = ["read", "write"] members = ["user:admin@example.com", "group:dev-team"] ``` ### Validation IAM rules are validated when the configuration is loaded: * `connection` must be `*` or reference a valid connection ID * `permissions` must only contain valid values: `read`, `write`, `ddl`, `admin`, `explain`, `execute`, `export`, or `*` * `members` must use valid formats: `user:`, `group:`, or `*` * `group:` must reference a defined group Invalid rules will cause the server to fail at startup with an error message. ## AI Providers Configure providers for the [AI Assistant](/features/ai-assistant). Repeat for multiple providers. | Field | Description | Required | | ---------- | ---------------------------------------------------------------------------- | -------------------------------------- | | `id` | Unique identifier | Yes | | `name` | Display name (defaults to `id`) | | | `vendor` | AI vendor: `openai`, `anthropic`, `google`, `openai-compatible` | Yes | | `model` | Model identifier | Yes | | `api_key` | API key for the vendor | Yes (optional for `openai-compatible`) | | `base_url` | Base URL of an OpenAI-compatible API (e.g. `https://api.groq.com/openai/v1`) | Required for `openai-compatible` | Use `vendor = "openai-compatible"` with `base_url` for any provider that implements the OpenAI API (Groq, OpenRouter, Together, Ollama, vLLM, LiteLLM, and most self-hosted gateways). `api_key` may be omitted for local providers that run without authentication (e.g. Ollama, self-hosted vLLM). ```toml pgconsole.toml theme={null} [[ai.providers]] id = "gpt4" name = "GPT-4o" vendor = "openai" model = "gpt-4o" api_key = "sk-..." [[ai.providers]] id = "groq" name = "Llama 3.3 70B" vendor = "openai-compatible" model = "llama-3.3-70b-versatile" api_key = "gsk_..." base_url = "https://api.groq.com/openai/v1" ``` ## Agents Non-human principals that authenticate to the [MCP Server](/features/mcp-server) with a bearer token. An agent is **not** a [user](#users) — it can't log into the UI. Repeat for multiple agents. | Field | Description | Required | | -------------- | ---------------------------------------------------------------------------------------- | -------- | | `id` | Unique identifier, referenced in IAM rules as `agent:` | Yes | | `token` | Secret bearer token the agent sends as `Authorization: Bearer ` (globally unique) | Yes | | `name` | Display name (defaults to `id`) | | | `on_behalf_of` | A [`[[users]]`](#users) email. Its presence makes the agent **delegated** — see below | | | `permissions` | Cap intersected with the user's grant; delegated agents only | | | `connections` | Connection IDs the agent may touch; delegated agents only | | There are two kinds of agent: * **Pure agent** (no `on_behalf_of`) — a standalone service account, e.g. a CI bot. Authorize it with normal [IAM rules](#access-control-iam) whose `members` include `agent:`. It is granted *only* what those rules say (`*`/`group:`/`user:` rules never apply to agents). * **Delegated agent** (`on_behalf_of` set) — acts on behalf of a user and **inherits that user's permissions**, optionally narrowed by the `permissions`/`connections` caps. It can never exceed the user, and automatically loses access when the user does. An agent only sees the MCP tools its effective permissions unlock — a `read`-only agent sees the discovery and `query` tools; `write_data`/`run_ddl` appear only with those permissions. Give each agent the narrowest grant it needs. ```toml pgconsole.toml theme={null} # Pure agent — granted by an agent: IAM rule [[agents]] id = "migration-bot" name = "Nightly Migration Bot" token = "pgc_mcp_xxxxxxxxxxxxxxxx" [[iam]] connection = "staging" permissions = ["read", "ddl"] members = ["agent:migration-bot"] # Delegated agent — bounded by alice, capped to read-only on prod [[agents]] id = "alice-claude" name = "Alice's Claude Code" token = "pgc_mcp_yyyyyyyyyyyyyyyy" on_behalf_of = "alice@example.com" permissions = ["read"] connections = ["prod"] ``` # Configure External Access Source: https://docs.pgconsole.com/configuration/external-access pgconsole listens on port `9876` by default (configurable via `--port`). You can access it directly or put it behind a reverse proxy. ## Direct Access Users access pgconsole directly at its host and port. ```mermaid theme={null} flowchart LR Browser --> pgconsole["pgconsole :9876"] ``` No additional configuration is needed. ## Behind a Reverse Proxy A reverse proxy handles TLS termination and exposes pgconsole on a public domain. Set [`external_url`](/configuration/config#general) in `pgconsole.toml` to the public-facing URL so that pgconsole constructs correct OAuth callback URLs. ```toml pgconsole.toml theme={null} [general] external_url = "https://pgconsole.example.com" ``` ```mermaid theme={null} flowchart LR Browser -->|external_url| Proxy -->|internal_url| pgconsole ``` ### Nginx ```nginx theme={null} server { server_name pgconsole.example.com; location / { proxy_pass http://localhost:9876; 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; } } ``` ### Caddy Caddy provides automatic HTTPS with Let's Encrypt. ```caddy theme={null} pgconsole.example.com { reverse_proxy localhost:9876 } ``` ### Kubernetes Ingress Use an Ingress resource to expose pgconsole with TLS termination inside a Kubernetes cluster. ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: pgconsole-ingress annotations: nginx.ingress.kubernetes.io/ssl-redirect: "true" spec: ingressClassName: nginx tls: - hosts: - pgconsole.example.com secretName: pgconsole-tls rules: - host: pgconsole.example.com http: paths: - path: / pathType: Prefix backend: service: name: pgconsole port: number: 9876 ``` The `pgconsole-tls` Secret holds your TLS certificate. If you use [cert-manager](https://cert-manager.io/), add the annotation `cert-manager.io/cluster-issuer` to provision it automatically. ### Kubernetes Gateway API The [Gateway API](https://gateway-api.sigs.k8s.io/) is the successor to Ingress. Define a Gateway for TLS termination and an HTTPRoute for routing: ```yaml theme={null} apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: pgconsole-gateway spec: gatewayClassName: nginx listeners: - name: https protocol: HTTPS port: 443 hostname: pgconsole.example.com tls: mode: Terminate certificateRefs: - name: pgconsole-tls - name: http-redirect protocol: HTTP port: 80 hostname: pgconsole.example.com --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: pgconsole-route spec: parentRefs: - name: pgconsole-gateway hostnames: - pgconsole.example.com rules: - backendRefs: - name: pgconsole port: 9876 ``` ## OAuth Callback When SSO is configured, `external_url` is used to construct the OAuth callback URL. The OAuth provider redirects users back to `external_url` after authentication. ```mermaid theme={null} flowchart LR pgconsole -->|① redirect| OAuthProvider[OAuth Provider] OAuthProvider -->|② external_url callback| pgconsole ``` # Server Flags Source: https://docs.pgconsole.com/configuration/server-flags ## Available Flags | Setting | Flag | Environment Variable | Default | | ----------- | ---------- | -------------------- | --------- | | Port | `--port` | `PORT` | `9876` | | Config file | `--config` | `PGCONSOLE_CONFIG` | Demo mode | Command line flags take precedence over environment variables. ## Examples ### npx ```bash theme={null} npx @pgplex/pgconsole --port 3000 --config /path/to/pgconsole.toml ``` ### Docker ```bash theme={null} # Demo mode (no config needed) docker run -p 9876:9876 pgplex/pgconsole # With config file docker run -p 9876:9876 -v /path/to/pgconsole.toml:/etc/pgconsole.toml pgplex/pgconsole # Custom port docker run -p 3000:3000 -e PORT=3000 -v /path/to/pgconsole.toml:/etc/pgconsole.toml pgplex/pgconsole # Pass config via environment variable (useful for PaaS like Railway where we can't mount file) docker run -p 9876:9876 -e PGCONSOLE_CONFIG="$(cat /path/to/pgconsole.toml)" pgplex/pgconsole ``` `PGCONSOLE_CONFIG` contains the full TOML configuration as a string. When set, pgconsole parses it directly instead of reading from a file. `--config` takes precedence over `PGCONSOLE_CONFIG`. This is useful for PaaS platforms (Railway, Cloudflare, etc.) where you cannot mount files. # AI Assistant Source: https://docs.pgconsole.com/features/ai-assistant pgconsole includes an AI assistant that helps you work with your database. Features include Text-to-SQL generation, query explanation, error fixing, query rewriting, and change risk assessment. ## Configuration API keys are stored in your config file and never sent to the frontend. Add one or more AI providers to your `pgconsole.toml`. See [AI Providers](/configuration/config#ai-providers) for the full field reference. ```toml pgconsole.toml theme={null} [[ai.providers]] id = "gpt4" name = "GPT-4o" vendor = "openai" model = "gpt-4o" api_key = "sk-..." ``` ```toml pgconsole.toml theme={null} [[ai.providers]] id = "claude" name = "Claude Sonnet" vendor = "anthropic" model = "claude-sonnet-4-20250514" api_key = "sk-ant-..." ``` ```toml pgconsole.toml theme={null} [[ai.providers]] id = "gemini" name = "Gemini Pro" vendor = "google" model = "gemini-2.5-pro" api_key = "AIza..." ``` ## Features ### Text-to-SQL Generate SQL queries from natural language descriptions. Open the **Chat** tab in the right panel, select an AI provider, and describe the query you need. Conversations support multi-turn follow-ups. Text-to-SQL conversation ### Fix SQL When the editor detects a syntax error (via inline linting), an AI fix suggestion appears. Clicking it sends the SQL and error message to the AI, which returns a corrected version that replaces the original in the editor. Fix SQL with AI ### Explain SQL Get plain-language explanations of SQL queries. **Usage:** * Right-click a query in the editor and select **Explain with AI** * Or click the **Explain** button in a function/procedure schema tab The explanation opens in the Chat tab, where you can ask follow-up questions about the query. Explain SQL with AI ### Rewrite SQL **Usage:** Right-click a query in the editor and select **Rewrite with AI**. The AI rewrites the query for better performance or readability, using your database schema for context. The rewritten SQL replaces the original in the editor. ### Change Risk Assessment Analyze staged changes for potential risks before execution. In the [staged changes preview](/features/sql-editor#inline-editing), click **Assess Risk** to have the AI review the SQL. The assessment returns a list of findings, each with a severity level (high, moderate, low), category, and description. Risk Assessment ## Schema Context pgconsole only sends schema information to the AI provider. The AI assistant uses your database schema to generate accurate queries. Schema context includes tables, columns, types, constraints, indexes, and comments. Control which schemas are included in the AI context via the database icon in the Chat toolbar: | Mode | Behavior | | ---------------------------- | ------------------------------------------- | | **Current schema** (default) | Only the currently selected schema | | **All schemas** | All non-system schemas | | **Custom** | Cherry-pick specific schemas via checkboxes | # Audit Log Source: https://docs.pgconsole.com/features/audit-log pgconsole emits audit logs as JSON lines to stdout, allowing you to capture and process them with your existing log infrastructure. It also keeps audit entries in memory so they can be inspected from the `/audit-log` page. ## In-App Audit Log The `/audit-log` page has two tabs: * **Connection** — SQL execution and data export entries for the selected connection, newest first. Requires `admin` permission on that connection. * **System** — instance-level events that aren't tied to a connection (`auth.login` / `auth.logout`), including the auth provider and source IP. Only visible to an instance **owner**, since these events span all users. Entries are stored in memory only. They are lost when the server restarts. By default, pgconsole retains entries indefinitely while the process is running, so memory usage grows with audit volume. For high-traffic deployments, set `retention_days` to prune older entries: ```toml pgconsole.toml theme={null} [general.audit] retention_days = 30 ``` See the [configuration reference](/configuration/config#audit-log) for details. ## Events | Event | Description | | ------------- | --------------------------------------- | | `auth.login` | User login attempt (success or failure) | | `auth.logout` | User logout | | `sql.execute` | SQL query execution | | `data.export` | Data exported (e.g., CSV download) | ## Log Format All audit events are JSON objects with a common structure: ```json theme={null} { "type": "audit", "ts": "2024-01-15T10:30:45.123Z", "action": "...", "actor": "alice@example.com" } ``` ### Common Fields | Field | Description | | -------- | ------------------ | | `type` | Always `"audit"` | | `ts` | ISO 8601 timestamp | | `action` | Event type | | `actor` | Username or email | ### auth.login ```json theme={null} { "type": "audit", "ts": "2024-01-15T10:30:45.123Z", "action": "auth.login", "actor": "alice@example.com", "provider": "google", "ip": "192.168.1.100", "success": true, "source": "web" } ``` | Field | Description | | ---------- | --------------------------------------------- | | `provider` | Auth provider (`basic`, `google`, `keycloak`) | | `ip` | Client IP address | | `success` | Whether login succeeded | | `error` | Error message (if failed) | | `source` | Event origin: currently `"web"` | ### auth.logout ```json theme={null} { "type": "audit", "ts": "2024-01-15T10:35:00.000Z", "action": "auth.logout", "actor": "alice@example.com", "source": "web" } ``` | Field | Description | | -------- | ------------------------------- | | `actor` | User who logged out | | `source` | Event origin: currently `"web"` | ### sql.execute ```json theme={null} { "type": "audit", "ts": "2024-01-15T10:32:15.456Z", "action": "sql.execute", "actor": "alice@example.com", "connection": "prod-db", "database": "postgres", "sql": "SELECT * FROM users WHERE active = true", "success": true, "duration_ms": 45, "row_count": 150, "source": "web" } ``` | Field | Description | | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `connection` | Connection ID | | `database` | Database name | | `sql` | Full SQL query text | | `success` | Whether query succeeded | | `duration_ms` | Execution time in milliseconds | | `row_count` | Number of rows returned (optional) | | `error` | Error message (if failed) | | `source` | Query origin: `"web"` for the web app, `"mcp"` for the [MCP Server](/features/mcp-server) | | `tool` | MCP tool name that ran the query (only when `source` is `"mcp"`) | | `agent` | The [agent](/configuration/config#agents) id that ran the query (only when `source` is `"mcp"`). For a delegated agent the `actor` is the user it acts for; for a pure agent the `actor` is `agent:` | ### data.export ```json theme={null} { "type": "audit", "ts": "2024-01-15T10:33:00.000Z", "action": "data.export", "actor": "alice@example.com", "connection": "prod-db", "database": "postgres", "sql": "SELECT * FROM users WHERE active = true", "row_count": 150, "format": "csv", "source": "web" } ``` | Field | Description | | ------------ | --------------------------------------------------- | | `connection` | Connection ID | | `database` | Database name | | `sql` | SQL query that produced the exported data | | `row_count` | Number of rows exported | | `format` | Export format (`csv`, `tsv`, `json`, or `markdown`) | | `source` | Export origin: currently `"web"` | ## Capturing Logs Audit logs are written to stdout alongside other server output. Filter by `"type":"audit"` to capture audit events only: ```bash theme={null} # Filter and write to file # --line-buffered: flush output after each line for real-time logging pgconsole | grep --line-buffered '"type":"audit"' >> audit.log # Parse and forward to log aggregator # --unbuffered: disable jq's output buffering for real-time streaming pgconsole | jq --unbuffered -c 'select(.type == "audit")' | nc logserver 5000 ``` # Database Access Control Source: https://docs.pgconsole.com/features/database-access-control pgconsole provides fine-grained access control for your database connections. You define [IAM rules](/configuration/config#access-control-iam) in your configuration file, and pgconsole enforces them — rejecting unauthorized queries before they reach the database. IAM is **opt-in**. With no `[[iam]]` rules defined, every authenticated user has full access to all connections. Enforcement begins the moment you define your first rule — from then on, any user without a matching rule is denied. ```mermaid theme={null} flowchart LR U[User] -->|SQL| P[pgconsole] P -->|Check| IAM[IAM Config] IAM -->|Allowed| P P -->|Execute| DB[(Database)] IAM -.->|Denied| R[Reject] ``` Unlike PostgreSQL's built-in role system (`GRANT`/`REVOKE`), pgconsole's access control operates at the application layer. You don't need to create database roles for each user — all users connect through shared credentials while pgconsole enforces per-user permissions, controls application-level features like export, and logs every action for [auditing](/features/audit-log). IAM Permission Denied Once at least one rule is defined, access control works on three principles: * **Default deny** — users have no access unless a rule explicitly grants it * **Connection-scoped** — permissions are granted per database connection, not globally * **Disjoint permissions** — each permission level is independent; `write` does not imply `read` While IAM is active, users only see connections they have at least one permission for. Connections without any matching rules are hidden entirely. ## Prerequisites * [Authentication](/configuration/config#authentication) must be enabled, and at least one `[[iam]]` rule must be defined; otherwise, all users get full access to all connections * Users must be defined in [`[[users]]`](/configuration/config#users) * Groups (if used) must be defined in [`[[groups]]`](/configuration/config#groups) ## Permission Levels Independent permissions control what users can do: | Permission | What It Controls | UI Elements Affected | | ---------- | -------------------------------------------------------- | --------------------------------------------------- | | `read` | `SELECT`, `SHOW`, `SET` queries | Query execution | | `write` | `INSERT`, `UPDATE`, `DELETE` | Inline row editing, add/delete rows, staged changes | | `ddl` | `CREATE`, `ALTER`, `DROP`, `TRUNCATE`, `GRANT`, `REVOKE` | Edit function definitions | | `admin` | Role/database management, `pg_terminate_backend` | Terminate sessions, cancel other users' queries | | `explain` | `EXPLAIN` queries | Explain button in editor toolbar and context menu | | `execute` | `CALL` stored procedures | Procedure execution | | `export` | Export from results | Export menu | A single SQL statement can require multiple permissions. For example, `SELECT pg_terminate_backend(123)` requires both `read` (for the SELECT) and `admin` (for the function call). The permission badge shows the user's permissions on each connection. IAM Permission Badge ## Groups Groups let you manage permissions for multiple users at once instead of listing individual emails in every IAM rule. Define groups in `pgconsole.toml` with [`[[groups]]` sections](/configuration/config#groups): ```toml pgconsole.toml theme={null} [[groups]] id = "developers" name = "Development Team" members = ["alice@example.com", "bob@example.com"] [[groups]] id = "dba" name = "Database Administrators" members = ["admin@example.com"] ``` ## Defining Rules Each [`[[iam]]`](/configuration/config#access-control-iam) rule specifies a connection, a set of permissions, and a set of members. ```toml pgconsole.toml theme={null} [[iam]] connection = "production" permissions = ["explain", "read", "write"] members = ["*"] ``` ### Targeting Connections | Value | Scope | | ----------------- | --------------------- | | `*` | All connections | | `` | A specific connection | ### Targeting Members | Format | Who It Matches | | ------------------ | ---------------------- | | `*` | Any authenticated user | | `user:` | A specific user | | `group:` | All members of a group | ### Granting Permissions List individual permissions or use the shorthand: ```toml pgconsole.toml theme={null} # Specific permissions permissions = ["read", "write", "explain", "export"] # All permissions at once permissions = ["*"] ``` ## Common Patterns ### Read-Only Access for Everyone, Full Access for DBAs ```toml pgconsole.toml theme={null} [[iam]] connection = "*" permissions = ["read", "explain", "export"] members = ["*"] [[iam]] connection = "*" permissions = ["*"] members = ["group:dba"] ``` ### Environment-Based Access ```toml pgconsole.toml theme={null} # Developers: full access to dev, read-only on staging and prod [[iam]] connection = "development" permissions = ["*"] members = ["group:developers"] [[iam]] connection = "staging" permissions = ["read", "explain"] members = ["group:developers"] [[iam]] connection = "production" permissions = ["read", "explain"] members = ["group:developers"] # DBAs: full access everywhere [[iam]] connection = "*" permissions = ["*"] members = ["group:dba"] ``` ### Analyst with Export Access ```toml pgconsole.toml theme={null} [[iam]] connection = "analytics" permissions = ["read", "explain", "export"] members = ["user:analyst@example.com"] ``` ### Write Access on Staging Only ```toml pgconsole.toml theme={null} [[iam]] connection = "*" permissions = ["read"] members = ["group:dev-team"] [[iam]] connection = "staging" permissions = ["write"] members = ["group:dev-team"] ``` Because permissions are a union of all matching rules, the dev team ends up with `read` on all connections and `read` + `write` on staging. # MCP Server Source: https://docs.pgconsole.com/features/mcp-server pgconsole exposes a remote [Model Context Protocol](https://modelcontextprotocol.io) (MCP) server so external AI agents — Claude Code, Cursor, VS Code Copilot, Windsurf, and others — can work with the Postgres connections pgconsole manages. Unlike handing an agent a raw connection string, every MCP request is **governed**: it runs as a defined [agent](/configuration/config#agents) principal, is gated by [Access Control (IAM)](/features/database-access-control), is checked per statement, and is recorded in the [Audit Log](/features/audit-log). ## Endpoint The server is mounted on the running pgconsole instance over Streamable HTTP: ``` POST /mcp ``` Authenticate with an [agent](/configuration/config#agents) token: ``` Authorization: Bearer ``` Requests without a valid token are rejected with `401`. ## Identity Each token belongs to an `[[agents]]` entry — a non-human principal that is **not** a user (no UI login). There are two kinds: If no `[[iam]]` rules are defined, IAM is off and agents have full access to all connections; define at least one rule to enforce least privilege. * **Pure agent** — a standalone service account (e.g. a CI bot). When IAM is active, authorized by IAM rules whose `members` include `agent:`. Audited as `agent:`. * **Delegated agent** — acts `on_behalf_of` a user, **inheriting that user's permissions** narrowed by optional `permissions`/`connections` caps. It can never exceed the user and loses access automatically when the user does. Audited as the user, tagged with the agent. ```toml pgconsole.toml theme={null} # Pure agent [[agents]] id = "migration-bot" token = "pgc_mcp_xxxxxxxxxxxxxxxx" [[iam]] connection = "staging" permissions = ["read", "ddl"] members = ["agent:migration-bot"] # Delegated agent — bounded by alice, read-only on prod [[agents]] id = "alice-claude" token = "pgc_mcp_yyyyyyyyyyyyyyyy" on_behalf_of = "alice@example.com" permissions = ["read"] connections = ["prod"] ``` See [Agents](/configuration/config#agents) for the full field reference. ## Connecting a client Point any MCP client that supports remote (Streamable HTTP) servers at the endpoint. For example, with Claude Code: ```bash theme={null} claude mcp add --transport http pgconsole https://pgconsole.example.com/mcp \ --header "Authorization: Bearer pgc_mcp_xxxxxxxxxxxxxxxx" ``` ## Tools The advertised tool list is **filtered per agent** — an agent only sees the tools its permissions unlock. ### Discovery Available whenever the agent can access at least one connection. | Tool | Description | | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `list_connections` | Connections the agent can access, with the IAM permissions granted on each | | `list_objects` | Browse a connection's catalog. Omit `schema` to list schemas with counts; with `schema`, returns a paginated, filterable list of tables/views (name, kind, estimated rows, size, comment) | | `describe_table` | Full detail for one table/view: columns, primary/foreign keys, indexes, constraints, and comments | `list_objects` is paginated (pass the response's `nextCursor` back as `cursor`) and filterable (`nameFilter`), so agents navigate large schemas top-down instead of pulling a full dump. ### Execution One tool per IAM permission. Each appears only if the agent holds that permission on at least one connection. | Tool | Permission | Accepts | | --------------- | ---------- | ---------------------------------------------------------------------------------------------------------- | | `explain_query` | `explain` | A single `SELECT` to plan (options: `analyze`, `buffers`, `format`) | | `query` | `read` | Read-only statements (`SELECT`, `SHOW`, …). Results are capped at 1000 rows (override down with `maxRows`) | | `write_data` | `write` | `INSERT` / `UPDATE` / `DELETE` / `COPY` | | `run_ddl` | `ddl` | `CREATE` / `ALTER` / `DROP` / `GRANT` / `REVOKE` / … | ## Enforcement Every execution tool runs the submitted SQL through pgconsole's parser-based permission detection before touching the database: 1. The SQL is parsed; each statement's required permission must match the tool's permission. A `DROP` sent to `query`, or a mixed-class batch, is rejected — there is no smuggling a privileged statement through a lower-privileged tool. 2. The full set of permissions the SQL requires (including ones implied by function calls, e.g. `pg_terminate_backend` requires `admin`) must be a subset of the agent's grants. 3. The query is executed and recorded in the audit log, tagged with `source: "mcp"`, the tool name, and the agent. `explain_query` only accepts a single `SELECT` (Postgres `EXPLAIN` rejects other statement kinds); with `analyze` (which actually executes the statement) it additionally requires every permission running the statement would require. `query` caps its result at 1000 rows so a broad `SELECT` can't flood an agent's context. When capped, the response sets `truncated: true` and reports the full `rowCount` alongside the returned rows — narrow with `LIMIT`/`WHERE`, or pass a smaller `maxRows`. This cap applies to the MCP route only; the web UI is uncapped (it needs the full result set for CSV export and inline editing). Least privilege: a pure agent gets only what its `agent:` IAM rules grant; a delegated agent can never exceed the user it acts for, and is further narrowed by its `permissions`/`connections` caps. Give each agent the narrowest grant it needs. # SQL Editor Source: https://docs.pgconsole.com/features/sql-editor pgconsole's SQL editor is a workspace for writing queries, browsing schemas, and working with results. What sets it apart: * **Understands your SQL** — syntax highlighting, autocomplete, formatting, error detection, and function signature help are all backed by a PostgreSQL parser, not regex. * **Guardrails** — [database access control](/features/database-access-control) and [audit logs](/features/audit-log) ensure every query is authorized and recorded. * **AI assistant** — generate, explain, fix, and rewrite SQL with an [AI assistant](/features/ai-assistant) that understands your schema context. SQL Editor overview ## Editor ### SQL Intelligence The editor parses your SQL in real-time to provide: * **Autocomplete** — context-aware suggestions for tables, columns, joins, and CTEs (`Ctrl+Space`) Autocomplete suggestions * **Formatting** — pretty-print or collapse to one line SQL formatting * **Error detection** — red underlines with hover tooltips; optionally **Fix with AI** * **Code folding** — collapse `SELECT`, `WITH`, and other blocks ### Quick SQL Generation When a table or view is selected in the schema browser, the Quick SQL menu generates SQL templates and inserts them into the editor: INSERT, UPDATE, DELETE, CREATE TABLE, and ALTER ADD COLUMN are only available for tables (not views). Quick SQL menu ## Data Grid Query results appear in the panel below the editor in a virtual-scrolling grid that handles large result sets efficiently. Query results ### Navigation | Action | Behavior | | ------------------- | ----------------------------- | | Click a row | Open the row detail panel | | Double-click a cell | Start editing that cell | | `j` / `↓` | Select next row | | `k` / `↑` | Select previous row | | `Space` | Pin or unpin the selected row | Pinned rows stick to the top of the grid for easy comparison across large result sets. ### Inline Editing With `write` permission, you can modify data directly in the grid. All changes are staged locally — nothing is committed until you explicitly execute. Row detail panel | Action | How | | --------------- | --------------------------------------------------------------------------- | | Edit a cell | Double-click the cell, or click the row and press Edit in the detail panel | | Add a row | Click the **+** button in the results toolbar | | Delete a row | Right-click and select Delete, or use the Delete button in the detail panel | | Duplicate a row | Right-click and select Duplicate (primary key fields cleared) | Staged changes are color-coded: green for new rows (INSERT), amber for modified rows (UPDATE), red with strikethrough for deleted rows (DELETE). When changes are pending, a floating bar lets you **Preview** the generated SQL or **Discard** all changes. The preview modal shows syntax-highlighted INSERT/UPDATE/DELETE statements and optionally an AI-powered risk assessment before you **Execute All**. Staged changes preview ### Data Export * **Export results** — downloads visible rows as CSV, TSV, JSON, or Markdown (requires `export` permission) * **Copy as Markdown** — copies results as a markdown table to the clipboard ## Schema Inspection Schema tabs provide detailed inspection of database objects. Schema tab ## Active Sessions The Processes button in the toolbar opens a modal showing active database connections. The list auto-refreshes periodically. With `admin` permission, a **Terminate** button appears on each row with a confirmation countdown. Active processes # White Labeling Source: https://docs.pgconsole.com/features/white-labeling pgconsole supports white labeling, allowing you to rebrand and embed the console into your own product. * **Custom Logo** — Replace the default logo with your own in the top-right corner. * **Custom Theme** — Customize the color scheme to match your brand. * **Embedding** — Embed pgconsole into your application using an iframe. # FAQ Source: https://docs.pgconsole.com/getting-started/faq ## Getting Help * Bugs or feature requests - [GitHub Issues](https://github.com/pgplex/pgconsole/issues) * Billing questions - [Email us](mailto:billing@pgconsole.com) ## What PostgreSQL versions are supported? pgconsole supports all PostgreSQL versions that have not reached [end-of-life](https://www.postgresql.org/support/versioning/). ## Does pgconsole phone home? Is it air-gapped? No. pgconsole is fully self-hosted and makes no outbound network requests except: * Database connections you configure * [AI provider](/features/ai-assistant) APIs, if you enable AI features ## License pgconsole is licensed under the [Apache License 2.0](https://github.com/pgplex/pgconsole/blob/main/LICENSE) and is free to use, with all features included. ## Certifications SOC 2 Type II. # Introduction Source: https://docs.pgconsole.com/getting-started/introduction SQL Editor overview *From the makers of [Bytebase](https://www.bytebase.com), [pgschema](https://github.com/pgschema/pgschema), and [Google Cloud SQL for Postgres](https://cloud.google.com/sql/postgresql).* `pgconsole` is an open source web-based PostgreSQL editor. Single binary, single config file, no database required. Connect your team to PostgreSQL with access control and audit logging built in. ```mermaid theme={null} flowchart TD Dev[Developer] -->|read, write| PG[pgconsole] Analyst[Analyst] -->|read| PG DBA[DBA] -->|admin| PG PG -->|SQL| Dev_DB[(Dev)] PG -->|SQL| Staging_DB[(Staging)] PG -->|SQL| Prod_DB[(Prod)] PG -.->|loads pgconsole.toml| Git[(Git)] ``` ## Fast * Full PostgreSQL parser powers realtime autocomplete, syntax highlighting, and error detection * Query, edit results, stage changes, and apply — all in one view * AI assistant generates SQL, explains queries, fixes errors, and assesses change risk ## Secure * Self-hosted and air-gapped ready — no outbound traffic except to your databases and optional AI provider * Server-side connections — database credentials never reach individual users. No firewall exceptions, no bastion hosts * Fine-grained IAM controls who can read, write, or administer each connection * Every query and login is recorded in the audit log ## Work with Git * Everything is in `pgconsole.toml` — connections, users, groups, access rules, AI providers * No database, no migrations, no admin UI settings that drift between environments * Review access control changes in PRs the same way you review code ## Use cases ### Team * Credentials stay on the server, permissions enforced per user, every query logged * Onboard new members by adding a line to `pgconsole.toml`, not by creating database roles * Manage access control in Git — same review process as code changes ### Individual Developer * A modern alternative to pgAdmin, DBeaver, or psql * No account required, no Java runtime, no Electron — single binary that starts in seconds * Autocomplete and error detection work on CTEs, subqueries, and window functions ### Bundle with Your Product (OEM) * Add a database UI to any product that ships PostgreSQL * No additional database, no separate user management * Configure once in your deployment and it's ready for your customers ## Next steps Run your first query in minutes # Quickstart Source: https://docs.pgconsole.com/getting-started/quickstart Get pgconsole running and execute your first query in under a minute. ## Step 1: Add a database connection Create a `pgconsole.toml` file with at least one connection: ```toml pgconsole.toml theme={null} [[connections]] id = "local" name = "Local PostgreSQL" host = "localhost" port = 5432 database = "postgres" username = "postgres" password = "postgres" ``` If connecting to a database on your host machine from Docker, use `host.docker.internal` instead of `localhost` in your config. That's the minimum config. See [Config Reference](/configuration/config) for authentication, access control, AI providers, and more. ## Step 2: Start ```bash theme={null} docker run -p 9876:9876 -v /path/to/pgconsole.toml:/etc/pgconsole.toml pgplex/pgconsole ``` ```bash theme={null} npx @pgplex/pgconsole --config /path/to/pgconsole.toml ``` ```bash theme={null} npm install -g @pgplex/pgconsole pgconsole --config /path/to/pgconsole.toml ``` Once you see the banner in your terminal, pgconsole is ready: ``` /\_ \ _____ __ ___ ___ ___ ____ ___\//\ \ __ /\ '__`\ /'_ `\ /'___\ / __`\ /' _ `\ /',__\ / __`\\ \ \ /'__`\ \ \ \L\ \/\ \L\ \/\ \__//\ \L\ \/\ \/\ \/\__, `\/\ \L\ \\_\ \_/\ __/ \ \ ,__/\ \____ \ \____\ \____/\ \_\ \_\/\____/\ \____//\____\ \____\ \ \ \/ \/___L\ \/____/\/___/ \/_/\/_/\/___/ \/___/ \/____/\/____/ \ \_\ /\____/ \/_/ \_/__/ Server running on http://localhost:9876 ``` Open `http://localhost:9876` in your browser. You'll see the SQL editor with the schema browser on the left. ## Demo mode If you run pgconsole **without** `--config`, it starts in demo mode with an embedded in-memory PostgreSQL database pre-loaded with sample data: ```bash theme={null} docker run -p 9876:9876 pgplex/pgconsole ``` ```bash theme={null} npx @pgplex/pgconsole ``` ```bash theme={null} npm install -g @pgplex/pgconsole pgconsole ``` Demo mode data is ephemeral and resets on every restart. To connect to a real database, create a `pgconsole.toml` as shown in [Step 1](#step-1-add-a-database-connection). ## Docker Compose Add pgconsole as a service alongside your existing PostgreSQL in Docker Compose: ```yaml docker-compose.yml theme={null} services: postgres: image: postgres:17 environment: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres pgconsole: image: pgplex/pgconsole ports: - "9876:9876" configs: - source: pgconsole_config target: /etc/pgconsole.toml depends_on: - postgres configs: pgconsole_config: content: | [[connections]] id = "local" name = "Local PostgreSQL" host = "postgres" port = 5432 database = "postgres" username = "postgres" password = "postgres" ``` ```bash theme={null} docker compose up ``` Open `http://localhost:9876` — pgconsole connects to your database and you can browse schemas, run queries, and edit data immediately. ## Kubernetes Deploy pgconsole as a Deployment with a ConfigMap for the configuration: ```yaml pgconsole.yaml theme={null} apiVersion: v1 kind: ConfigMap metadata: name: pgconsole-config data: pgconsole.toml: | [[connections]] id = "main" name = "Production PostgreSQL" host = "postgres.default.svc.cluster.local" port = 5432 database = "postgres" username = "postgres" password = "postgres" --- apiVersion: apps/v1 kind: Deployment metadata: name: pgconsole spec: replicas: 1 selector: matchLabels: app: pgconsole template: metadata: labels: app: pgconsole spec: containers: - name: pgconsole image: pgplex/pgconsole ports: - containerPort: 9876 volumeMounts: - name: config mountPath: /etc/pgconsole.toml subPath: pgconsole.toml volumes: - name: config configMap: name: pgconsole-config --- apiVersion: v1 kind: Service metadata: name: pgconsole spec: selector: app: pgconsole ports: - port: 9876 targetPort: 9876 ``` ```bash theme={null} kubectl apply -f pgconsole.yaml kubectl port-forward svc/pgconsole 9876:9876 ``` Open `http://localhost:9876` to access pgconsole. To expose pgconsole externally with TLS, see [Kubernetes Ingress](/configuration/external-access#kubernetes-ingress) and [Kubernetes Gateway API](/configuration/external-access#kubernetes-gateway-api). ## Cloudflare pgconsole can run on Cloudflare using [Containers](https://developers.cloudflare.com/containers/) — our own [demo.pgconsole.com](https://demo.pgconsole.com) is deployed this way. **Prerequisites:** A Cloudflare account with Containers enabled and the [Wrangler CLI](https://developers.cloudflare.com/workers/wrangler/) installed. See the [demo worker source](https://github.com/pgplex/pgconsole/tree/main/worker/demo) for a complete example — it powers [demo.pgconsole.com](https://demo.pgconsole.com). **Deploy** Push the Docker image to the Cloudflare container registry and deploy the worker: ```bash theme={null} # Cloudflare Containers requires an explicit version tag — `latest` is not supported npx wrangler containers push pgplex/pgconsole: npx wrangler deploy ``` ## Next steps Expose pgconsole to your team Autocomplete, formatting, inline editing, and more