PostgreSQL
Datafly Signal writes first-party events into a PostgreSQL table for reliable relational storage with advanced JSONB querying and the full Postgres extension ecosystem.
Why route PostgreSQL through Datafly Signal
- The data lands in infrastructure you own. Signal writes to your PostgreSQL directly from your own deployment. Datafly operates no intermediary and never holds a copy of your event data.
- Client IP and user agent are recorded as event context. Both come from the original visitor request rather than from your server, so geography and device analysis done in the warehouse reflects the actual visitor.
- Presets for Retail and General. The mapping starts from a working configuration for your vertical instead of a blank field map.
- One consent decision governs every destination. PostgreSQL is gated by the same consent state as the rest of your stack, so a withdrawal applies everywhere at once.
Prerequisites
Before configuring PostgreSQL in Signal, you need a PostgreSQL server (self-hosted or managed), a database with a schema and table, and a user with INSERT privileges.
Set Up a PostgreSQL Server
You have several options:
Option A: Managed PostgreSQL
- Amazon RDS for PostgreSQL — Create an RDS instance
- Azure Database for PostgreSQL — Create an Azure PostgreSQL server
- Google Cloud SQL for PostgreSQL — Create a Cloud SQL instance
- Supabase, Neon, Aiven, or other managed providers
Option B: Self-Hosted PostgreSQL
- Install PostgreSQL using the official guide.
- Start the PostgreSQL server.
- Ensure it is accessible from your Signal infrastructure on port
5432.
Configure Network Access
Ensure your PostgreSQL server accepts connections from Signal’s IP addresses:
- RDS: Configure the security group inbound rules on port
5432. - Azure: Add firewall rules in the Networking tab.
- Cloud SQL: Add authorised networks.
- Self-hosted: Configure
pg_hba.confand firewall rules.
Create a Database, Schema, and Table
Connect to the PostgreSQL server and create the target resources:
CREATE DATABASE datafly_events;
\c datafly_events
CREATE SCHEMA IF NOT EXISTS datafly;
CREATE TABLE datafly.events (
event_id VARCHAR(64) NOT NULL PRIMARY KEY,
type VARCHAR(20),
event VARCHAR(256),
anonymous_id VARCHAR(64),
user_id VARCHAR(256),
timestamp TIMESTAMPTZ,
received_at TIMESTAMPTZ,
sent_at TIMESTAMPTZ,
context JSONB,
properties JSONB,
traits JSONB,
source_id VARCHAR(64),
integration_id VARCHAR(64)
);
CREATE INDEX idx_events_timestamp ON datafly.events (timestamp DESC);
CREATE INDEX idx_events_type_event ON datafly.events (type, event);Using JSONB (binary JSON) instead of JSON enables indexing and efficient querying with operators like ->, ->>, @>, and ?. TIMESTAMPTZ stores timestamps with timezone information for correct ordering across time zones.
Create a User for Signal
CREATE USER datafly_signal WITH PASSWORD 'your_secure_password';
GRANT USAGE ON SCHEMA datafly TO datafly_signal;
GRANT INSERT ON TABLE datafly.events TO datafly_signal;Configuration
| Field | Type | Required | Description |
|---|---|---|---|
host | string | Yes | The PostgreSQL server hostname. |
port | string | Yes | The PostgreSQL server port. Defaults to 5432. |
database | string | Yes | The target database name. |
schema_name | string | Yes | The schema to write to. Defaults to public. |
table_name | string | Yes | The target table name to insert rows into. |
username | string | Yes | The database username for authentication. |
password | secret | Yes | The database password for authentication. |
ssl_mode | select | No | The SSL mode for the connection: disable, require, verify-ca, verify-full. Defaults to require. |
Signal Setup
Quick Setup
- Navigate to Integrations in the sidebar.
- Open the Integration Library tab.
- Find PostgreSQL or filter by Database.
- Click Install, select a variant if available, and fill in the required fields.
- Click Install Integration to create the integration with a ready-to-use default blueprint.
API Setup
curl -X POST http://localhost:8084/v1/admin/integration-catalog/postgresql/install \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "PostgreSQL",
"variant": "default",
"config": {
"host": "db.example.com",
"port": "5432",
"database": "datafly_events",
"schema_name": "datafly",
"table_name": "events",
"username": "datafly_signal",
"password": "your_secure_password",
"ssl_mode": "require"
},
"delivery_mode": "server_side"
}'Schema
Signal writes the standard event envelope. The recommended table definition:
| Column | PostgreSQL type | Notes |
|---|---|---|
event_id | VARCHAR(64) NOT NULL PRIMARY KEY | Unique per event — also the natural dedupe key. |
type | VARCHAR(20) | Event type. |
event | VARCHAR(256) | Event name (snake_case). |
anonymous_id | VARCHAR(64) | First-party visitor identifier. |
user_id | VARCHAR(256) | Logged-in user identifier (nullable). |
timestamp | TIMESTAMPTZ | Client event time. |
received_at | TIMESTAMPTZ | Time Signal received the event. |
sent_at | TIMESTAMPTZ | Time the row was delivered. |
context | JSONB | Page, device, user agent, consent metadata. |
properties | JSONB | Custom event properties. |
traits | JSONB | User traits. |
source_id | VARCHAR(64) | Pipeline source identifier. |
integration_id | VARCHAR(64) | Signal integration identifier. |
Query JSONB with ->, ->>, @>, and ? operators.
Consent
PostgreSQL is a first-party destination under your control. The default blueprint forwards all events. Apply consent filtering via pipeline transforms or downstream views/policies over context->>'consent' if needed.
Testing
- Enable the integration in Signal and trigger a test event on your website.
- Connect to the PostgreSQL server and query the target table:
SELECT * FROM datafly.events ORDER BY timestamp DESC LIMIT 10;- Verify that event rows are appearing with correct data.
- Test JSONB querying:
SELECT event_id, properties->>'order_id' AS order_id, (properties->>'total')::numeric AS total
FROM datafly.events
WHERE event = 'Order Completed'
ORDER BY timestamp DESC
LIMIT 5;- In Signal, check the Live Events view to confirm delivery status shows as successful.
Troubleshooting
| Problem | Solution |
|---|---|
| Events not appearing in the table | Verify the host, port, database, schema, and table name are correct. |
| Connection refused / timeout | Check that the PostgreSQL server accepts connections from Signal’s IP. Verify firewall rules, security groups, or pg_hba.conf. |
FATAL: password authentication failed | The username or password is incorrect. Verify the credentials. |
permission denied for schema | The user lacks USAGE on the schema. Run GRANT USAGE ON SCHEMA datafly TO datafly_signal;. |
permission denied for table | The user lacks INSERT on the table. Run GRANT INSERT ON TABLE datafly.events TO datafly_signal;. |
relation does not exist | The table does not exist or the schema is not in the search path. Use the fully qualified name schema.table. |
| SSL connection errors | Verify the ssl_mode setting. Most managed services require require or verify-full. Self-hosted servers may need SSL certificate configuration. |
value too long for type | An event field exceeds the column’s VARCHAR length. Increase the column size with ALTER TABLE. |
Visit PostgreSQL documentation for full SQL reference, JSONB operators, and performance tuning guides.