| name | snowflake-postgres |
| description | Guide to Snowflake Postgres — managed PostgreSQL with pg_lake (Iceberg tables on object storage), pg_incremental (automated archival), and zero-ETL bridge to Snowflake analytics |
Snowflake Postgres
Snowflake Postgres is a fully managed PostgreSQL service that bridges OLTP and OLAP workloads. Run transactional queries on Postgres, analyze the same data in Snowflake — no ETL pipelines needed. Key extensions (pg_lake, pg_incremental) write PostgreSQL tables as Apache Iceberg tables to object storage, where Snowflake reads them directly.
Getting Started
Creating an Instance
CREATE POSTGRES INSTANCE my_postgres
WAREHOUSE_SIZE = 'SMALL'
DATABASE = 'my_pg_db';
Or via the Snowflake CLI:
snow postgres create-instance my_postgres --warehouse-size SMALL --database my_pg_db
Connecting
Retrieve connection parameters after instance creation:
SHOW POSTGRES INSTANCES;
DESCRIBE POSTGRES INSTANCE my_postgres;
Connect with any standard PostgreSQL client:
psql "host=<instance-host> port=5432 dbname=my_pg_db user=admin sslmode=require"
Instance Sizing
| Size | vCPUs | Memory | Use Case |
|---|
| XSMALL | 2 | 8 GB | Dev/test, light transactional |
| SMALL | 4 | 16 GB | Small production workloads |
| MEDIUM | 8 | 32 GB | Moderate OLTP |
| LARGE | 16 | 64 GB | High-throughput transactional |
Supported Extensions
Snowflake Postgres supports standard PostgreSQL extensions including pg_lake, pg_incremental, pg_partman, PostGIS, pgcrypto, uuid-ossp, hstore, and others. Run SHOW AVAILABLE EXTENSIONS; from psql to list all.
pg_lake — Iceberg Tables on Object Storage
pg_lake writes PostgreSQL tables as Apache Iceberg tables to S3 or other object storage. Changes in Postgres are automatically synced to Iceberg format, making the data queryable from both Postgres (OLTP) and Snowflake (OLAP).
Setup
CREATE EXTENSION IF NOT EXISTS pg_lake;
Converting Tables to Lake Tables
CREATE TABLE orders (
id SERIAL PRIMARY KEY,
customer_id INT NOT NULL,
total NUMERIC(10,2),
status TEXT DEFAULT 'pending',
created_at TIMESTAMPTZ DEFAULT now()
);
SELECT pg_lake.create_lake_table('public', 'orders');
Once converted, all inserts, updates, and deletes to the orders table are automatically propagated to the Iceberg representation on object storage.
Storage Integration
pg_lake uses the storage integration configured on your Snowflake Postgres instance. The instance provisions and manages the S3 path automatically. To check sync status:
SELECT * FROM pg_lake.lake_tables;
Controlling Sync Behavior
SELECT pg_lake.set_sync_interval('public', 'orders', '30 seconds');
SELECT pg_lake.sync_table('public', 'orders');
SELECT table_name, last_synced_at, pending_changes
FROM pg_lake.sync_status;
pg_incremental — Automated Data Archival
pg_incremental manages time-partitioned tables with automatic archival of old partitions. Combined with pg_lake, archived partitions remain queryable through Snowflake.
Setup
CREATE EXTENSION IF NOT EXISTS pg_incremental;
Creating Partitioned Tables with Retention
SELECT pg_incremental.create_partitioned_table(
table_name := 'events',
partition_col := 'event_time',
interval := '1 day',
retention := '90 days'
);
Integration with pg_lake
When both extensions are active, archived partitions are written to Iceberg before being dropped from Postgres. This means old data is still queryable via Snowflake:
SELECT pg_lake.create_lake_table('public', 'events');
Managing Retention
SELECT * FROM pg_incremental.partition_info('events');
SELECT pg_incremental.set_retention('events', '60 days');
SELECT pg_incremental.archive_partition('events', '2025-01-15');
Zero-ETL Bridge to Snowflake
Snowflake reads the Iceberg tables created by pg_lake directly — no separate ETL pipeline, no data copying, no orchestration.
How It Works
- pg_lake writes Postgres data as Iceberg tables to object storage
- Snowflake uses a catalog integration to discover and read those Iceberg tables
- You query the data in Snowflake using standard SQL
Setting Up the Snowflake Side
CREATE OR REPLACE CATALOG INTEGRATION pg_lake_catalog
CATALOG_SOURCE = ICEBERG_REST
TABLE_FORMAT = ICEBERG
CATALOG_NAMESPACE = 'public'
REST_CONFIG = (
CATALOG_URI = '<pg_lake_catalog_uri>'
CATALOG_API_TYPE = 'AWS_GLUE'
)
ENABLED = TRUE;
CREATE OR REPLACE ICEBERG TABLE analytics_db.public.orders
EXTERNAL_VOLUME = 'pg_lake_volume'
CATALOG = 'pg_lake_catalog'
CATALOG_TABLE_NAME = 'public.orders';
SELECT
DATE_TRUNC('month', created_at) AS month,
COUNT(*) AS order_count,
SUM(total) AS revenue
FROM analytics_db.public.orders
GROUP BY 1
ORDER BY 1;
Catalog-Linked Database (Simplified)
For a simpler setup, use a catalog-linked database to auto-discover all pg_lake tables:
CREATE DATABASE pg_analytics
LINKED_CATALOG = 'pg_lake_catalog';
SELECT * FROM pg_analytics.public.orders LIMIT 10;
Common Patterns
OLTP + OLAP Hybrid Architecture
Keep transactional workloads on Postgres, run analytics in Snowflake:
INSERT INTO orders (customer_id, total, status)
VALUES (42, 99.99, 'confirmed');
SELECT customer_id, SUM(total) AS lifetime_value
FROM pg_analytics.public.orders
GROUP BY customer_id
ORDER BY lifetime_value DESC
LIMIT 100;
Event Sourcing with Lake Archival
Use pg_incremental to keep the event table lean in Postgres while preserving full history in Snowflake:
SELECT * FROM events WHERE event_time > now() - INTERVAL '7 days';
SELECT event_type, COUNT(*), DATE_TRUNC('week', event_time) AS week
FROM pg_analytics.public.events
GROUP BY 1, 3;
Microservice Database with Analytics Bridge
Each microservice owns its Postgres database. pg_lake bridges selected tables to a shared Snowflake analytics layer without coupling services:
SELECT pg_lake.create_lake_table('public', 'user_signups');
SELECT pg_lake.create_lake_table('public', 'purchases');
SELECT u.signup_source, COUNT(p.id) AS purchases
FROM service_a.public.user_signups u
JOIN service_b.public.purchases p ON u.user_id = p.user_id
GROUP BY 1;
Extensions and Compatibility
Key Differences from Vanilla PostgreSQL
- Managed infrastructure: no OS-level access, automated backups and patching
- pg_lake and pg_incremental: exclusive to Snowflake Postgres
- Storage: uses Snowflake-managed storage with automatic Iceberg integration
- Networking: accessed through Snowflake's network layer with private connectivity options
Supported Extensions
| Extension | Purpose |
|---|
| pg_lake | Iceberg table sync to object storage |
| pg_incremental | Time-partitioned tables with auto-archival |
| pg_partman | Advanced partition management |
| PostGIS | Geospatial data types and functions |
| pgcrypto | Cryptographic functions |
| uuid-ossp | UUID generation |
| pg_trgm | Trigram-based text similarity |
| btree_gin/gist | Additional index types |
Best Practices
-
Right-size for OLTP: choose an instance size based on your transactional workload, not your analytics needs. Snowflake handles the heavy analytical queries.
-
Be selective with pg_lake: convert tables that need analytics access, not every table. System tables, temporary tables, and small lookup tables usually don't need Iceberg sync.
-
Set appropriate sync intervals: default 60 seconds works for most cases. Reduce for near-real-time dashboards, increase for batch-oriented analytics to reduce I/O overhead.
-
Monitor sync lag: check pg_lake.sync_status regularly. If pending_changes grows consistently, consider increasing instance size or adjusting sync intervals.
-
Use pg_incremental for large time-series tables: keep Postgres fast by only holding recent data. Let pg_lake archive older partitions to Iceberg where Snowflake handles historical queries efficiently.
-
Keep transactional queries on Postgres: don't route analytical queries to Postgres. Use Snowflake for aggregations, joins across large datasets, and reporting.
-
Use catalog-linked databases: prefer CREATE DATABASE ... LINKED_CATALOG over manually creating individual Iceberg tables in Snowflake — it auto-discovers new pg_lake tables.
-
Test with realistic data volumes: pg_lake sync performance depends on change volume and row size. Test with production-like data before going live.