- Home
- Destinations
- PostgreSQL
Send events to PostgreSQL
Row-shaped event delivery into a customer-owned Postgres table, UPSERT by primary key, idempotent.
What is PostgreSQL?
PostgreSQL is the open-source relational database used by most modern application stacks. As a destination, Postgres works when the customer wants events in the same database their operational app uses, for joins against their domain tables, in-app analytics, or audit trails. Pushrail's adapter writes one row per event with the canonical envelope columns plus the payload as JSONB.
Why deliver events to PostgreSQL
- Same database as the customer's application, joins are trivial.
- JSONB stores arbitrary payloads with index support.
- In-app analytics queries run against the same connection pool the app already has.
- Audit-trail use cases benefit from transactional guarantees.
How Pushrail delivers events to PostgreSQL
The adapter writes one row per event using `INSERT … ON CONFLICT (event_id) DO NOTHING` so re-runs are naturally idempotent. The table shape is the canonical envelope: `event_id` (UUID PK), `event_type` (TEXT), `occurred_at` (TIMESTAMPTZ), `customer_external_id` (TEXT), `source` (TEXT), `payload` (JSONB), `metadata` (JSONB), `received_at` (TIMESTAMPTZ DEFAULT now()). Customers can opt into one table per event type for tighter schemas, or stay with the single-table default.
Auth and credentials
Username/password over TLS (default) or AWS IAM authentication for RDS-hosted Postgres (recommended, short-lived tokens, no static passwords). Credentials encrypted at rest. The customer creates a dedicated `pushrail_writer` role with `INSERT` on the destination table and no other privileges.
Batching, retries, and replay
Inserts are batched in groups of 1,000 events per transaction with a 1-second flush window. Transient errors (connection reset, deadlock) retry with exponential backoff. Permanent errors (auth, table not found) land in the DLQ. The `ON CONFLICT` clause makes replays a true no-op on already-inserted rows; the customer never sees duplicates.
Example payload
Pushrail accepts the canonical event shape on POST /v1/events. Below is the ingestion request your service makes.
{
"eventType": "order.completed",
"occurredAt": "2026-05-26T14:21:08.493Z",
"source": "billing-service",
"customerExternalId": "acct_8K2zRq",
"idempotencyKey": "order_38a91f-completed",
"correlationId": "req_4f30b2",
"payload": {
"orderId": "ord_38a91f",
"amount": 12900,
"currency": "USD",
"items": [
{ "sku": "PR-PRO-MONTHLY", "qty": 1, "price": 12900 }
]
},
"metadata": {
"tier": "pro",
"region": "us-east-1"
}
}Example configuration
The fields your customer fills in to point Pushrail at their PostgreSQL setup.
{
"type": "POSTGRES",
"name": "Customer Postgres",
"host": "acme-prod.cluster-abc123.us-east-1.rds.amazonaws.com",
"port": 5432,
"database": "acme_app",
"schema": "pushrail",
"table": "events",
"tls": true,
"auth": {
"mode": "RDS_IAM",
"username": "pushrail_writer",
"region": "us-east-1"
},
"batchSize": 1000,
"flushIntervalSec": 1
}Common use cases
- Land events in the same database the customer's app already uses for trivial joins.
- Power an in-app analytics tab over event history without a separate warehouse.
- Audit log destinations with transactional guarantees.
- Per-customer audit tables for compliance evidence.
Related destinations
Row-shaped event delivery into a customer-owned MySQL table, INSERT IGNORE by primary key, idempotent.
High-throughput row-shaped event delivery into a customer's ClickHouse cluster, one row per event.
Stream events directly into a customer's BigQuery dataset, table per event type or single wide table.
Frequently asked questions
How does Pushrail write events into Postgres?
One row per event using INSERT … ON CONFLICT (event_id) DO NOTHING, so re-runs are naturally idempotent. The table uses the canonical envelope columns plus payload and metadata as JSONB. Customers can keep the single-table default or opt into one table per event type.
Why land events in Postgres instead of a warehouse?
When the customer wants events in the same database their operational app uses, for trivial joins against domain tables, in-app analytics over the existing connection pool, or audit trails with transactional guarantees.
Whose credentials are used and how is it secured?
The customer's. They create a dedicated pushrail_writer role with INSERT on the destination table and no other privileges. Pushrail connects with username/password over TLS or AWS IAM authentication for RDS-hosted Postgres (recommended, short-lived tokens). Credentials are encrypted at rest.
Are replays safe?
Yes. Inserts are batched in groups of 1,000 per transaction. Transient errors (connection reset, deadlock) retry with backoff; permanent errors (auth, missing table) land in the dead-letter queue. The ON CONFLICT clause makes replay a true no-op on already-inserted rows, so the customer never sees duplicates.