# Collector configuration

You configure the DBGorilla collector with a TOML file, `collector.toml`, with environment
variables, or with both.  The configuration says how to reach DBGorilla and which databases to
monitor, and also sets transport security and connection limits.

The collector never writes to your databases.

---

## Supplying the configuration

The collector is a single Linux container image.  It runs anywhere a container runs: Docker,
Docker Compose, ECS and Fargate, Cloud Run, Azure Container Instances, Nomad, Kubernetes.  It
reads the same configuration everywhere.

There are two ways to supply it, and they can be combined:

| Source | How | When to use it |
|---|---|---|
| A mounted file | Mount `collector.toml` into the container and pass `--config-file <path>` | Anywhere you can mount a file or project a config object |
| Environment variables | Set `DBG__<SECTION>__<KEY>` on the container | Platforms where a task or service definition is easier than a volume |

Environment variables override the file, so a shared base file plus a few per-environment
variables is a valid pattern.

### From a mounted file

The env file and the `docker run` command are under [Self-managed PostgreSQL](/docs/connect/self-managed/#self-managed-postgresql).

Those two pieces, one read-only file and a few injected variables, map directly onto any
container platform: a bind mount and `environment:` in Compose, a volume and `secrets` in an
ECS task definition, a projected ConfigMap and `env` in a Kubernetes Pod spec.

The container runs as uid 1000 with a read-only root filesystem, so mount the file (and any
certificates) read-only and make sure uid 1000 can read them.

### From environment variables alone

No file is required.  Every setting has an environment-variable form: `DBG__`, then the path to the
key, with a double underscore between each level.  List entries such as `[[component]]` are indexed
from zero.

```sh
docker run --rm \
  -e DBG__DBGORILLA__AGENT_ID=00000000-0000-0000-0000-000000000000 \
  -e DBG__DBGORILLA__TENANT_ID=11111111-1111-1111-1111-111111111111 \
  -e DBG__DBGORILLA__SECRET='${DBG_SERVER_SECRET}' \
  -e DBG__COMPONENT__0__NAME=orders \
  -e DBG__COMPONENT__0__ENGINE=postgres \
  -e DBG__COMPONENT__0__PROVIDER__TYPE=self_hosted \
  -e DBG__COMPONENT__0__AUTH__METHOD=password \
  -e DBG__COMPONENT__0__AUTH__USER=dbg_readonly \
  -e DBG__COMPONENT__0__AUTH__PASSWORD='${ORDERS_DB_PASSWORD}' \
  -e DBG__COMPONENT__0__CONNECT__HOST=db.internal.example.com \
  -e DBG__COMPONENT__0__CONNECT__PORT=5432 \
  -e DBG__COMPONENT__0__CONNECT__SSL_MODE=verify-full \
  --env-file secrets.env \
  dbgorillapublic.azurecr.io/dbg-collector:latest
```

This is the shape to use on ECS, Fargate, Cloud Run and similar, where a task or service definition
already carries environment variables and secret injection.  The two credential variables
stay as `${...}` references.  See [Secret references](#secret-references).

### With Helm

On Kubernetes the chart wires the same file for you.  Pass your `collector.toml` with `--set-file`;
the chart renders it verbatim into a ConfigMap and mounts it at
`/etc/dbg-collector/collector.toml`.

Create the Secret first, with whatever manages secrets in your cluster, holding one key per
environment variable your file references.  The `secrets.env` from [Self-managed PostgreSQL](/docs/connect/self-managed/#self-managed-postgresql) works for that:

```sh
kubectl create namespace dbg-collector
kubectl create secret generic dbg-collector-secrets \
  --namespace dbg-collector \
  --from-env-file=secrets.env
```

The chart reads `DBG_SERVER_SECRET` from that Secret itself.  It does not read your
`collector.toml`, so it cannot know your database password variables: map each one from the same
Secret with `extraEnv` in a values file:

```yaml
# values.yaml
extraEnv:
  - name: ORDERS_DB_PASSWORD
    valueFrom:
      secretKeyRef: { name: dbg-collector-secrets, key: ORDERS_DB_PASSWORD }
```

```sh
helm install dbg-collector oci://dbgorillapublic.azurecr.io/charts/dbg-collector \
  --namespace dbg-collector \
  --values ./values.yaml \
  --set-file config.inline=./collector.toml \
  --set secrets.existingSecret=dbg-collector-secrets
```

Only the Secret's name reaches the command line, so no credential lands in shell history, in `ps`
output, or in CI logs.

Whichever route you take, the collector loads the same file with the same loader.

---

## Minimal configuration

```toml
[dbgorilla]
agent_id  = "00000000-0000-0000-0000-000000000000"
tenant_id = "11111111-1111-1111-1111-111111111111"
secret    = "${DBG_SERVER_SECRET}"

[[component]]
name   = "orders"
engine = "postgres"

[component.provider]
type = "self_hosted"

[component.auth]
method   = "password"
user     = "dbg_readonly"
password = "${ORDERS_DB_PASSWORD}"

[component.connect]
host      = "db.internal.example.com"
port      = 5432
databases = ["orders"]
ssl_mode  = "verify-full"
```

---

## Sections

| Section | Purpose | Required |
|---|---|---|
| [`[dbgorilla]`](#connection-to-dbgorilla) | Credentials and endpoints for the DBGorilla service | Yes |
| [`[[component]]`](#databases-to-monitor) | One database system to monitor.  Repeat per system | Yes, at least one |
| [`[commands]`](/docs/reference/collector-commands/#database-commands) | Which statements DBGorilla may run on your databases.  The samples turn on `explain` and `collect_statistics` | No.  Without it, no commands are allowed |
| [`[topology]`](#schema-scanning) | Schema-scan cadence | No |
| [`[engine.postgres]` / `[engine.mysql]`](#performance-tuning) | Connection-footprint tuning | No |
| [`[otelcol]`](#bundled-metrics-collector) | Bundled metrics-collector process settings | No |

---

## Connection to DBGorilla

Section `[dbgorilla]`.

Identity and destination.  Every endpoint has a production default, so most deployments set only the
first three keys.

| Key | Type | Default | Description |
|---|---|---|---|
| `agent_id` | string | Required | Your collector's client ID (a UUID), issued by DBGorilla. |
| `tenant_id` | string | Required | Your tenant UUID.  Labels all exported telemetry. |
| `secret` | secret ref | `${DBG_SERVER_SECRET}` | The client secret paired with `agent_id`.  See [Secret references](#secret-references). |
| `opamp_base_url` | string | `wss://otlp.dbgorilla.com/v1/opamp` | Control-plane endpoint.  Override only for a self-hosted DBGorilla. |
| `otlp_base_url` | string | `https://otlp.dbgorilla.com:443` | Telemetry endpoint.  Override only for a self-hosted DBGorilla. |
| `auth_base_url` | string | `https://auth.dbgorilla.com` | Token endpoint.  Override only for a self-hosted DBGorilla. |
| `max_message_bytes` | integer | `67108864` (64 MiB) | Maximum control-plane message size. |
| `opamp_idle_timeout_secs` | integer | `75` | Reconnect if no control-plane traffic arrives within this window.  Must exceed the 30s server heartbeat. |

A self-hosted DBGorilla install sets all three URLs:

```toml
[dbgorilla]
agent_id       = "00000000-0000-0000-0000-000000000000"
tenant_id      = "11111111-1111-1111-1111-111111111111"
secret         = "${DBG_SERVER_SECRET}"
opamp_base_url = "wss://dbg.internal.acme.com/ingest/v1/opamp"
otlp_base_url  = "https://dbg.internal.acme.com"
auth_base_url  = "https://dbg.internal.acme.com/auth"
```

An `https://` telemetry endpoint uses TLS and authentication.  Any other scheme is treated as
insecure plaintext.

---

## Databases to monitor

Section `[[component]]`, repeated once per database system.

Each block names an engine and carries three sub-blocks:

| Sub-block | What it sets |
|---|---|
| [`[component.provider]`](#where-the-database-runs) | Where it is hosted |
| [`[component.auth]`](#database-login) | How to authenticate |
| [`[component.connect]`](#database-address-and-tls) | Address and scope |

| Key | Type | Default | Description |
|---|---|---|---|
| `name` | string | Required | Your label for this system.  Must be unique.  This is how it appears in DBGorilla. |
| `engine` | string | Required | `postgres` or `mysql`. |
| `commands` | list | inherits `[commands]` | Per-component command allow-list.  See [Database commands](/docs/reference/collector-commands/#database-commands). |

---

## Where the database runs

Section `[component.provider]`.

The provider is selected by `type`.  Each provider works with these engines and authentication methods:

| `type` | Engines | `[component.auth]` methods | Guide |
|---|---|---|---|
| `self_hosted` | `postgres`, `mysql` | `password` | [Native Postgres](/docs/connect/self-managed/), [MySQL](/docs/connect/prepare-mysql/) |
| `aws_rds` | `postgres`, `mysql` | `iam`, `password` | [AWS RDS and Aurora](/docs/connect/aws-rds-aurora/) |
| `aws_aurora` | `postgres`, `mysql` | `iam`, `password` | [AWS RDS and Aurora](/docs/connect/aws-rds-aurora/) |
| `cloud_sql` | `postgres`, `mysql` | `gcp_iam`, `password` | [Google Cloud SQL and AlloyDB](/docs/connect/google-cloud/) |
| `alloydb` | `postgres` | `gcp_iam`, `password` | [Google Cloud SQL and AlloyDB](/docs/connect/google-cloud/) |
| `cnpg` | `postgres` | `password` | [CloudNativePG](/docs/connect/cloudnativepg/) |
| `instaclustr` | `postgres` | `password` | [Instaclustr](/docs/connect/instaclustr/#instaclustr-postgresql) |

### Self-hosted

`type = "self_hosted"`

Any database you run yourself: on-premises, a VM, or a container.  The address comes from
`[component.connect]`.

```toml
[component.provider]
type = "self_hosted"
```

| Key | Type | Default | Description |
|---|---|---|---|
| `node_exporter.mode` | string | `auto` | Host-metric collection: `auto` (probe and use if present), `enabled` (assume present), `disabled` (never collect). |
| `node_exporter.port` | integer | `9100` | Port your node_exporter listens on. |

### AWS RDS

`type = "aws_rds"`

A single RDS instance.

| Key | Type | Default | Description |
|---|---|---|---|
| `region` | string | Required | AWS region. |
| `instance_id` | string | Required | The DB instance identifier. |
| `role_arn` | string | None | An IAM role to assume before minting `iam` tokens.  Leave unset to use the collector's own AWS identity. |
| `cloudwatch.poll_interval` | string | `60s` | How often CloudWatch is polled. |
| `cloudwatch.period` | string | `60s` | Datapoint granularity. |
| `cloudwatch.delay` | string | `180s` | Query offset past CloudWatch's publish lag.  Values below `120s` risk gaps. |

```toml
[component.provider]
type        = "aws_rds"
region      = "us-east-1"
instance_id = "prod-pg"
```

### AWS Aurora

`type = "aws_aurora"`

An Aurora cluster.  Writer and readers are discovered automatically.

| Key | Type | Default | Description |
|---|---|---|---|
| `region` | string | Required | AWS region. |
| `cluster_id` | string | Required | The DB cluster identifier. |

The `role_arn` and `cloudwatch.*` settings are the same as for `aws_rds`.

### Google Cloud SQL

`type = "cloud_sql"`

A Cloud SQL instance.  Name the primary; read replicas are discovered from it.

| Key | Type | Default | Description |
|---|---|---|---|
| `project` | string | Required | Google Cloud project ID. |
| `instance` | string | Required | The primary instance's name. |
| `region` | string | None | The instance's region.  Shown alongside the database in DBGorilla. |
| `monitoring.poll_interval` | string | `60s` | How often Cloud Monitoring is polled.  Each poll is a billed read on your project, so a longer interval costs less. |
| `monitoring.delay` | string | `180s` | Query offset past Cloud Monitoring's publish lag. |

```toml
[component.provider]
type     = "cloud_sql"
project  = "my-project"
region   = "us-central1"
instance = "prod-pg"
```

### AlloyDB

`type = "alloydb"`

An AlloyDB cluster.  Name the primary instance; read pools are discovered from the cluster.

| Key | Type | Default | Description |
|---|---|---|---|
| `project` | string | Required | Google Cloud project ID. |
| `region` | string | Required | The cluster's region. |
| `cluster` | string | Required | The AlloyDB cluster name. |
| `instance` | string | Required | The primary instance's name. |

The `monitoring.*` settings are the same as for `cloud_sql`.  AlloyDB's certificate is
self-signed, so no certificate authority vouches for it and `ssl_mode` defaults to `require` here.
To verify the connection anyway, pin that certificate as shown in [Google Cloud SQL and AlloyDB](/docs/connect/google-cloud/).

### CloudNativePG

`type = "cnpg"`

A CloudNativePG `Cluster` in Kubernetes.  The collector connects through the cluster's `-rw`
service.

| Key | Type | Default | Description |
|---|---|---|---|
| `namespace` | string | Required | Namespace holding the `Cluster`. |
| `cluster` | string | Required | The `Cluster` object's name.  One component per cluster. |
| `kubernetes.mode` | string | `auto` | Use of the Kubernetes API: `auto` (use it when permitted, otherwise collect metrics only), `enabled` (require it, and fail if the permissions are missing), `disabled` (never call it; needs no Kubernetes permissions).  Backup status needs the API. |
| `metrics.port` | integer | `9187` | Port of the CloudNativePG metrics exporter on each instance. |

```toml
[component.provider]
type      = "cnpg"
namespace = "prod-db"
cluster   = "app-db"

[component.provider.kubernetes]
mode = "auto"
```

### Instaclustr

`type = "instaclustr"`

A NetApp Instaclustr managed PostgreSQL cluster.  Nodes are discovered through the Instaclustr API.

| Key | Type | Default | Description |
|---|---|---|---|
| `cluster_id` | string | Required | The cluster's UUID. |
| `api_username` | string | Required | The Instaclustr console user the API keys belong to. |
| `api_key` | secret ref | Required | A read-only provisioning API key.  Discovery needs nothing more. |
| `provider` | string | None | Instaclustr's cloud-provider name, such as `AWS_VPC`.  Shown alongside the database in DBGorilla. |
| `region` | string | None | Instaclustr's region name, such as `US_EAST_1`.  Shown alongside the database in DBGorilla. |
| `use_private_addresses` | bool | `false` | Connect to nodes on their private addresses.  Set it when the collector runs inside a peered VPC. |
| `prometheus_api_key` | secret ref | None | Instaclustr's Prometheus API key.  Adds the platform's host metrics (CPU, memory, disk) for each node. |
| `provisioning_api_key` | secret ref | None | A writable provisioning API key.  Lets DBGorilla build sandboxes by forking this cluster.  It acts on Instaclustr's API; the collector still never writes to your databases.  Instaclustr scopes it to the whole account, so set it only if you want forks. |
| `allow_networks` | list | `[]` | CIDR ranges added to every fork's firewall, such as the collector's own egress address. |
| `allow_security_groups` | list | `[]` | AWS security group IDs added to every fork's firewall.  Needs `use_private_addresses = true`. |
| `api_base` | string | Instaclustr's public API | Override only for a dedicated Instaclustr environment. |

A complete file is on [Instaclustr](/docs/connect/instaclustr/#instaclustr-postgresql).

---

## Database login

Section `[component.auth]`.

The authentication method is selected by `method`.

### Password

`method = "password"`

| Key | Type | Description |
|---|---|---|
| `user` | string | Database user. |
| `password` | secret ref | The password.  See [Secret references](#secret-references). |

```toml
[component.auth]
method   = "password"
user     = "dbg_readonly"
password = "${ORDERS_DB_PASSWORD}"
```

### AWS IAM

`method = "iam"`

IAM is the recommended authentication method for RDS and Aurora databases.  It uses short-lived
RDS/Aurora IAM tokens, minted per connection.  Region comes from the provider block.

| Key | Type | Description |
|---|---|---|
| `user` | string | The IAM-enabled database user. |

Requires an IAM-enabled database user and the collector's AWS identity granting `rds-db:connect`.
To assume a role first, set `role_arn` in the provider block.

### Google Cloud IAM

`method = "gcp_iam"`

Short-lived Google Cloud IAM tokens for Cloud SQL and AlloyDB, minted with the collector's own
Google Cloud identity: Workload Identity, an attached service account, or Application Default
Credentials.

| Key | Type | Default | Description |
|---|---|---|---|
| `user` | string | Required | The IAM database user.  For a service account, its email without `.gserviceaccount.com`, for example `dbg-collector@my-project.iam`. |
| `scopes` | list | `[]` | Extra token scopes.  AlloyDB needs `https://www.googleapis.com/auth/alloydb.login`; Cloud SQL needs none. |

```toml
[component.auth]
method = "gcp_iam"
user   = "dbg-collector@my-project.iam"
```

---

## Database address and TLS

Section `[component.connect]`.

Address, transport security, and which databases to collect.

### PostgreSQL

| Key | Type | Default | Description |
|---|---|---|---|
| `host` | string | Required | Database host. |
| `port` | integer | `5432` | Database port. |
| `databases` | list | `[]` | Databases to monitor.  Empty collects every non-template database. |
| `exclude_databases` | list | `[]` | Databases to skip, applied after `databases`. |
| `ssl_mode` | string | `verify-full` | `disable`, `require`, `verify-ca`, `verify-full`.  See [Transport security](#transport-security). |
| `ca_cert` | path | system trust store | CA bundle the server certificate is verified against. |
| `client_cert` | path | None | Client certificate for mTLS. |
| `client_key` | path | None | Client private key for mTLS. |
| `allow_unverified_token_transport` | bool | `false` | Lets a token (`iam`, `gcp_iam`) travel over `ssl_mode = "require"` when the server certificate cannot be verified, as on Cloud SQL's default internal CA.  Never extends to `disable`.  See [Transport security](#transport-security). |

#### Migration history

Section `[component.connect.migrations]`.

PostgreSQL only.  The collector looks for migration-history tables and reads them, so each
migration shows up in DBGorilla's topology next to the schema it produced.
By default it looks for Flyway's `flyway_schema_history` and Alembic's `alembic_version` in every
schema it collects.

| Key | Type | Default | Description |
|---|---|---|---|
| `mode` | string | `auto` | `auto` finds and reads the tables; `disabled` turns migration history off for this component. |
| `adapter` | list of tables | Flyway and Alembic defaults | Which tools to look for, and under which table name.  Each entry has `tool` (`flyway` or `alembic`) and `table`. |

Set `adapter` when you renamed the history table, for example with Flyway's `flyway.table`:

```toml
[component.connect.migrations]
mode = "auto"

[[component.connect.migrations.adapter]]
tool  = "flyway"
table = "schema_versions"

[[component.connect.migrations.adapter]]
tool  = "alembic"
table = "alembic_version"
```

Listing `adapter` replaces the defaults, so list every tool you use.  An unrecognized key in this
block is an error: the collector logs it and skips the component until the key is fixed.

### MySQL

| Key | Type | Default | Description |
|---|---|---|---|
| `host` | string | Required | Database host. |
| `port` | integer | `3306` | Database port. |
| `databases` | list | `[]` | Schemas to monitor.  Empty collects every non-system schema. |
| `exclude_databases` | list | `[]` | Schemas to skip, applied after `databases`. |
| `ssl_mode` | string | `verify_identity` | `disabled`, `required`, `verify_ca`, `verify_identity`.  MySQL's own spelling.  A PostgreSQL value such as `verify-full` is logged as a warning and the database is skipped. |
| `ca_cert` | path | system trust store | CA bundle the server certificate is verified against. |

For MySQL this block covers server verification, and the CA bundle above is the whole of it.  An
unrecognised `ssl_mode` does not stop the collector: it logs a warning naming the value and skips
that database.

Certificate paths are paths inside the container.  Mount the files in and point at them by
absolute path.

---

## Security

<span id="database-commands"></span>Which statements DBGorilla may run on your databases is set by `[commands]`, on
[Database commands](/docs/reference/collector-commands/#database-commands).

### Secret references

Do not store sensitive data in plaintext in `collector.toml`.

Passwords, API keys and the DBGorilla client secret are therefore written as references.  The
collector resolves a reference at connection time, and never writes the resolved value to logs or
telemetry.

| Form | Meaning |
|---|---|
| `"${VAR}"` | Read from environment variable `VAR`. `"${env:VAR}"` is the same. |
| `{ env = "VAR" }` | The same thing, written out. |
| `"${file:/run/secrets/db}"` | Read from a file, trailing newline trimmed. |
| `{ file = "/run/secrets/db" }` | The same thing, written out. |
| `"literal"` | A bare string is an inline plaintext literal.  Local testing only. `{ inline = "literal" }` is the same, written out. |

A reference names where the value comes from; your platform supplies it.  Both forms are
available everywhere the collector runs:

| Platform | Environment reference | File reference |
|---|---|---|
| Docker / Compose | `--env-file`, or `environment:` | Docker secret or a read-only bind mount |
| ECS / Fargate | `secrets` in the task definition, from Secrets Manager or SSM Parameter Store | A mounted volume |
| Cloud Run / Container Instances | Secret-backed environment variables | A mounted secret volume |
| Kubernetes | `env.valueFrom.secretKeyRef` | A projected Secret volume |
| Plain host or systemd | `EnvironmentFile=` | Any file the collector's user can read |

Prefer `--env-file` or a platform secret over an inline `-e` on the command line, where `ps` can
read it.

On Kubernetes with Helm: set `secrets.databasePasswords` and the chart creates both the Secret
and the environment variables your file references.  Or point `secrets.existingSecret` at a Secret
managed elsewhere, for example one synced by External Secrets Operator, and map each database
password variable from it with `extraEnv`, as in [With Helm](#with-helm).

The same rule applies when the whole configuration comes from environment variables.  Keep the
reference in the configuration variable and let your secret store populate the variable it names:

```sh
DBG__COMPONENT__0__AUTH__PASSWORD='${ORDERS_DB_PASSWORD}'   # in the task definition
ORDERS_DB_PASSWORD=...                                      # injected from your secret store
```

Putting the password directly in `DBG__COMPONENT__0__AUTH__PASSWORD` works, but the collector
treats it as an inline literal and logs a warning: the value then lives in the deployment
definition, outside your secret store.

### Transport security

`ssl_mode` controls the connection to your database.  It is separate from the collector's own
outbound TLS to DBGorilla.

| PostgreSQL | MySQL | Behaviour |
|---|---|---|
| `disable` | `disabled` | No encryption. |
| `require` | `required` | Encrypted, certificate not verified.  No protection against interception. |
| `verify-ca` | `verify_ca` | Chain verified against `ca_cert`, hostname not checked.  Trusts anything `ca_cert` signed; put the server's own certificate there to pin that one server. |
| `verify-full` | `verify_identity` | Chain and hostname both verified.  Default, and the recommended setting. |

Two more behaviours:

- When authentication uses a token (`iam`, `gcp_iam`), the collector forces certificate
  verification regardless of `ssl_mode`, so a bearer token is never sent over an unverified
  link.  The one exception is a server whose certificate no certificate authority vouches for:
  set `allow_unverified_token_transport = true` with `ssl_mode = "require"` to accept an
  encrypted, unverified link.  AlloyDB turns this on by default.  A token is never sent
  unencrypted.
- PostgreSQL silently treats an unrecognised `ssl_mode` as `verify-full`.

The collector's own connections to DBGorilla trust the image's system certificate authorities.  A
self-hosted DBGorilla behind a private CA needs that CA added: mount a PEM bundle and point
`SSL_CERT_FILE` at it, or on Kubernetes set the chart's `trustBundle.configMapName` to a ConfigMap
holding the bundle.  The bundle replaces the system roots rather than adding to them, so include
every CA the collector must trust.

### Database privileges

The collector needs a read-only login and nothing more.

- **PostgreSQL**: a role with `pg_monitor`, plus `USAGE` on each schema you want captured and
  `SELECT` on its tables.  Schema capture runs `pg_dump --schema-only`, which reads no rows but
  takes a share lock on every table it dumps, and PostgreSQL allows that lock only with `SELECT`
  on the table.  One table without it fails the whole capture, so `pg_monitor` alone reports
  metrics fine and captures no schema.  Full grants in
  [Database grants](/docs/connect/prepare-postgresql/#database-grants).
- **MySQL**: a different set, including grants for views, triggers, routines and events.  The full
  list, and what each one allows, is in [MySQL](/docs/connect/prepare-mysql/#database-grants).

### Deployment posture

The collector listens on no inbound port, runs as a non-root user with a read-only root
filesystem, and makes only outbound connections.

---

## Schema scanning

Section `[topology]`.

Schema and structure scanning is always on; only the cadence is configurable.

| Key | Type | Default | Description |
|---|---|---|---|
| `interval` | duration | `60s` | How often each database is re-scanned.  Minimum `60s`. |

```toml
[topology]
interval = "5m"
```

---

## Performance tuning

Most deployments do not need to change these defaults.

### Connection pools

Sections `[engine.postgres.pool]` and `[engine.mysql.pool]`.

| Key | Type | Default | Description |
|---|---|---|---|
| `max_connections_per_instance` | integer | `8` | Hard ceiling on concurrent connections to one instance.  Raise to scan a many-database instance faster; lower to be gentler on the target. |
| `per_db_max` | integer | `2` | Warm connections kept per database for the command path only.  Does not scale with database count. |
| `idle_timeout_secs` | integer | `90` | Recycle a pooled connection after this much idle time. |
| `max_lifetime_secs` | integer | `600` | Recycle a pooled connection after this much total age. |

### PostgreSQL metrics connections

Section `[engine.postgres.otel]`.

Metrics-collection connection caps.

| Key | Type | Default | Description |
|---|---|---|---|
| `pg_max_open` | integer | `2` | Maximum open connections per metrics receiver. |
| `pg_keep_warm_connections` | bool | `true` | Keep one connection warm per database between scrapes.  Set `false` on a many-database instance to trade reconnects for a smaller footprint. |
| `pg_max_idle_time_secs` | integer | `60` | Idle recycle time. |
| `pg_max_lifetime_secs` | integer | `600` | Lifetime recycle time. |
| `sqlquery_max_open_conn` | integer | `1` | Maximum open connections per query receiver. |

### MySQL metrics connections

Section `[engine.mysql.otel]`.

| Key | Type | Default | Description |
|---|---|---|---|
| `mysql_max_open` | integer | `2` | Maximum open connections per metrics receiver.  Two receivers run per node. |
| `mysql_keep_warm_connections` | bool | `true` | Keep a connection warm between scrapes. |
| `mysql_max_idle_time_secs` | integer | `60` | Idle recycle time. |
| `mysql_max_lifetime_secs` | integer | `600` | Lifetime recycle time. |

### Bundled metrics collector

Section `[otelcol]`.

The bundled metrics-collector process.  Change only if instructed by support.

| Key | Type | Default | Description |
|---|---|---|---|
| `binary` | string | `dbg_otelcol` | Path to, or name on `PATH` of, the collector binary. |
| `config_dir` | path | system temp directory | Where the generated collector config is written. |

---

## Logs

The collector writes JSON logs to standard output.  Set `DBG_LOG_FORMAT=text` on the container for
plain, readable lines when running it by hand.

---

## Startup validation

The collector refuses to start on any of these, naming the problem:

- `dbgorilla.agent_id` missing, or not a UUID.
- `dbgorilla.tenant_id` missing.
- `dbgorilla.secret` pointing at an environment variable or file that does not exist.
- No `[[component]]` configured.
- Two components sharing a `name`.
- A `[component.provider]` with no `type`, or a `[component.auth]` with no `method`.
- An engine, provider and auth combination outside the supported set.
- `topology.interval` below `60s`.

An unrecognized key in a MySQL `[component.connect]` block is logged as a warning and ignored, so
a typo is visible rather than silently inert.

## PostgreSQL example

### Configuration

The example below runs one collector against one PostgreSQL cluster
(`db-cluster1.internal.example.com`), restricted to a single database.

The agent ID and secret are issued per collector; the tenant ID identifies your organization and is
shared across all of its collectors.  All three are shown when you add a collector in the app.

:::caution
The secret is shown once and cannot be recovered afterward.  Capture it when it is displayed.
:::

Keep secrets out of the file.  Reference them as `${VAR}` and supply the values as environment
variables, from whatever secrets manager you already use.

```toml
[dbgorilla]
agent_id  = "a8c1cde3-3e91-4ecf-b615-26bae0c35f02"
tenant_id = "fd4c6676-c1a1-4d9a-babb-54958c26369d"
secret    = "${DBG_SERVER_SECRET}"

# One [[component]] block per database system to monitor.
[[component]]
name   = "example_db"        # the name your system appears under in DBGorilla
engine = "postgres"

[component.provider]
type = "self_hosted"

[component.auth]
method   = "password"
user     = "dbg_readonly"
password = "${DB_PASSWORD}"

[component.connect]
host      = "db-cluster1.internal.example.com"
port      = 5432
databases = ["example_db"]   # empty = all non-template databases
ssl_mode  = "verify-full"    # disable | require | verify-ca | verify-full

[commands]
enabled = true
allowed = ["explain", "collect_statistics"]   # see https://www.dbgorilla.com/docs/reference/collector-commands/#database-commands
```

#### Choosing `ssl_mode`

`verify-full` is the right choice whenever the database is reached over a network, and it works
against managed databases whose certificates chain to a public authority.

If your server presents a certificate from an internal authority, which is what most
Kubernetes-operator-managed PostgreSQL does, then `verify-full` and `verify-ca` fail on trust, because
the container has no reason to hold your CA.  Either mount your CA into the container and point
`ca_cert` at it, or use `require`, which keeps the connection encrypted but skips certificate
verification.

Use `disable` only for a database on the same machine as the collector.  Over a network it sends the
password and every query in clear text.

## MySQL example

### Configuration

The `[[component]]` block is the same shape as PostgreSQL's, with `engine = "mysql"`, port 3306, and
MySQL's own SSL vocabulary.

```toml
[dbgorilla]
agent_id  = "a8c1cde3-3e91-4ecf-b615-26bae0c35f02"
tenant_id = "fd4c6676-c1a1-4d9a-babb-54958c26369d"
secret    = "${DBG_SERVER_SECRET}"

[[component]]
name   = "staging-mysql"
engine = "mysql"

[component.provider]
type = "self_hosted"

[component.auth]
method   = "password"
user     = "dbg_monitor"
password = "${MYSQL_PASSWORD}"

[component.connect]
host      = "mysql.internal"
port      = 3306
databases = ["app"]
ssl_mode  = "verify_identity"   # disabled | required | verify_ca | verify_identity (default)

[commands]
enabled = true
allowed = ["explain", "collect_statistics"]   # see https://www.dbgorilla.com/docs/reference/collector-commands/#database-commands
```

`[commands]` behaves the same on MySQL as on PostgreSQL.  To turn either off, remove it from `allowed` or set
`enabled = false`, then restart the collector.  See
[Database commands](/docs/reference/collector-commands/#database-commands).

Complete files are on [Self-managed MySQL](/docs/connect/self-managed/#self-managed-mysql) and
[AWS RDS for MySQL](/docs/connect/aws-rds-aurora/#aws-rds-for-mysql).

:::danger[`ssl_mode` does not use the PostgreSQL spellings, and the key name matters too]

MySQL uses its own `--ssl-mode` vocabulary: `disabled`, `required`, `verify_ca`, `verify_identity`.
They use underscores where PostgreSQL uses hyphens.  The default is `verify_identity`, the strictest of the
four.

A wrong value and a wrong key name both leave the collector running.  Neither stops it:

| What you write | What happens |
|---|---|
| `ssl_mode = "verify-full"` | The collector keeps running and **skips that database**.  Each pass logs a warning naming the bad value and listing the four valid ones. |
| `sslmode = "verify-full"` | Unknown key.  Logged as a warning, then **ignored**, and you silently get the `verify_identity` default. |

Either mistake leaves a collector that looks healthy and monitors less than you expect.  Check the
log for "unrecognised ssl_mode" or "ignoring unrecognised key".
:::

## Recipes

The recipes are on [Self-managed server](/docs/connect/self-managed/), [AWS RDS and Aurora](/docs/connect/aws-rds-aurora/), [Google Cloud SQL and AlloyDB](/docs/connect/google-cloud/), [Kubernetes](/docs/connect/kubernetes/), [CloudNativePG](/docs/connect/cloudnativepg/) and [Instaclustr](/docs/connect/instaclustr/).

Each recipe is a complete `collector.toml` for one common setup.  Replace the
placeholders, keep the secret references as they are, and run it with the command underneath.

The database login and its grants are on
[Prepare PostgreSQL](/docs/connect/prepare-postgresql/#database-grants)
for PostgreSQL and [Prepare MySQL](/docs/connect/prepare-mysql/) for MySQL.  Which commands to
allow is under [Database commands](/docs/reference/collector-commands/#database-commands).