# Connect Postgres > Set up a PostgreSQL connection — required fields, TLS mode, and least-privilege credentials for a read-only analytics role. *[View this page in the Flax docs](https://flax-analytics.com/docs/connections/postgres)* This page covers connecting a PostgreSQL database (or a Postgres-compatible warehouse) to Flax. Add the connection from the **Data** page at [/app/data](/app/data). ## Required fields | Field | Description | | --- | --- | | `host` | Hostname of the Postgres server. | | `port` | Server port (typically `5432`). | | `database` | The database to query. | | `user` | Login role used for queries. | | `password` | Stored as the connection secret, encrypted at rest. | TLS is negotiated with `sslmode`, which defaults to `prefer` when unset. Use a stricter mode such as `require` for connections that cross a public network. ## Least-privilege credentials Create a dedicated role for Flax rather than reusing an application or admin login. Grant it only what analytics needs: ```sql CREATE ROLE flax_reader LOGIN PASSWORD '…'; GRANT CONNECT ON DATABASE analytics TO flax_reader; GRANT USAGE ON SCHEMA public TO flax_reader; GRANT SELECT ON ALL TABLES IN SCHEMA public TO flax_reader; ``` Flax only issues read queries, so a `SELECT`-only role is sufficient. Scope the grants to the schemas your semantic model actually references. > [!TIP] > After saving, use the connection test on the Data page to confirm Flax can reach the host and authenticate before you build models on it. ## Connecting a database behind a firewall If your database only accepts connections from known IPs, allow Flax's outbound (**egress**) address so its queries can reach you. Flax connects from a single static IP: | Flax hosting | Egress IP to allowlist | | --- | --- | | Flax Cloud (flax-analytics.com) | `195.201.255.126` | | Self-hosted | Your server's own outbound IP | Add that address to your database firewall / cloud security group / `pg_hba.conf`, alongside the least-privilege role above. Prefer not to expose a public endpoint at all? Under **Private network access** in the connection form, pick one of two outbound-only tunnels — the database never needs a public address: - **SSH bastion tunnel** — Flax dials your bastion (egress-checked), then reaches the database from the far side. Provide the bastion host/user and pin its host key. - **Cloudflare Access tunnel** — reach a database on a private network through your own Cloudflare Tunnel, with **zero inbound firewall exposure** (see below). ### Cloudflare Access tunnel If you already run [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/), Flax can connect over it — no open port, no bastion, nothing to install on Flax's side. One-time setup: 1. **Publish the database as a TCP service** on the tunnel. In your `cloudflared` config: ```yaml ingress: - hostname: bi-db.example.com service: tcp://10.0.0.5:5432 - service: http_status:404 ``` Then `cloudflared tunnel route dns bi-db.example.com` and reload `cloudflared`. 2. **Add a Zero Trust Access application** for that hostname with a policy whose action is **Service Auth** (not *Allow* — Allow expects a browser identity). Optionally also require Flax's egress IP (`195.201.255.126`) as a source, restoring an IP allowlist. 3. **Create a service token** and enter its **Client ID** and **Client Secret**, plus the hostname, in the connection form's **Cloudflare Access tunnel** fields. Both token halves are stored encrypted at rest. Flax reaches the database by opening an Access-authenticated WebSocket to that hostname and tunneling the Postgres protocol over it — the public hop is SSRF-checked, and your database stays private (ADR-0061). > [!NOTE] > Self-hosting? The egress IP is your own server's outbound address. Pin it to a reserved/floating > IP so it survives host rebuilds — see `deploy/provision/egress-ip.sh` in the repo. ## Query execution Queries you build in Explore are compiled to Postgres SQL and pushed down to your database (ADR-0004) — Flax never pulls whole tables to filter them locally. Repeated queries can be served from the result cache; see [Caching & performance](/docs/connections/caching-and-performance). ## Related - [Connecting data](/docs/connections/overview) - [Connection security](/docs/connections/security) - [Caching & performance](/docs/connections/caching-and-performance)