Reference for the n8n PostgreSQL node -- operations, authentication, parameter binding, and common patterns.
The PostgreSQL node connects to a PostgreSQL database to run queries, insert rows, update records, and delete data. It is used for persistent storage, reporting pipelines, and integrating n8n workflows with application databases.
Create a PostgreSQL credential in n8n with the following connection parameters:
| Parameter | Description | Example |
|---|---|---|
| Host | Database server hostname or IP | db.example.com |
| Port | PostgreSQL port | 5432 |
| Database | Database name | app_production |
| User | Database username | n8n_user |
| Password | Database password | ******** |
| SSL | Enable SSL/TLS connection | true for cloud-hosted DBs |
Tip
Create a dedicated database user for n8n with the minimum privileges needed (e.g.,
SELECT,INSERT,UPDATEon specific tables). Avoid using superuser credentials.
| Operation | Description |
|---|---|
| Execute Query | Run any SQL statement (SELECT, INSERT, UPDATE, DELETE, DDL). Returns result rows for SELECT queries. |
| Insert | Insert one or more rows into a table using field mapping. No raw SQL needed. |
| Update | Update rows matched by a key column using field mapping. |
| Delete | Delete rows matched by a key column. |
| Upsert | Insert a row or update it if a conflict occurs on the specified key. Uses ON CONFLICT. |
| Parameter | Applies To | Description |
|---|---|---|
| Query | Execute Query | The SQL statement to run. Supports n8n expressions and parameter binding. |
| Table | Insert, Update, Delete, Upsert | Target table name. |
| Columns | Insert, Update, Upsert | Which incoming data fields to map to table columns. |
| Key Column | Update, Delete, Upsert | The column used to match existing rows (e.g., id). |
| Options > Query Parameters | Execute Query | Array of values for parameterized queries (e.g., $1, $2 placeholders). |
| Options > Output Large-Format Columns As | All | How to handle bytea, json, and array columns in output. |
Warning: Never concatenate user input into SQL strings
Using expressions like
`SELECT * FROM users WHERE id = '{{ $json.id }}'`is vulnerable to SQL injection. Always use parameter binding instead.
Use numbered placeholders ($1, $2, ...) in your query and pass values through Query Parameters:
SELECT * FROM orders WHERE customer_id = $1 AND status = $2
Then in Query Parameters, provide the values as an array:
[{{ $json.customer_id }}, {{ $json.status }}]
n8n sends these as parameterized values, which PostgreSQL handles safely.
| PostgreSQL Type | n8n Output Type | Notes |
|---|---|---|
integer, bigint |
Number | |
text, varchar |
String | |
boolean |
Boolean | |
timestamp, date |
String | Returned as ISO 8601 string |
json, jsonb |
Object | Automatically parsed |
numeric, decimal |
String | Returned as string to preserve precision |
uuid |
String | |
bytea |
Binary | Available as binary data |
Insert data from another node:
Webhook --> Code (transform) --> PostgreSQL (Insert)
Map incoming fields to table columns. The Insert operation processes one item per row, so n8n automatically inserts multiple rows if multiple items arrive.
Query with dynamic filters:
Schedule Trigger --> PostgreSQL (Execute Query) --> Slack (Send Message)
Run a parameterized SELECT on a schedule, then forward the results to a notification node. Use Query Parameters for any dynamic filter values.
Upsert for idempotent syncs:
Google Sheets (Read) --> PostgreSQL (Upsert)
Set the key column to your unique identifier (e.g., email). New rows are inserted; existing rows are updated. This makes the sync safe to run repeatedly.
Batch processing:
PostgreSQL (Select) --> SplitInBatches --> HTTP Request --> PostgreSQL (Update)
Fetch records, process them in batches through an external API, then update each record's status back in the database.
SELECT "firstName" FROM users.max_connections limit. Use connection pooling (e.g., PgBouncer) in production.null before the PostgreSQL node.BEGIN; ... COMMIT;.RETURNING * to your INSERT query in Execute Query mode to get the inserted row (including auto-generated IDs) back in the output.EXECUTIONS_TIMEOUT or optimize your query if this occurs.ECONNREFUSED or Connection terminated unexpectedly when connecting to the database.
n8n cannot reach the PostgreSQL server. Verify the host, port, and network configuration. If both n8n and PostgreSQL run in Docker, they must be on the same Docker network or use the container name as the host (e.g., postgres instead of localhost). Also check that SSL is enabled in the credential if the database requires it.
relation "table_name" does not exist when running a query.
The table does not exist in the connected database, or you are connected to the wrong database. Confirm the Database field in the credential matches the database where the table was created. If the table name uses mixed case, quote it in SQL: SELECT * FROM "MyTable".
null value in column "X" violates not-null constraint during Insert.
An upstream node is sending an empty string or null for a required column. Either set a default value in the PostgreSQL table definition (ALTER TABLE ... ALTER COLUMN ... SET DEFAULT ...), make the column nullable, or add a Code node upstream to replace empty values with a valid default before the insert.
I build production n8n and Cloudflare automation for teams — the same engineering behind HarperFlow. Fixed-price, escrow-protected, US-based.