---
title: Install & configure NRDOT Collector for self-hosted PostgreSQL monitoring
source: https://docs.newrelic.com/docs/opentelemetry/database/postgresql/hosted
---

> #### 💡 PREVIEW
>
> We're still working on this feature, but we'd love for you to try it out!
>
> This feature is currently provided as part of a preview pursuant to our [pre-release policies](https://docs.newrelic.com/docs/licenses/license-information/referenced-policies/new-relic-pre-release-policy/).

Set up PostgreSQL monitoring using the NRDOT Collector on self-hosted environments including physical servers, virtual machines, and standalone installations.

## Prerequisites [#prerequisites]

Before you install, make sure you have:

-   PostgreSQL `14` and above
-   A New Relic [license key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#ingest-license-key).
-   Administrative access to your PostgreSQL instance.
-   Network connectivity between the host where you install the NRDOT Collector and your PostgreSQL database.
-   Network connectivity to [New Relic OTLP endpoints documentation](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/)
-   This integration is available as a part of New Relic public preview program. Check with your Organization Manager to opt in from the [Previews & Trials](https://one.newrelic.com/admin-portal/promotion-management/home) page.

For supported PostgreSQL versions, required grants, and recommended server parameters, see [Compatibility and prerequisites](https://docs.newrelic.com/docs/opentelemetry/database/postgresql/compatibility).

## Set up NRDOT Collector [#setup]

Install the NRDOT Collector on your system:

**For AMD64 architecture**

-   For Debian/Ubuntu system, run:

    ```bash
    NRDOT_VERSION=$(curl -s https://api.github.com/repos/newrelic/nrdot-collector-releases/releases/latest | grep '"tag_name":' | awk -F'"' '{print $4}') && curl -L "https://github.com/newrelic/nrdot-collector-releases/releases/download/${NRDOT_VERSION}/nrdot-collector_${NRDOT_VERSION}_linux_amd64.deb" --output nrdot-collector.deb && sudo dpkg -i nrdot-collector.deb
    ```
-   For RHEL/CentOS/OEL system, run:

    ```bash
    NRDOT_VERSION=$(curl -s https://api.github.com/repos/newrelic/nrdot-collector-releases/releases/latest | grep '"tag_name":' | awk -F'"' '{print $4}') && curl -L "https://github.com/newrelic/nrdot-collector-releases/releases/download/${NRDOT_VERSION}/nrdot-collector_${NRDOT_VERSION}_linux_x86_64.rpm" --output nrdot-collector.rpm && sudo rpm -ivh nrdot-collector.rpm
    ```

**For ARM64 architecture**

-   For Debian/Ubuntu system, run:

    ```bash
    NRDOT_VERSION=$(curl -s https://api.github.com/repos/newrelic/nrdot-collector-releases/releases/latest | grep '"tag_name":' | awk -F'"' '{print $4}') && curl -L "https://github.com/newrelic/nrdot-collector-releases/releases/download/${NRDOT_VERSION}/nrdot-collector_${NRDOT_VERSION}_linux_arm64.deb" --output nrdot-collector.deb && sudo dpkg -i nrdot-collector.deb
    ```

-   For RHEL/CentOS/OEL system, run:

    ```bash
    NRDOT_VERSION=$(curl -s https://api.github.com/repos/newrelic/nrdot-collector-releases/releases/latest | grep '"tag_name":' | awk -F'"' '{print $4}') && curl -L "https://github.com/newrelic/nrdot-collector-releases/releases/download/${NRDOT_VERSION}/nrdot-collector_${NRDOT_VERSION}_linux_aarch64.rpm" --output nrdot-collector.rpm && sudo rpm -ivh nrdot-collector.rpm
    ```

## Configure database user and grants [#database-user]

Create a monitoring user with the necessary privileges for your PostgreSQL instance.

**To create the monitoring user and grants:**

1.  Connect to your PostgreSQL instance as a superuser or another user with required permissions:

    ```shell
    sudo -u postgres psql --dbname=postgres --port=5432
    ```

2.  Create the monitoring user:

    ```sql
    CREATE USER <YOUR_DB_USERNAME> WITH LOGIN PASSWORD '<YOUR_DB_PASSWORD>';
    ```

3.  Grant the monitoring user role membership as needed:

-   If you're on PostgreSQL 15 and above, assign role to inherit privileges:

    ```sql
    ALTER ROLE <YOUR_DB_USERNAME> INHERIT;
    ```

    > #### 💡 TIP
    >
    > If you're on PostgreSQL 14, the monitoring user inherits privileges by default, so you can skip this step.

-   Create the following schema and grants in every database you want to monitor:

    ```sql
    CREATE SCHEMA IF NOT EXISTS otel;
    GRANT USAGE ON SCHEMA otel TO <YOUR_DB_USERNAME>;
    GRANT USAGE ON SCHEMA public TO <YOUR_DB_USERNAME>;
    GRANT SELECT ON ALL TABLES IN SCHEMA public TO <YOUR_DB_USERNAME>;
    GRANT pg_monitor TO <YOUR_DB_USERNAME>;
    CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
    ```

-   (Optional) To collect vector metrics, create the `pgvector` extension in each database:

    ```sql
    CREATE EXTENSION IF NOT EXISTS vector;
    ```

    The `l1`, `hamming`, and `jaccard` distance functions require `pgvector` version `0.7.0` or above.

-   (Optional) To verify the connection and grants, run:

    ```shell
    psql --username=<YOUR_DB_USERNAME> --host=localhost --port=5432 --dbname=<YOUR_DATABASE_NAME> --command '\conninfo'
    psql --username=<YOUR_DB_USERNAME> --host=localhost --port=5432 --dbname=<YOUR_DATABASE_NAME> \
      -c "SELECT * FROM pg_stat_database LIMIT 1;" && echo "pg_stat_database OK"
    psql --username=<YOUR_DB_USERNAME> --host=localhost --port=5432 --dbname=<YOUR_DATABASE_NAME> \
      -c "SELECT * FROM pg_stat_activity LIMIT 1;" && echo "pg_stat_activity OK"
    psql --username=<YOUR_DB_USERNAME> --host=localhost --port=5432 --dbname=<YOUR_DATABASE_NAME> \
      -c "SELECT * FROM pg_stat_statements LIMIT 1;" && echo "pg_stat_statements OK"
    ```

    If each command completes without an error, the monitoring user, host, and port are all correct.

## Configure NRDOT Collector [#configure-collector]

Configure the NRDOT Collector with your PostgreSQL-specific settings. Select your configuration type based on your monitoring needs:

**Standard configuration**

This configuration focuses on essential PostgreSQL monitoring with the `nrpostgresql` receiver only.

1.  Create a configuration file named as `postgresql-config.yaml`:

    ```bash
      sudo nano /etc/nrdot-collector/postgresql-config.yaml
    ```

2.  Add the following configuration to the `postgresql-config.yaml` file you created in the previous step.

```yaml
receivers:
  nrpostgresql:
    endpoint: "<YOUR_DB_HOST>:<YOUR_DB_PORT>"
    username: "<YOUR_DB_USERNAME>"
    password: "<YOUR_DB_PASSWORD>"
    databases:
      - <YOUR_DATABASE_NAME>
    collection_interval: 15s
    events:
      db.server.top_query:
        enabled: true
      db.server.query_sample:
        enabled: true
    top_query_collection:
      max_rows_per_query: 1000
      top_n_query: 200
      collection_interval: 60s
      allowed_comment_keys: [nr_service_guid]
    query_sample_collection:
      max_rows_per_query: 1000
      allowed_comment_keys: [nr_service_guid]
    # Metrics needed for the out-of-the-box dashboard but disabled by default —
    # see "Available metrics" below for the full default-on/default-off breakdown.
    metrics:
      postgresql.database.locks:
        enabled: true
      postgresql.deadlocks:
        enabled: true
      postgresql.function.calls:
        enabled: true
      postgresql.query.conflicts:
        enabled: true
      postgresql.sequential_scans:
        enabled: true
      postgresql.temp.io:
        enabled: true
      postgresql.temp_files:
        enabled: true

processors:
  batch:
  # Static server.address/server.port instead of deriving them from
  # service.instance.id — with receiver.nrpostgresql.useOTelSemconv enabled,
  # service.instance.id becomes a UUID v5 hash, not a "host:port" string, so
  # a Split(...)-based transform breaks. Applies uniformly to both metrics
  # and logs pipelines since neither depends on that internal format.
  resource/postgresql:
    attributes:
      - key: server.address
        value: "<YOUR_DB_HOST>"
        action: upsert
      - key: server.port
        value: <YOUR_DB_PORT>
        action: upsert

exporters:
  otlp/newrelic:
    endpoint: "<YOUR_NEWRELIC_OTLP_ENDPOINT>"
    headers:
      api-key: "<YOUR_NEWRELIC_LICENSE_KEY>"
    compression: gzip
    retry_on_failure:
      enabled: true
      initial_interval: 5s
      max_interval: 30s
      max_elapsed_time: 300s

service:
  pipelines:
    metrics:
      receivers: [nrpostgresql]
      processors: [resource/postgresql, batch]
      exporters: [otlp/newrelic]
    logs:
      receivers: [nrpostgresql]
      processors: [resource/postgresql, batch]
      exporters: [otlp/newrelic]
```

**Full-feature configuration**

This configuration adds host-level metrics such as CPU, memory, disk, network, and more alongside PostgreSQL database monitoring.

1.  Create a configuration file named as `postgresql-config.yaml`:

    ```bash
      sudo nano /etc/nrdot-collector/postgresql-config.yaml
    ```

2.  Add the following configuration to the `postgresql-config.yaml` file you created in the previous step.

```yaml
extensions:
  health_check:

receivers:
  otlp:
    protocols:
      grpc:
      http:

  host_metrics:
    collection_interval: 30s
    scrapers:
      cpu:
        metrics:
          system.cpu.time:
            enabled: false
          system.cpu.utilization:
            enabled: true
      load:
      memory:
        metrics:
          system.memory.utilization:
            enabled: true
      paging:
        metrics:
          system.paging.utilization:
            enabled: false
          system.paging.faults:
            enabled: false
      filesystem:
        metrics:
          system.filesystem.utilization:
            enabled: true
      disk:
        metrics:
          system.disk.merged:
            enabled: false
          system.disk.pending_operations:
            enabled: false
          system.disk.weighted_io_time:
            enabled: false
      network:
        metrics:
          system.network.connections:
            enabled: false
      # Uncomment to enable process metrics
      # processes:
      # process:
      #  metrics:
      #    process.cpu.utilization:
      #      enabled: true
      #    process.cpu.time:
      #      enabled: false

  nrpostgresql:
    endpoint: "<YOUR_DB_HOST>:<YOUR_DB_PORT>"
    username: "<YOUR_DB_USERNAME>"
    password: "<YOUR_DB_PASSWORD>"
    databases:
      - <YOUR_DATABASE_NAME>
    collection_interval: 15s
    events:
      db.server.top_query:
        enabled: true
      db.server.query_sample:
        enabled: true
    query_sample_collection:
      max_rows_per_query: 1000
      allowed_comment_keys: [nr_service_guid]
    top_query_collection:
      max_rows_per_query: 1000
      top_n_query: 200
      collection_interval: 60s
      allowed_comment_keys: [nr_service_guid]
    # Metrics needed for the out-of-the-box dashboard but disabled by default —
    # see "Available metrics" in the docs for the full default-on/default-off breakdown.
    metrics:
      postgresql.database.locks:
        enabled: true
      postgresql.deadlocks:
        enabled: true
      postgresql.function.calls:
        enabled: true
      postgresql.query.conflicts:
        enabled: true
      postgresql.sequential_scans:
        enabled: true
      postgresql.temp.io:
        enabled: true
      postgresql.temp_files:
        enabled: true

processors:
  metrics_transform:
    transforms:
      - include: system.cpu.utilization
        action: update
        operations:
          - action: aggregate_labels
            label_set: [state]
            aggregation_type: mean
      - include: system.paging.operations
        action: update
        operations:
          - action: aggregate_labels
            label_set: [direction]
            aggregation_type: sum

  filter/exclude_cpu_utilization:
    metrics:
      datapoint:
        - 'metric.name == "system.cpu.utilization" and attributes["state"] == "interrupt"'
        - 'metric.name == "system.cpu.utilization" and attributes["state"] == "nice"'
        - 'metric.name == "system.cpu.utilization" and attributes["state"] == "softirq"'

  filter/exclude_memory_utilization:
    metrics:
      datapoint:
        - 'metric.name == "system.memory.utilization" and attributes["state"] == "slab_unreclaimable"'
        - 'metric.name == "system.memory.utilization" and attributes["state"] == "inactive"'
        - 'metric.name == "system.memory.utilization" and attributes["state"] == "cached"'
        - 'metric.name == "system.memory.utilization" and attributes["state"] == "buffered"'
        - 'metric.name == "system.memory.utilization" and attributes["state"] == "slab_reclaimable"'

  filter/exclude_memory_usage:
    metrics:
      datapoint:
        - 'metric.name == "system.memory.usage" and attributes["state"] == "slab_unreclaimable"'
        - 'metric.name == "system.memory.usage" and attributes["state"] == "inactive"'

  filter/exclude_filesystem_utilization:
    metrics:
      datapoint:
        - 'metric.name == "system.filesystem.utilization" and attributes["type"] == "squashfs"'

  filter/exclude_filesystem_usage:
    metrics:
      datapoint:
        - 'metric.name == "system.filesystem.usage" and attributes["type"] == "squashfs"'
        - 'metric.name == "system.filesystem.usage" and attributes["state"] == "reserved"'

  filter/exclude_filesystem_inodes_usage:
    metrics:
      datapoint:
        - 'metric.name == "system.filesystem.inodes.usage" and attributes["type"] == "squashfs"'
        - 'metric.name == "system.filesystem.inodes.usage" and attributes["state"] == "reserved"'

  filter/exclude_system_disk:
    metrics:
      datapoint:
        - 'metric.name == "system.disk.operations" and IsMatch(attributes["device"], "^loop.*") == true'
        - 'metric.name == "system.disk.merged" and IsMatch(attributes["device"], "^loop.*") == true'
        - 'metric.name == "system.disk.io" and IsMatch(attributes["device"], "^loop.*") == true'
        - 'metric.name == "system.disk.io_time" and IsMatch(attributes["device"], "^loop.*") == true'
        - 'metric.name == "system.disk.operation_time" and IsMatch(attributes["device"], "^loop.*") == true'

  filter/exclude_network:
    metrics:
      datapoint:
        - 'IsMatch(metric.name, "^system.network.*") == true and attributes["device"] == "lo"'

  attributes/exclude_system_paging:
    include:
      match_type: strict
      metric_names:
        - system.paging.operations
    actions:
      - key: type
        action: delete

  cumulative_to_delta:

  transform:
    trace_statements:
      - context: span
        statements:
          - truncate_all(span.attributes, 4095)
          - truncate_all(resource.attributes, 4095)
          
  transform/host:
    metric_statements:
      - context: metric
        statements:
          - set(metric.description, "")
          - set(metric.unit, "")

  # Static server.address/server.port instead of deriving them from
  # service.instance.id — with receiver.nrpostgresql.useOTelSemconv enabled,
  # service.instance.id becomes a UUID v5 hash, not a "host:port" string, so
  # a Split(...)-based transform breaks. Applies uniformly to both metrics
  # and logs pipelines since neither depends on that internal format.
  resource/postgresql:
    attributes:
      - key: server.address
        value: "<YOUR_DB_HOST>"
        action: upsert
      - key: server.port
        value: <YOUR_DB_PORT>
        action: upsert

  batch:

  resource_detection:
    detectors: ["system"]
    system:
      hostname_sources: ["os"]
      resource_attributes:
        host.id:
          enabled: true

  resource_detection/cloud:
    detectors: ["gcp", "ec2", "azure"]
    timeout: 2s
    override: true

  resource_detection/env:
    detectors: ["env"]
    timeout: 2s
    override: true

exporters:
  otlp/newrelic:
    endpoint: "<YOUR_NEWRELIC_OTLP_ENDPOINT>"
    headers:
      api-key: "<YOUR_NEWRELIC_LICENSE_KEY>"
    compression: gzip
    retry_on_failure:
      enabled: true
      initial_interval: 5s
      max_interval: 30s
      max_elapsed_time: 300s

service:
  telemetry:
    metrics:
      level: none

  extensions: [health_check]

  pipelines:
    metrics/host:
      receivers: [host_metrics]
      processors:
        - metrics_transform
        - filter/exclude_cpu_utilization
        - filter/exclude_memory_utilization
        - filter/exclude_memory_usage
        - filter/exclude_filesystem_utilization
        - filter/exclude_filesystem_usage
        - filter/exclude_filesystem_inodes_usage
        - filter/exclude_system_disk
        - filter/exclude_network
        - attributes/exclude_system_paging
        - transform/host
        - resource_detection
        - resource_detection/cloud
        - resource_detection/env
        - cumulative_to_delta
        - batch
      exporters: [otlp/newrelic]

    metrics/postgresql:
      receivers: [nrpostgresql]
      processors:
        - resource/postgresql
        - resource_detection
        - resource_detection/cloud
        - resource_detection/env
        - batch
      exporters: [otlp/newrelic]

    logs/postgresql:
      receivers: [nrpostgresql]
      processors:
        - resource/postgresql
        - resource_detection
        - resource_detection/cloud
        - resource_detection/env
        - batch
      exporters: [otlp/newrelic]

    traces:
      receivers: [otlp]
      processors: [transform, resource_detection, resource_detection/cloud, resource_detection/env, batch]
      exporters: [otlp/newrelic]

    metrics:
      receivers: [otlp]
      processors: [resource_detection, resource_detection/cloud, resource_detection/env, batch]
      exporters: [otlp/newrelic]

    logs:
      receivers: [otlp]
      processors: [resource_detection, resource_detection/cloud, resource_detection/env, batch]
      exporters: [otlp/newrelic]
```

> #### ⚠️ IMPORTANT
>
> PostgreSQL integration via NRDOT Collector follows standard [data ingest pricing](https://docs.newrelic.com/docs/accounts/accounts-billing/new-relic-one-pricing-billing/data-ingest-billing/#data). Note that full feature configuration ingests more data than a standard setup, which may increase your overall bill.

### Configuration parameters

The following table describes the key configuration parameters for the `nrpostgresql` receiver:

| Parameter                       | Description                                                                                                                                                               |
| ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `<YOUR_DB_HOST>`                | Enter your PostgreSQL Database hostname.                                                                                                                                  |
| `<YOUR_DB_PORT>`                | Enter your PostgreSQL Database port.                                                                                                                                      |
| `<YOUR_DB_USERNAME>`            | Enter your database username.                                                                                                                                             |
| `<YOUR_DB_PASSWORD>`            | Enter your database password.                                                                                                                                             |
| `<YOUR_DATABASE_NAME>`          | Enter the name of the database you want to monitor.                                                                                                                       |
| `<YOUR_NEWRELIC_OTLP_ENDPOINT>` | Enter the New Relic OTLP endpoint. For more information, see [New Relic OTLP endpoints](https://docs.newrelic.com/docs/opentelemetry/best-practices/opentelemetry-otlp/). |
| `<YOUR_NEWRELIC_LICENSE_KEY>`   | Enter your New Relic [license key](https://docs.newrelic.com/docs/apis/intro-apis/new-relic-api-keys/#ingest-license-key).                                                |
| `collection_interval`           | Enter the interval between metric scrapes. Default: `15s`.                                                                                                                |

> #### 💡 TIP
>
> You can also:
>
> -   [Configure multiple receivers](https://docs.newrelic.com/docs/opentelemetry/database/postgresql/multi-receiver): To monitor multiple PostgreSQL instances from one collector.
> -   [Link your PostgreSQL database with APM](https://docs.newrelic.com/docs/opentelemetry/database/capabilities/db-apm): To correlate your application performance with database operations. This allows you to see exactly which applications are generating specific database workloads.
> -   [EXPLAIN plan permissions](https://docs.newrelic.com/docs/opentelemetry/database/postgresql/explain-permissions): To collect query plans for locking or write statements without granting write access to the monitoring user.
> -   [Set up secret management](https://docs.newrelic.com/docs/opentelemetry/database/capabilities/db-apm/#secret-management): To securely manage sensitive information, such as database credentials. This helps to enhance the security of your monitoring setup by avoiding hardcoding sensitive data in configuration files.

## Validate NRDOT Collector configuration [#validate]

1.  Update the config path to point to your new `postgresql-config.yaml` file:

    ```bash
      sudo sed -i 's|OTELCOL_OPTIONS="--config=/etc/nrdot-collector/config.yaml"|OTELCOL_OPTIONS="--config=/etc/nrdot-collector/postgresql-config.yaml"|' /etc/nrdot-collector/nrdot-collector.conf
    ```

2.  Validate the NRDOT Collector configuration to ensure it's correctly formatted and will work properly:

    ```bash
      sudo /usr/bin/nrdot-collector validate --config=/etc/nrdot-collector/postgresql-config.yaml
    ```

## Restart NRDOT Collector [#restart-collector]

After configuring the collector, restart the NRDOT Collector service to apply the changes:

```bash
sudo systemctl restart nrdot-collector
```

To verify that the collector is running properly, check the service status:

```bash
sudo systemctl status nrdot-collector
```

> #### 💡 TIP
>
> Always restart the NRDOT Collector after making configuration changes to ensure the new settings take effect.

## Find and use your data [#find-use-data]

Query samples and top queries arrive as log events (`event.name = 'db.server.query_sample'` / `'db.server.top_query'`, `db.system.name = 'postgresql'`). Metrics arrive under the `postgresql.*` namespace.

To find your PostgreSQL database entity in New Relic:

1.  Go to **<https://one.newrelic.com> > All Capabilities > Databases**.
2.  From the **Entity type** dropdown, select **PostgreSQL instance**, then click **Apply**.
3.  Select your PostgreSQL database from the list of entities.

## Related documentation [#related]

[Link your PostgreSQL database with APM](https://docs.newrelic.com/docs/opentelemetry/database/capabilities/db-apm)

Learn how to correlate your application performance with database operations.

[Troubleshooting guide](https://docs.newrelic.com/docs/opentelemetry/database/postgresql/troubleshooting)

Learn how to troubleshoot common issues with PostgreSQL monitoring.

[Metrics reference](https://docs.newrelic.com/docs/opentelemetry/database/postgresql/metrics-reference)

Learn about the available metrics collected by the NRDOT Collector.
