# Connection reference

> postgres:// URIs, PG* env vars, password_command, .pgpass, sslmode=require rules, localhost exemptions, and pool settings.

- Repository: timescale/tigerfs
- GitHub: https://github.com/timescale/tigerfs
- Human docs: https://grok-wiki.com/public/docs/timescale-tigerfs-60719456a5c3
- Complete Markdown: https://grok-wiki.com/public/docs/timescale-tigerfs-60719456a5c3/llms-full.txt

## Source Files

- `internal/tigerfs/db/connection.go`
- `internal/tigerfs/db/password.go`
- `internal/tigerfs/db/tls.go`
- `internal/tigerfs/cmd/test_connection.go`
- `docs/spec.md`

---

---
title: "Connection reference"
description: "postgres:// URIs, PG* env vars, password_command, .pgpass, sslmode=require rules, localhost exemptions, and pool settings."
---

TigerFS opens PostgreSQL through `db.NewClient`, which resolves credentials, enforces TLS on connection strings, and builds a `pgxpool` pool used by every mount backend. Connection strings come from CLI arguments, cloud backend CLIs (`tiger:`, `ghost:`), or standard `PG*` / `TIGERFS_*` configuration; `tigerfs test-connection` additionally uses `db.ResolveConnectionString` to assemble a URL from config when no argument is passed.

## Connection string formats

TigerFS accepts the same connection string shapes as [pgx](https://github.com/jackc/pgx): URL form and libpq key-value form.

| Format | Example | Notes |
|--------|---------|-------|
| URL | `postgres://user@host:5432/dbname` | Preferred for mounts; supports query params (`?sslmode=require`) |
| URL (alternate scheme) | `postgresql://user@host/db` | Treated identically to `postgres://` |
| Key-value | `host=db.example.com user=myuser dbname=mydb` | Parsed for TLS host extraction and `sslmode` injection |

URL structure:

```text
postgres://[user[:password]@]host[:port][/dbname][?param=value&...]
```

<Warning>
Passwords embedded in URLs appear in process listings and logs. `db.SanitizeConnectionString` strips URL passwords and masks `password=` in key-value strings before logging; prefer `user@host` URLs plus a separate secret source.
</Warning>

## How TigerFS picks a connection target

Resolution differs by command.

### Mount (`tigerfs mount`)

| Input | `connStr` passed to `NewClient` | Mountpoint |
|-------|----------------------------------|------------|
| `tigerfs mount postgres://user@host/db /mnt` | `postgres://user@host/db` | `/mnt` |
| `tigerfs mount tiger:ID` | Resolved `postgres://…` from Tiger CLI | `<default_mount_dir>/ID` |
| `tigerfs mount /mnt` (no URL) | `""` (empty; pgx uses `PG*` env vars) | `/mnt` |

Cloud prefixes are resolved in `cmd/mount.go` via `backend.Resolve` → `GetConnectionString` before connect.

### `test-connection` and `migrate`

These commands call `db.ResolveConnectionString` when no explicit argument is given:

1. Explicit connection argument (if any)
2. `TIGER_SERVICE_ID` / `connection.tiger_service_id` → Tiger Cloud CLI
3. Config `host` (from `PGHOST` / config file) → builds `postgres://{user}@{host}:{port}/{database}`

If none apply, resolution fails with an error asking for a connection string, Tiger service ID, or `PGHOST`.

```mermaid
sequenceDiagram
  participant CLI as tigerfs CLI
  participant Resolve as db.ResolveConnectionString
  participant NC as db.NewClient
  participant Pool as pgxpool

  CLI->>Resolve: explicit / config / Tiger Cloud
  Resolve-->>CLI: postgres URL or error
  CLI->>NC: connStr + Config
  NC->>NC: resolvePassword
  NC->>NC: enforceSSLMode
  NC->>Pool: ParseConfig, Ping
  Pool-->>NC: ready
```

<Note>
For remote hosts, pass an explicit `postgres://user@remote-host:5432/db` on **mount** so `enforceSSLMode` can read the hostname from the URL. An empty mount connection string is treated as localhost for TLS defaults (see [TLS enforcement](#tls-enforcement)); pgx may still connect using `PGHOST` from the environment.
</Note>

## PostgreSQL environment variables

Viper binds standard libpq variables into `config.Config` at startup (`config.Init`):

| Variable | Config field | Default when unset |
|----------|--------------|-------------------|
| `PGHOST` | `host` | (empty) |
| `PGPORT` | `port` | `5432` |
| `PGUSER` | `user` | (empty) |
| `PGDATABASE` | `database` | (empty) |
| `PGPASSWORD` | `password` | (empty; not used directly by `NewClient`) |

TigerFS-prefixed overrides use `TIGERFS_` + map key (e.g. `TIGERFS_PORT` overrides `PGPORT` when both are set).

Additional Tiger connection env vars:

| Variable | Purpose |
|----------|---------|
| `TIGERFS_PASSWORD` | Database password (after `PGPASSWORD` in precedence) |
| `TIGERFS_PASSWORD_COMMAND` | Shell command whose stdout is the password |
| `TIGERFS_POOL_SIZE` | Maps to `pool_size` (default `10`) |
| `TIGERFS_POOL_MAX_IDLE` | Maps to `pool_max_idle` (default `5`) |
| `TIGERFS_INSECURE_NO_SSL` | Skips remote TLS enforcement when `true` |
| `TIGER_SERVICE_ID` | Legacy Tiger Cloud service fallback in `ResolveConnectionString` |

<Info>
The `password` field exists on `Config` for display and viper binding, but `db.resolvePassword` reads `PGPASSWORD` / `TIGERFS_PASSWORD` from the environment only—not `cfg.Password`. Do not put passwords in `config.yaml`; use `.pgpass`, env vars, or `password_command`.
</Info>

## Password resolution

`db.NewClient` resolves secrets before `pgxpool.ParseConfig`:

```mermaid
flowchart TD
  A[Connection string already contains password?] -->|yes| B[pgx uses URL password as-is]
  A -->|no| C{PGPASSWORD set?}
  C -->|yes| D[Inject into URL / append key-value]
  C -->|no| E{TIGERFS_PASSWORD set?}
  E -->|yes| D
  E -->|no| F{password_command configured?}
  F -->|yes| G[Run command, inject stdout]
  F -->|no| H[pgx reads ~/.pgpass]
```

| Priority | Source | Behavior |
|----------|--------|----------|
| 1 (implicit) | Password in `postgres://user:pass@…` | Left unchanged; env/command not consulted |
| 2 | `PGPASSWORD` | Injected into URL; wins over `TIGERFS_PASSWORD` and `password_command` |
| 3 | `TIGERFS_PASSWORD` | Injected when `PGPASSWORD` unset |
| 4 | `password_command` | Executed when env passwords unset |
| 5 | `~/.pgpass` | Handled by pgx when no injected password |

### `password_command`

Configure in `~/.config/tigerfs/config.yaml`:

```yaml
connection:
  password_command: "vault kv get -field=password secret/prod-db"
```

Or via `TIGERFS_PASSWORD_COMMAND`.

<ParamField body="password_command" type="string">
Shell command run to fetch the database password. First token is the executable; remaining tokens are arguments (`strings.Fields`—no quoted-argument parsing). Stdout is trimmed; must be non-empty. Context timeout: **5 seconds**. Non-zero exit includes stderr in the error.
</ParamField>

<RequestExample>

```bash
export TIGERFS_PASSWORD_COMMAND='pass show tigerfs/prod'
tigerfs test-connection postgres://app@db.example.com/prod
```

</RequestExample>

### `.pgpass`

TigerFS does not parse `.pgpass` itself. When no password is injected, [pgx](https://github.com/jackc/pgx) reads `~/.pgpass` using the usual libpq rules.

```text
hostname:port:database:username:password
```

```bash
chmod 0600 ~/.pgpass
tigerfs mount postgres://myuser@localhost:5432/mydb /mnt/db
```

### Password injection rules

- **URL:** Requires `user@host`; replaces or adds `user:password@`.
- **Key-value:** Appends `password=…` only if `password=` is absent; existing key-value passwords are not overwritten.

If `PGPASSWORD` is set but the connection string has no `user@host` segment, injection fails with `connection string has no user@host format`.

## TLS enforcement

`db.enforceSSLMode` runs on every `NewClient` call after password handling.

### Remote hosts (non-localhost)

| Current `sslmode` | Action |
|-------------------|--------|
| (missing) | Add `sslmode=require` |
| `disable`, `prefer` | Replace with `sslmode=require` |
| `require`, `verify-ca`, `verify-full` | Unchanged |

### Localhost exemption

Hosts treated as local (no forced `require`):

- `localhost`, `127.0.0.1`, `::1`, empty host
- Unix socket paths (host starts with `/`)

When no `sslmode` is present on a local connection, TigerFS adds **`sslmode=disable`** (avoids pgx `prefer` handshake issues on `ssl=off` clusters). An explicit `sslmode` in the connection string is always preserved on localhost (e.g. `sslmode=require` on `localhost` stays as written).

### Disabling enforcement

<ParamField body="insecure_no_ssl" type="boolean" default="false">
CLI: `--insecure-no-ssl` on `mount` and `migrate`. Config: `connection.insecure_no_ssl` or `TIGERFS_INSECURE_NO_SSL=true`. Skips TLS rewriting; logs a warning for remote hosts.
</ParamField>

<Warning>
`--insecure-no-ssl` allows plaintext connections to remote databases. Use only for local development or trusted networks.
</Warning>

## Connection pool settings

Each mount daemon holds one `pgxpool.Pool` for the lifetime of the process.

| Setting | Config key | Env var | Default | pgx mapping |
|---------|------------|---------|---------|-------------|
| Max connections | `connection.pool_size` | `TIGERFS_POOL_SIZE` | `10` | `MaxConns` |
| Min idle connections | `connection.pool_max_idle` | `TIGERFS_POOL_MAX_IDLE` | `5` | `MinConns` |

Validation (`tigerfs config` write path): `pool_size` ≥ 1; `pool_max_idle` ≥ 0.

Additional pool behavior in `db.NewClient`:

- **Ping:** Caller context must succeed before the client is returned.
- **Pool creation:** Uses `context.Background()` so short mount timeouts do not cancel background pool maintenance.
- **`AfterConnect`:** Sets PostgreSQL `statement_timeout` from `query_timeout` when configured (default 30s in config).
- **Tracing:** Optional SQL debug tracing via `log_sql_params`.
- **Shutdown:** `Client.Close()` closes the pool on unmount.

Concurrent filesystem operations share the pool; server-side poolers (PgBouncer, etc.) work transparently.

<Tip>
Size `pool_size` to your database `max_connections` budget across all TigerFS mounts on the same instance. Each mount process opens up to `pool_size` connections.
</Tip>

## Verify connectivity

```bash
tigerfs test-connection postgres://user@localhost:5432/mydb
```

With only environment configuration:

```bash
export PGHOST=localhost PGDATABASE=mydb PGUSER=myuser
tigerfs test-connection
```

Successful output includes PostgreSQL version, current database/user, and counts of accessible schemas and tables (system/internal schemas excluded).

On failure, check stderr for structured zap hints (TLS, `password_command`, parse errors) before errno from mount tools.

## Security practices

| Practice | Recommendation |
|----------|----------------|
| Production secrets | `~/.pgpass` or `password_command` |
| Development / CI | `PGPASSWORD` or `TIGERFS_PASSWORD` |
| Remote encryption | Explicit `postgres://…` URL; avoid relying on empty-string mount + `PGHOST` for TLS defaults |
| Config file | Never store `password:` in `config.yaml` |
| Logging | Passwords stripped via `SanitizeConnectionString` in registry and debug logs |

## Related pages

<CardGroup>
  <Card title="Configuration reference" href="/configuration-reference">
    Full config file layout, precedence, and TigerFS-specific env vars.
  </Card>
  <Card title="Cloud backends" href="/cloud-backends">
    tiger: and ghost: prefix resolution and CLI credential requirements.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    mount, test-connection, migrate flags and argument patterns.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    Connection failures, TLS errors, and errno recovery.
  </Card>
</CardGroup>
