# TimescaleDB TimescaleDB

Source: https://userguide.qualytics.io/source-datastore/datastore/jdbc/timescale-db/

Adding and configuring a TimescaleDB connection within Qualytics empowers the platform to build a symbolic link with your schema to perform operations like data discovery, visualization, reporting, syncing, profiling, scanning, anomaly surveillance, and more.

This documentation provides a step-by-step guide on adding TimescaleDB as a source datastore in Qualytics. It covers the entire process, from initial connection setup to testing and finalizing the configuration.

By following these instructions, enterprises can ensure their TimescaleDB environment is properly connected with Qualytics, unlocking the platform’s potential to help you proactively manage your full data quality lifecycle.

![timescaledb-connection-form](https://userguide.qualytics.io/assets/source-datastores/datastore/jdbc/timescale-db/overview/timescaledb-connection-form.png)

Let’s get started 🚀

## TimescaleDB Setup Guide

Qualytics connects to TimescaleDB through the **PostgreSQL JDBC driver**. TimescaleDB is a PostgreSQL extension, so the permission model follows the same PostgreSQL conventions. Qualytics uses the standard JDBC metadata APIs both to list the schemas the role can see and to discover tables, including hypertables, along with their columns and primary keys. Qualytics automatically filters out TimescaleDB internal schemas (`timescaledb_information`, `timescaledb_experimental`) during schema discovery.

### Minimum TimescaleDB Permissions (Source Datastore)

| Permission | Purpose |
| --- | --- |
| `CONNECT ON DATABASE` | Allow the role to connect to the target database |
| `USAGE ON SCHEMA <schema_name>` | Access objects within the target schema |
| `SELECT ON ALL TABLES IN SCHEMA` | Read data from all existing tables (including hypertables) for profiling and scanning |
| `SELECT ON ALL SEQUENCES IN SCHEMA` | Read sequence metadata for incremental field detection |

> **Note**
>
> Qualytics cannot write to TimescaleDB, so a TimescaleDB datastore cannot be used as an enrichment datastore. Use an enrichment datastore on another connector instead.

### Example: Source Datastore Role (Read-Only)

Replace `<database_name>`, `<schema_name>`, and `<password>` with your actual values.

```sql
-- Create a dedicated read-only role
CREATE ROLE qualytics_read_role LOGIN PASSWORD ‘<password>’;

-- Grant connection and schema access
GRANT CONNECT ON DATABASE <database_name> TO qualytics_read_role;
GRANT USAGE ON SCHEMA <schema_name> TO qualytics_read_role;

-- Grant read access to all existing and future tables (including hypertables)
GRANT SELECT ON ALL TABLES IN SCHEMA <schema_name> TO qualytics_read_role;
GRANT SELECT ON ALL SEQUENCES IN SCHEMA <schema_name> TO qualytics_read_role;
ALTER DEFAULT PRIVILEGES IN SCHEMA <schema_name> GRANT SELECT ON TABLES TO qualytics_read_role;
```

> **Note**
>
> Qualytics automatically filters out system schemas (`pg_catalog`, `pg_toast`, `pg_internal`, `information_schema`, `timescaledb_information`, `timescaledb_experimental`) during schema discovery. You do not need to restrict access to these schemas manually.

### Troubleshooting Common Errors

| Error | Likely Cause | Fix |
| --- | --- | --- |
| `FATAL: password authentication failed` | Incorrect username or password | Verify the credentials and ensure the role exists with `\du` in psql |
| `FATAL: no pg_hba.conf entry for host` | The TimescaleDB server does not allow connections from the Qualytics host IP | Add the Qualytics IP to `pg_hba.conf` and reload the configuration |
| `permission denied for schema` | The role lacks `USAGE` on the target schema | Run `GRANT USAGE ON SCHEMA <schema_name> TO <role>` |
| `permission denied for table` | The role lacks `SELECT` on one or more tables | Run `GRANT SELECT ON ALL TABLES IN SCHEMA <schema_name> TO <role>` |
| `relation does not exist` | The hypertable or table name is incorrect, or the user cannot see it | Verify the table exists with `\dt` in psql and check schema permissions |

### Detailed Troubleshooting Notes

#### Authentication Errors

The error `FATAL: password authentication failed` indicates that the credentials provided are incorrect or the role does not exist.

Common causes:

- **Incorrect password**: the password does not match the one set for the role.
- **Role does not exist**: the role name was misspelled or was never created.
- **Authentication method mismatch**: the `pg_hba.conf` file requires a different authentication method (e.g., `md5` vs `scram-sha-256`).

> **Note**
>
> TimescaleDB uses the same authentication system as PostgreSQL. Check `pg_hba.conf` and PostgreSQL server logs for detailed error information.

#### Permission Errors

The error `permission denied for schema` or `permission denied for table` means the role authenticated successfully but lacks the necessary grants.

Common causes:

- **Missing `USAGE` on schema**: the role cannot access the schema even if table-level grants exist.
- **Missing `SELECT` on tables**: the role has schema access but cannot read specific tables or hypertables.
- **Default privileges not set**: new tables created after the initial grant are not automatically accessible. Use `ALTER DEFAULT PRIVILEGES` to fix this.

#### Connection Errors

The error `FATAL: no pg_hba.conf entry for host` means the TimescaleDB server does not recognize the Qualytics host IP.

Common causes:

- **IP not whitelisted**: the Qualytics server IP is not listed in `pg_hba.conf`.
- **Wrong database name**: the `pg_hba.conf` entry restricts access to specific databases.
- **SSL required**: the server requires SSL connections but the client is connecting without SSL.

> **Tip**
>
> Start by confirming credentials are valid (authentication errors), then verify schema/table permissions (permission errors), and finally check network connectivity (connection errors).

## Add a Source Datastore

A source datastore is a storage location Qualytics connects to so it can profile, scan, and monitor data. Adding TimescaleDB as a source lets Qualytics query it over JDBC with the PostgreSQL driver and run quality operations on the tables it discovers.

Before you start, review the [Minimum TimescaleDB Permissions](https://userguide.qualytics.io/source-datastore/datastore/jdbc/timescale-db/#minimum-timescaledb-permissions-source-datastore) the connecting role needs.

> **Warning: The destination lives on another connector**
>
> Qualytics cannot write to TimescaleDB, so a TimescaleDB datastore cannot be used as an enrichment datastore. Create one on a supported connector, then link it as the TimescaleDB datastore's destination. See [Supported Connectors](https://userguide.qualytics.io/source-datastore/enrichment-datastore/supported-connectors/) for the list.

### Field reference

The **Add Datastore** page shows the sections below when TimescaleDB is selected. When reusing an existing connection, the **Connection Properties** and **Secrets Management** sections come already filled in and read-only: Qualytics has already validated those credentials, so you fill in only the **Location** and the **General** fields. To change a saved connection's credentials, edit the connection through the [Manage Connections](https://userguide.qualytics.io/settings/connections/manage-connections/) page; edits there apply to every datastore that reuses the connection.

#### Connection Properties

These fields define the TimescaleDB server Qualytics connects to and the role it connects with. They belong to the connection: when reusing an existing connection, they come already filled in and read-only.

| Field | Required | Type | Description |
| --- | --- | --- | --- |
| **Connection Name** | Yes | Text | A label for the saved connection (e.g., `acme_timescale_reporting`), so other datastores can reuse it later. |
| **Host** | Yes | Text | The hostname or address of the TimescaleDB server. |
| **Port** | Yes | Number | The port the instance listens on. Defaults to `5432`. |
| **User** | Yes | Text | The role Qualytics connects as. |
| **Password** | Yes | Text | The password for that role. |
| **Max Parallelization** | No | Number | Caps how many queries Qualytics runs at the same time against a datastore reached through this connection, from 1 to 100. Leave it empty for no explicit limit. The cap applies per datastore, so datastores sharing this connection each get their own allowance. Sits in the collapsed **Advanced** section of the form. See [Max Parallelization](https://userguide.qualytics.io/source-datastore/datastore/connection/how-it-works/#max-parallelization). |

#### Secrets Management

This group is optional: use it only if you want Qualytics to pull credentials from a secrets manager instead of typing them into the form. Turn on **HashiCorp Vault** to show the fields below. Despite the label, any secrets manager that exposes a compatible REST API works, not only HashiCorp Vault; see [HashiCorp Vault](https://userguide.qualytics.io/source-datastore/datastore/connection/hashicorp-vault/deep-dive/introduction/). It also belongs to the connection: read-only when reusing an existing connection.

| Field | Required | Type | Description |
| --- | --- | --- | --- |
| **Login URL** | Yes | Text | The full URL of the login endpoint (e.g., `https://vault.example.com/v1/auth/approle/login`). With Vault namespaces, add the namespace after `/v1/`, as in `/v1/<namespace>/auth/approle/login`. |
| **Credentials Payload** | Yes | Text | A JSON body containing the credentials Vault expects (e.g., `{"role_id":"...","secret_id":"..."}`). |
| **Token JSONPath** | Yes | Text | The JSONPath that extracts the client token from Vault's response. Defaults to `$.auth.client_token`. |
| **Secret URL** | Yes | Text | The full URL of the secret, including `https://` and the host (e.g., `https://vault.example.com/v1/secret/data/timescale`). With Vault namespaces, add the namespace after `/v1/`. |
| **Token Header Name** | Yes | Text | The HTTP header name used to send the token. Defaults to `X-Vault-Token`. |
| **Data JSONPath** | Yes | Text | The JSONPath that extracts the key/value pairs from Vault's response. Use `$.data.data` for a KV version 2 secret (its API path contains `/data/`) and `$.data` for KV version 1 or the database secrets engine. The form pre-fills `$.data`. |

> **Note**
>
> Reference a key from the secret in any **Connection Properties** or **Authentication** field with `${key}`, for example `${password}`. Write it with braces (`${key}`, never `$.key`) and match the key name's case. Qualytics resolves it each time the connection is opened, so a changed value takes effect on the next connection. Setup steps, the namespace rule, and a troubleshooting page are on the [HashiCorp Vault](https://userguide.qualytics.io/source-datastore/datastore/connection/hashicorp-vault/deep-dive/introduction/) pages.

#### Location

Pick the database and the schema or schemas Qualytics should read from. You fill these in on both flows.

| Field | Required | Type | Description |
| --- | --- | --- | --- |
| **Database** | Yes | Option | The database to read from. Click the refresh icon to load the databases the role can connect to. |
| **Schema** | Yes | Option | One or more schemas inside the selected database. Each schema you pick becomes its own Qualytics datastore. Defaults to `public`. |

> **Note: One datastore per schema**
>
> Selecting more than one schema creates one source datastore per schema, named from the **Name Template**. See [Multi-Schema Source Datastore Creation](https://userguide.qualytics.io/source-datastore/datastore/multi-schema/overview/) for details.

> **Note: System schemas**
>
> The `information_schema`, `pg_catalog`, `pg_toast`, and `pg_internal` schemas are left out of discovery, along with the temporary schemas whose names start with `pg_temp_` or `pg_toast_temp_`, and TimescaleDB's own `timescaledb_information` and `timescaledb_experimental` schemas.

#### General

Common fields for every source datastore, shown below the **Location** section. You fill these in on both flows.

| Field | Required | Type | Description |
| --- | --- | --- | --- |
| **Name Template** | No | Text | Defines the naming pattern for each source datastore being created. Use `{{schema}}` as a placeholder that gets replaced with the actual schema name (e.g., `ts_{{schema}}` becomes `ts_sales`). Left empty, the datastore is named from the connection name and the schema. |
| **Group** | No | Option | Organizes your datastores under a shared group in the navigation tree. Select an existing group or create a new one with the **Add New Group** toggle. |
| **Teams** | Yes | Option | Select one or more teams to associate with this source datastore. |
| **Initiate Sync** | No | Checkbox | Automatically sync the datastore to detect containers and fields after creation. |

Below the form, an information banner lists the **Public addresses** and **Private addresses** your datastore connections originate from. Allow the ones that match your network setup through your security groups or firewall rules; see [How Connections Work](https://userguide.qualytics.io/source-datastore/datastore/connection/how-it-works/#finding-the-addresses-to-allow) for where the addresses come from.

### Steps

There are two ways to set up the connection: reuse a connection you already saved (**Existing Connection**) or create a new one from scratch (**New Connection**). The tabs below walk through each option; pick the one you want to follow. Each field is described in the [Field reference](https://userguide.qualytics.io/source-datastore/datastore/jdbc/timescale-db/#source-field-reference) above.

**Tab: New Connection**

**Step 1**: Navigate to the **Datastores** page.

**Step 2**: Click the **Add**  button at the top-right corner and choose **Source** .

**Step 3**: The **Add Datastore** page opens.

**Step 4**: Select **New Connection** next to the **Search**  field.

**Step 5**: Select **TimescaleDB** from the connector grid. Use the search field to filter connectors by name.

**Step 6**: Fill in the **Connection Properties**: the **Connection Name**, **Host**, **Port**, **User**, and **Password**.

**Step 7**: Optionally, expand **Secrets Management** to retrieve credentials from a secrets manager.

**Step 8**: Fill in the **Location** fields (**Database** and **Schema**) and the **General** fields.

**Step 9**: Click **Test connection**. A success message confirms that the connection has been verified.

> **Info**
>
> The **Finish** and **Next** buttons stay disabled until the connection test passes on the current values. If the test fails, see [Troubleshooting Common Errors](https://userguide.qualytics.io/source-datastore/datastore/jdbc/timescale-db/#troubleshooting-common-errors).

**Step 10**: Click **Finish** to create the datastore.

> **Tip**
>
> To link a destination so Qualytics can write anomalies and metadata from the first operation, click **Next** instead of **Finish**. It has to live on a connector other than TimescaleDB; see [Link Enrichment on Datastore Creation](https://userguide.qualytics.io/source-datastore/enrichment-datastore/how-tos/link-during-creation/).

**Step 11**: A success dialog confirms that your datastore has been added. Click **Go to your datastore** to open its page.

**Tab: Existing Connection**

**Step 1**: Navigate to the **Datastores** page.

**Step 2**: Click the **Add**  button at the top-right corner and choose **Source** .

**Step 3**: The **Add Datastore** page opens.

**Step 4**: Select **Existing Connection** next to the **Search**  field.

**Step 5**: Select the saved TimescaleDB connection from the grid. Use the search field to filter connections by name. The **Connection Properties** and **Secrets Management** sections come already filled in and read-only.

> **Tip: Start a new connection from this one**
>
> To use the selected connection as a starting point for a brand-new connection instead, click the **Duplicate as a new connection**  button on the selected connection. The form switches to **New Connection** mode with the connection's settings already filled in for you to adjust.

**Step 6**: Fill in the **Location** fields (**Database** and **Schema**) and the **General** fields. These are the only fields left to fill in.

**Step 7**: Click **Test connection**. A success message confirms that the connection has been verified.

> **Info**
>
> The **Finish** and **Next** buttons stay disabled until the connection test passes on the current values. If the test fails, see [Troubleshooting Common Errors](https://userguide.qualytics.io/source-datastore/datastore/jdbc/timescale-db/#troubleshooting-common-errors).

**Step 8**: Click **Finish** to create the datastore.

> **Tip**
>
> To link a destination so Qualytics can write anomalies and metadata from the first operation, click **Next** instead of **Finish**. It has to live on a connector other than TimescaleDB; see [Link Enrichment on Datastore Creation](https://userguide.qualytics.io/source-datastore/enrichment-datastore/how-tos/link-during-creation/).

**Step 9**: A success dialog confirms that your datastore has been added. Click **Go to your datastore** to open its page.

## API Payload Examples

### Creating a Source Datastore

This section provides a sample payload for creating a TimescaleDB datastore. Replace the placeholder values with actual data relevant to your setup.

**Endpoint (Post):** `/api/datastores (post)`

**Tab: Creating a source datastore with a new connection**

```json
{
    "name": "your_datastore_name",
    "teams": ["Public"],
    "database": "timescale_database",
    "schema": "timescale_schema",
    "enrichment_only": false,
    "trigger_sync": true,
    "connection": {
        "name": "your_connection_name",
        "type": "timescale",
        "host": "timescale_host",
        "port": 5432,
        "username": "timescale_username",
        "password": "timescale_password"
    }
}
```

**Tab: Creating a source datastore with an existing connection**

```json
{
    "name": "your_datastore_name",
    "teams": ["Public"],
    "database": "timescale_database",
    "schema": "timescale_schema",
    "enrichment_only": false,
    "trigger_sync": true,
    "connection_id": 123
}
```

**Tab: Create a Source Datastore using the CLI**

```bash
# Step 1: Create a Connection
qualytics connections create \
    --type timescale \
    --name "your_connection_name" \
    --host ${TIMESCALE_HOST} \
    --port 5432 \
    --username ${TIMESCALE_USER} \
    --password ${TIMESCALE_PASSWORD}

# Step 2: Create a Source Datastore
qualytics datastores create \
    --name "your_datastore_name" \
    --connection-name "your_connection_name" \
    --database your_database \
    --schema public
```

### Link an Enrichment Destination

**Endpoint Details:** `/api/datastores/{datastore-id}/enrichment/{enrichment-id} (patch)`

*Last updated: November 3, 2026.*
