PostgreSQL
lastmod 2026-09-22created 2015-10-29196 edits
PostgreSQL is a type of object-relational database management system (ORDBMS). It is distributed under the BSD license, and open-source developers and related companies take part in its development.
On first installation it uses 5432 as the default port.
Categories
- PostgreSQL:Basic
- PostgreSQL:PrimaryKey (PK)
- PostgreSQL:Performance
- PostgreSQL:Vacuum
- PostgreSQL:Function
- PostgreSQL:CreateFunction
- PostgreSQL:TriggerFunctions
- PostgreSQL:Volatility - Function Volatility Categories -
VOLATILE,STABLE,IMMUTABLE
- PostgreSQL:TruncateTable -
TRUNCATE TABLE- empty all data from a table.
Commandline
Extensions
- pg_cron
- hstore
- Apache AGE - A Graph Extension for PostgreSQL.
- pg_graphql - GraphQL extension for PostgreSQL
- pg_crdt - experimental open-source CRDT extension for Postgrest
- PgBouncer - lightweight connection pooler for PostgreSQL
- pg_flo - stream, transform, and reroute PostgreSQL data in real time
- pgrx - a framework for developing PostgreSQL extensions in Rust
- pglite-fusion - embedding SQLite in PostgreSQL tables
- VectorChord - storing 400,000 vectors in PostgreSQL for $1
- pgvector
- pgcrypto
- pg-status - lightweight microservice for checking PostgreSQL host status
Projects using PostgreSQL
- PolarDB - open source that turns Postgres into a Share-nothing distributed DB
- MangoDB - open-source alternative to MongoDB (works as a proxy, converting MongoDB Wire Protocol queries to SQL and using PostgreSQL as the DB engine)
- Babelfish for PostgreSQL - open-source extension that lets PostgreSQL run applications written for MS SQL Server
- TimescaleDB
- Using Postgres as a Pub/Sub Server https://news.hada.io/topic?id=877
- Message-DB, a PostgreSQL-based Pub/Sub message & event store https://news.hada.io/topic?id=1096
- Apache Age, an extension that adds Graph DB features to Postgres https://news.hada.io/topic?id=3844
- PolarDB for PostgreSQL https://news.hada.io/topic?id=4362
- Supabase starts public beta - open-source alternative to Firebase https://news.hada.io/topic?id=3347
- EdgeDB - next-generation open-source ORDB for developers https://news.hada.io/topic?id=4602
- RudderStack - open-source alternative to Segment https://news.hada.io/topic?id=1759
- OrioleDB - cloud-native storage engine (a new storage engine for PostgreSQL)
- IaSQL - Infrastructure as data in PostgreSQL
- Fly Postgres - postgresql provided by fly.io
- PeerDB - ETL/ELT platform for Postgres
- Postgres-wasm - PostgreSQL server that runs in the browser
- Postgres Language Server (postgres_lsp)
- SQLedge - tool that replicates Postgres to SQLite at the Edge
- Pongo - Postgres-based Mongo with Strong Consistency
- PgQueuer – Python library that uses PostgreSQL as a job queue
- rainfrog - TUI tool for data management for Postgres
- PgQue – Bloat-free Postgres queue | GeekNews
- Barman - Backup and recovery manager for PostgreSQL | GeekNews
Client
Tools
AI Agents
- Xata Agent - PostgreSQL expert AI agent
- Snap-QL - desktop app for querying Postgres with AI
ETC
- Postgres.app - PostgreSQL for macOS
- Inquery - utility that calls webhooks when Postgres changes
- broadcaster (python) - Simple broadcast channels
- Retake - open-source hybrid search for Postgres
- PGMQ - Postgres-based alternative to SQS
- Postgres Language Server
- Hatchet - job orchestration platform built on Postgres
- PgDog - ultra-fast transaction pooling and sharding manager for PostgreSQL
- pgactive - Postgres active-active replication extension
- Ivory - Postgres / Patroni cluster management tool
- pgschema - Terraform-style declarative Postgres schema migration tool
- pgdbtemplate - creating PostgreSQL test DBs ultra-fast from template DBs
- Pg_lake - extension that supports Iceberg and data lake access from Postgres
- RegreSQL - tool that automates regression testing of SQL queries
- OpenWorkflow - open-source workflow automation and execution platform that runs on Postgres alone
- PGlite - embedded Postgres
- Biscuit - dedicated index for speeding up LIKE / ILIKE pattern search in PostgreSQL
- Sqldef - idempotent schema management tool for MySQL, PostgreSQL, and SQLite
- pgmicro - in-process PostgreSQL built on SQLite
-
pgBackRest - Reliable PostgreSQL Backup & Restore- Deprecated
Extension
PostgreSQL provides a way to extend database functionality by packaging SQL objects and using them as a single unit.
PostGIS
The PostGIS 2.3 extension group and the PostGIS 3.0 extensions are supported in Cloud SQL, including JSON-C support.
- postgis
- postgis_tiger_geocoder
- postgis_topology
Data type extensions
- btree_gin - Provides sample GIN operator classes that implement B-tree equivalent behavior.
- btree_gist - Provides GiST index operator classes that implement B-tree equivalent behavior.
- chkpass - Implements a data type chkpass designed for storing encrypted passwords. Not supported in PostgreSQL 12 for Cloud SQL.
- citext - Provides a case-insensitive string type citext.
- cube - Implements a data type cube for representing multidimensional cubes.
- hstore - Implements the hstore data type for storing sets of key-value pairs within a single PostgreSQL value.
- isn - Provides data types for some international product numbering standards.
- ltree - Implements a data type ltree for representing labels of data stored in a hierarchical tree structure.
- lo - Supports management of large objects (LO or BLOB).
- postgresql-hll - Introduces a new data type, hll (a HyperLogLog data structure). See also the postgresql-hll section below.
Language extensions
- plpgsql - A loadable procedural language for creating functions, procedures, and triggers. You can also use this language to execute code directly in DO blocks.
Other extensions
- dict_int - An add-on dictionary template for full-text search that controls the indexing of integers.
- earthdistance - Provides two approaches to calculating great-circle distances on the surface of the Earth.
- fuzzystrmatch - Provides several functions to determine similarities and distance between strings.
- intagg - Provides an integer aggregator and an enumerator.
- intarray - Provides a set of functions and operators for manipulating null-free arrays of integers and performing indexed searches on them.
- pageinspect - Inspects the contents of database pages at a low level. See also the pageinspect section below.
- pgaudit - Collects audit data. For how to use this extension in Cloud SQL, see PostgreSQL auditing with pgAudit.
- pg_buffercache - Provides a means of examining changes in the shared buffer cache in real time.
- pgfincore - A set of functions for managing pages in operating system disk cache memory from PostgreSQL. See also the pgfincore section below.
- pg_freespacemap - Examines the free space map (FSM). See also the pg_freespacemap section below.
- pgcrypto - Provides cryptographic functions for PostgreSQL.
- pgstattuple - Provides various functions for obtaining tuple-level statistics.
- pg_repack - Lets you remove bloat from tables and indexes. Optionally, you can perform an online CLUSTER (ordering tables by a cluster index). See also the pg_repack section below.
- pgrowlocks - Provides row locking information for a specified table.
- pg_prewarm - Provides a convenient way to load relation data into either the operating system buffer cache or the PostgreSQL buffer cache.
- pg_stat_statements - Provides a means of tracking execution statistics of all SQL statements executed.
- pg_trgm - Provides functions and operators for determining the similarity of alphanumeric text based on trigram matching, as well as index operator classes that support fast searching for similar strings.
- pg_visibility - Provides a way to examine the visibility map (VM) and page-level visibility information of a table. See also the pg_visibility section below.
- PL/Proxy - A procedural language handler that allows remote procedure calls between PostgreSQL databases, with optional sharding. See also the PL/Proxy section below.
- postgres_fdw - Lets you create foreign data wrappers within or across instances. See also the postgres_fdw section below.
- sslinfo - Provides information about the SSL certificate that the current client provided when connecting to the instance.
- tablefunc - Includes various functions that return tables (multiple rows).
- tsm_system_rows - Provides the table sampling method SYSTEM_ROWS, which can be used in the TABLESAMPLE clause of a SELECT command.
- tsm_system_time - Provides the table sampling method SYSTEM_TIME, which can be used in the TABLESAMPLE clause of a SELECT command.
- unaccent - A text search dictionary that removes accents (diacritic signs) from lexemes.
- uuid-ossp - Provides functions to generate universally unique identifiers (UUIDs) using one of several standard algorithms.
ETC
- Citus
- Github - supabase/postgres - Postgres Docker images including useful extensions
ERD Tools
- pgModeler - PostgreSQL Database Modeler
Show all parameters
show the value of a run-time parameter.
Checking the data directory
Timestamp
NOW
In PostgreSQL, NOW() returns the "transaction start time", not the wall clock.
Even if it runs several times inside a single BEGIN ... ROLLBACK transaction, it returns the same value.
For reference, if you need a current time that changes with every statement, use clock_timestamp().
MIN/MAX
Extracts the maximum/minimum. Record aggregate functions.
GREATEST/LEAST
Can be used where record aggregate functions do not apply. Usable on data types like Array, i.e. single-item data, or per variable inside plpgsql functions.
ARRAY
EMPTY ARRAY
Or:
Dollar Quoting
$$ is PostgreSQL's Dollar Quoting syntax. It is an alternative notation to single quotes '...'.
- Every character between
$$and$$is treated as a string - A tag can be used inside, as in
$JSON$/$function$ - Appending
::jsonbquickly creates ajsonbobject
The following are equivalent expressions:
Useful when you need single quotes inside single quotes:
Nesting is also possible by adding tags:
Tips
- [Recommended] Lesser Known PostgreSQL Features 1
Lesser-known PostgreSQL features:
- Finding the number of rows updated/inserted by Insert on Conflict (Upsert):
xmax - Granting privileges on specific columns
- Multi-pattern matching:
SIMILAR TOor~regular expressions - Finding the current sequence value without incrementing it:
pg_sequence_last_value() - Using multi-line SQL with
\COPY: use a TEMPORARY VIEW, orCOPYwith\g - Preventing values from being set on auto-generated keys: specify
GENERATED ALWAYSwhen creating the table (instead ofGENERATED BY DEFAULT) - Creating pivot tables: use
pandas.pivot_table,\crosstabview, or the tablefunc extension - #Dollar Quoting
- Adding comments to DB objects:
COMMENT ON TABLE/COLUMN; long string descriptions can also be added usingDollar Quoting- Write:
COMMENT ON TABLE sale IS 'Sales made in the system'; - View:
\d+ sale
- Write:
-
\set HISTFILE ~/.psql_history- :DBNAME
\set COMP_KEYWORD_CASE upper pg_sleep(초), pg_sleep_for('4 minutes 14 seconds') DISTINCT ON (그룹 컬럼) gen_random_uuid() - generates a version 4 UUID setseed() NOT VALID, then run validation of existing data separately with ALTER VALIDATE OVERLAPS operator Use Postgres for everything
Postgres can replace many backend technologies (up to millions of users)
- Kafka, RabbitMQ, Mongo, Redis,..
- For caching, instead of Redis, use TEXT as JSON in an UNLOGGED table
- Set expiration periods on data with stored procedures
It's 2026. Just Use Postgres
- It's 2026, Just Use Postgres (It's 2026. Just Use Postgr | GeekNews
- [Original] It’s 2026, Just Use Postgres | Tiger Data
Core argument:
- The long-standing advice to "use the right tool for the job" has instead led to database sprawl and created management hell. In the 2026 era of AI agents, handling everything with a single database is overwhelmingly advantageous. Bottom line → most (99%) companies need nothing more than Postgres.
Why go with Postgres alone now?
- AI agents need to spin up, fork, and debug test DBs quickly, which is nearly impossible with multiple DBs (Pinecone + Elasticsearch + Redis + MongoDB, etc.).
- With Postgres alone, backup, monitoring, security, and disaster recovery strategies are unified → cognitive load and hidden costs drop sharply.
- With multiple DBs, problems such as sync failures, skyrocketing recovery difficulty, and a 7x increase in operational complexity are real.
Concrete grounds for Postgres replacing specialized DBs
Postgres extensions already implement algorithms that are the same as or better than those of specialized DBs:
- Search → pg_textsearch (BM25) → replaces Elasticsearch
- Vector search → pgvector + pgvectorscale (DiskANN) → 28x faster and 75% cheaper than Pinecone
- Time series → TimescaleDB → comparable to or better than InfluxDB + full SQL support
- Documents → JSONB → MongoDB-level performance + ACID guarantees
- Geospatial → PostGIS (the standard since 2001)
- Queues → pgmq → can replace Kafka
- Most of the rest is covered by pg_cron, pgai, and others
Query History
- check history along with dates for queries in Postgres - how to check it in the pg_stat_activity table.
Partial Index
Used to index only the rows that satisfy a condition.
See the DDL example below:
CREATE UNIQUE INDEX email_idx ON public.myapp_email USING btree (email) WHERE (email IS NOT NULL and email <> '');
The where clause means indexing happens only when email is not NULL and email is not an empty string.
UNIQUE NULLS NOT DISTINCT
An option added in PostgreSQL 15+.
By default, PostgreSQL's UNIQUE constraint treats NULL != NULL.
For example, in a table like the following:
org_id INT8 NOT NULL,
user_id INT8,
metric_code TEXT NOT NULL,
...
UNIQUE (org_id, user_id, metric_code)
multiple rows like (1, NULL, 'api_calls') can be inserted.
Adding UNIQUE NULLS NOT DISTINCT here treats NULL = NULL and prevents duplicates.
Indexes in PostgreSQL
PostgreSQL indexes are a core structure for speeding up data access, improving query performance by reducing the amount of data that must be read from disk
- Indexes come in various forms such as Btree, Hash, BRIN, GIN, GiST, and SP-GiST, each optimized for different data characteristics and query patterns
- Indexes carry various costs, including disk space, write performance, query planner complexity, and memory usage
- Advanced features such as partial indexes, multicolumn indexes, covering indexes, and expression indexes can maximize efficiency in specific situations
- Choosing and managing the right indexes is emphasized as a key factor in PostgreSQL performance optimization
with Node.js
LISTEN & NOTIFY
- Asynchronous jobs implemented with the LISTEN & NOTIFY commands
- 31.8. Asynchronous Notification
- NOTIFY -- generate a notification
FOUND
FOUND is a PL/pgSQL built-in variable that is TRUE if the previous SQL statement affected any rows, and FALSE otherwise.
For example:
This part checks the result of the preceding UPDATE.
- UPDATE changes 1 or more rows → FOUND = TRUE → passes
- UPDATE changes 0 rows (project already archived or nonexistent) → FOUND = FALSE → exception raised
In other words, it is a guard that prevents attempts to archive a project that is already archived.
Column display order of a table
"[wiki.postgresql.org/wiki/Alter_column_position Alter column position]" in the PostgreSQL Wiki says:
PostgreSQL currently defines column order based on the ``attnum`` column of the ``pg_attribute`` table. The only way to change column order is either by recreating the table, or by adding columns and rotating data until you reach the desired layout.
That's pretty weak, but in their defense, in standard SQL, there is no solution for repositioning a column either. Database brands that support changing the ordinal position of a column are defining an extension to SQL syntax.
One other idea occurs to me: you can define a VIEW that specifies the order of columns how you like it, without changing the physical position of the column in the base table.
Why you should not use varchar(n) by default
See the relevant section.
Limiting character count instead of varchar(n)
See PostgreSQL:Constraints#Character limit. In short:
Measuring string length
- SQL 'LENGTH' & 'CHAR_LENGTH' Functions | Reintech media
- Postgres Length Functions With Practical Examples - CommandPrompt Inc.
select length('abcd'); -- 4 -- 문자열의 길이를 "바이트 단위"로 반환.
select char_length('abcd'); -- 4 -- length() 와 비슷하지만. "문자 단위"로 반환한다.
select length('Dž') -- 2
select char_length('Dž'); -- 1
select character_length('abcd'); -- 4 -- char_length() 와 동일
select bit_length('1234'); -- 32 -- 문자열의 비트 수
select octet_length('1234'); -- 4 -- 문자열의 바이트 수
Using it as a data warehouse
Key tweaks compiled from experience by Narrative, a decision support system based on data analysis
- Don't use the same server as production systems
- Upgrade to PG 12+ (don't use CTEs)
- Keep indexes as small as possible
- Table partitioning (range/list)
- Minimize disk I/O
- Vacuum after bulk inserts
- Use parallel queries
- Increase statistics sampling (per column or DB-wide. The default is 100, but use 500)
- Use fewer columns (because storage is row-based)
- Postgres performs excellently on tables of 50–100 million rows
- When scaling to billions, Citus is recommended
- https://www.citusdata.com - open source that turns Postgres into a distributed DB
[Recommended] Helpful patterns for using PostgreSQL
A write-up of practical patterns that help you use Postgres more productively and safely. Each pattern is small, but together they add up to a big difference.
Use UUID primary keys
- Because UUIDs are random, they have drawbacks in sorting and index performance
- They take up more space than numeric IDs
- But they have the following advantages
- UUIDs can be generated without connecting to the DB
- They can be safely exposed externally
Always add created_at and updated_at fields
- Knowing when a record was created and changed is very useful when debugging
- updated_at can be set up to update automatically via a trigger
- Create the function only once, but the trigger must be applied to each table
Set on update/delete restrict on foreign keys
- Always use on update restrict on delete restrict when setting foreign key constraints
- Prevents accidental cascading deletes when deleting data
- Storage is cheap but data recovery is very hard, so it is better to be conservative
Use schemas
- The default schema is public, but as the application grows it is better to split it into separate schemas
- Schemas work like namespaces, and joins are possible across different schemas
- The more tables there are, the more using schemas helps readability and maintenance
Use the enum table pattern
- Using an enum table instead of PostgreSQL's enum type or a check constraint is more flexible
- Managing enum values in a separate table makes it easy to add metadata or extend the enum values
- Constraints are maintained by referencing the enum table's values with foreign keys
Name tables in the singular
- Table names should be singular rather than plural
- Singular is clearer when writing queries, while plural can cause confusion with possessives or meaning
Name join tables mechanically
- For join tables in many-to-many relationships, naming them by concatenating the two table names is safe and clear
- e.g. person_pet
- Add a unique index on the combination to prevent duplicates
Use soft delete instead of deletion
- Rather than actually deleting data, it is better to use a timestamp field like revoked_at that indicates when it was deleted
- You can track not only whether something was deleted but also when
- A timestamp provides more information than a Boolean value
Represent status with a log table
- Instead of representing status with a single column, store the history of status changes in a separate table
- Specify when the status occurred with a valid_at column
- Set up a latest flag and a unique index + trigger so the latest status can be looked up quickly
- This is advantageous for asynchronous event processing or situations where order can get mixed up
Add system_id to special rows
- Besides enum tables, there are cases where specific "system rows" are needed
- Add a nullable system_id text field and set a unique index on it
- Specific rows can be looked up unambiguously via system_id
Use views sparingly
- Views are useful for abstracting complex queries but are hard to maintain
- Views must be recreated when columns are removed
- Building views on top of views causes performance and readability problems
Make full use of JSON queries
- Postgres is very powerful not only for storing JSON but also for queries that return JSON
- Nested relationships can be returned as JSON in a single query
- You can fetch all the data you need at once without the N+1 problem
- Drawbacks: loss of type information, and all the data must be loaded into memory at once
- The advantages in performance and structure outweigh them
Function pipelines - Adding functional programming to PostgreSQL
- Function pipelines: Building functional programming into PostgreSQL using custom operators
- Makes data analysis with SQL much easier
- Introduces functional programming concepts to SQL using Postgres "custom data types/operators/functions"
- Similar to Pandas, PromQL
SELECT device_id,
ㅤㅤtimevector(ts, val) -> sort() -> delta() -> abs() -> sum()
ㅤㅤㅤㅤas volatility
FROM measurements
WHERE ts >= now()-'1 day'::interval
GROUP BY device_id;
How to back up and restore data
## 데이터베이스 전체 백업
pg_dumpall -f all-backup.sql -U postgres -W
## 특정 데이터베이스 선택 백업
pg_dump -h 127.0.0.1 -p 5432 -U postgres -W --compress=5 -f sample_db-20201219.sql.gz sample
## 특정 데이터베이스의 테이블 백업
pg_dump -h 127.0.0.1 -p 5432 -U postgres -W -d sample -t sample_table -f sample_table-20201219.sql
How to restore from a query file:
What really happens when you drop a column in Postgres
Conclusion: DROP COLUMN is only "hiding", not "deleting"
- It is a design for performance, but as columns pile up you can hit the 1600 limit
- If needed, clean up data with VACUUM FULL or a table rewrite
- Understanding PostgreSQL's internal behavior is very useful from a system design or compliance perspective
Syncing with other databases
Syncing a specific table from another database (DB), such as MariaDB, and replicating it into a PostgreSQL table is possible through various tools and techniques.
The options commonly used for this are:
- Foreign Data Wrapper (FDW)
- A way for PostgreSQL to access and replicate tables in external databases; you can connect to MariaDB using a foreign data wrapper called mysql_fdw.
- Bucardo, SymmetricDS
- These tools support sync between heterogeneous databases, and you can set up data replication from MariaDB to PostgreSQL.
- Bucardo - A master-slave or master-master sync tool used in PostgreSQL environments.
- SymmetricDS - Supports bidirectional and unidirectional sync between MariaDB and PostgreSQL, and can sync per table.
- These tools detect data changes via triggers or logs and sync them to other databases in real time or periodically.
- Using an ETL (Extract, Transform, Load) solution
- You can periodically extract data from MariaDB and load it into PostgreSQL using ETL tools such as Apache NiFi or Talend.
bigint vs uuid
CREATE UNLOGGED TABLE test_bigint (
id bigint GENERATED ALWAYS AS IDENTITY (CACHE 200) PRIMARY KEY
);
Requires the uuid-ossp extension.
serial vs slug
See Uniform Resource Identifier#Slug vs ID.
See also nanoid.
Why you should avoid UUID version 4 primary keys in Postgres
- Why you should avoid UUID version 4 primary keys in Postgres | GeekNews
- UUID v4's high randomness causes index inefficiency and excessive I/O, degrading performance when used as a primary key in PostgreSQL
- Random inserts cause frequent page splits and index fragmentation, increasing WAL log size and write latency
- At 16 bytes, a UUID takes twice the space of a bigint, leading to lower cache hit rates and wasted memory
- It is mistaken for a security identifier, but according to RFC 4122, a UUID is not a security measure against guessing
- For new databases, integer sequence-based keys are recommended, and if unavoidable, time-ordered UUID v7
Meaning of SET LOCAL request.jwt.claims
SET LOCAL request.jwt.claims = '...' is a PostgreSQL command that sets a session variable valid only for the current transaction.
It plays a key role mainly when implementing RLS (Row Level Security) in Supabase or PostgREST environments. Details follow:
Main roles
- Passing authentication information: when an authenticated user sends an HTTP request, the API server (PostgREST, etc.) decodes the JWT token and passes its contents (Claims) to the DB in this variable.
- Transaction locality: because it uses SET LOCAL, it disappears as soon as the database connection closes or the transaction ends. This ensures it does not get mixed with other users' requests.
Why use it? (Relationship with RLS)
- The database's RLS policies reference this value to find out "who the currently logged-in user is".
- For example, to let users see only their own posts in the posts table, an RLS policy like the following is used:
CREATE POLICY "사용자는 자신의 글만 볼 수 있음" ON posts
FOR SELECT USING (
-- request.jwt.claims 내의 'sub' (사용자 ID)와 테이블의 user_id를 비교
auth.uid() = user_id
);
Here, the auth.uid() function internally reads this value in a way like current_setting('request.jwt.claims', true)::json->>'sub'.
Checking it yourself
If you want to check the currently set value in a DB tool, you can query it as follows:
Summary
You can think of this command as temporarily recording "who is running this query right now (ID, role, etc.)" in transaction memory so the DB engine can know it. This allows data to be protected safely at the DB level.
Docker-Compose Example
version: '2.1'
services:
root_db:
image: postgres
restart: always
volumes:
- db_data:/var/lib/postgresql/data
ports:
- "5432:5432"
environment:
POSTGRES_PASSWORD: password
POSTGRES_USER: postgres
POSTGRES_DB: root_db
healthcheck:
test: pg_isready -U "$$POSTGRES_USER" -d "$$POSTGRES_DB"
interval: 10s
timeout: 2s
retries: 10
volumes:
db_data: {}
Database URL: pg://root_db:5432?u=postgres&p=password&d=root_db
One of 7 databases for 2025
- See Database#7 databases for 2025 (GeekNews)
- PostgreSQL is a stable, go-to technology
- The phrase "Just use Postgres" is a widely known meme and an expression symbolizing reliability
- It is ACID-compliant and provides powerful features including physical and logical replication
- It is a stable database with broad support across major vendors
- Unique features can be added through extensions (Extensions)
- Examples of major extensions:
- AGE: support for graph data structures and the Cypher query language
- TimescaleDB: support for working with time-series data
- Hydra Columnar: provides a column-based storage engine
- Extensions are the key element that differentiates PostgreSQL from other databases
- It has a diverse ecosystem, and its defaults are sensible and user-friendly
- Even non-PostgreSQL services use the Postgres wire protocol to provide client compatibility
- It is lightweight enough to be installed even in WebAssembly (Wasm) environments
- It is worth investing time to understand PostgreSQL's possibilities and limitations
- e.g. understanding the complexity of MVCC (Multi-Version Concurrency Control)
- Recommends developing a simple CRUD application, writing a PostgreSQL extension, and so on
Postgres best practices Agent Skills
- Agent Skills released that teach Postgres best practices directly to AI agents | GeekNews
- [Original] Introducing: Postgres Best Practices
Agent Skills released that explicitly provide database rules, to solve the problem of AI coding agents not handling Postgres correctly
- Postgres has decades of accumulated features, edge cases, and performance characteristics, so agents can generate code that works but causes full table scans or omits security policies
- Consists of 30 rules in 8 categories, organized by impact priority, covering query performance, connection management, security and RLS, schema design, and more
- Provided not as documentation for humans to read, but as a rule set that AI agents reference directly
- Combined with an MCP server, you can build a practical database agent that has both execution ability and judgment criteria
The repo is live at https://github.com/supabase/agent-skills
To install a skill, you can use Vercel’s skills npm package to interactively install this skill on your agent.
If you’re using Claude Code, you can also install this skill as a plugin.
/plugin marketplace add supabase/agent-skills
/plugin install postgres-best-practices@supabase-agent-skills
Try it on your next Postgres project, and let us know what rules are missing.
Keeping Postgres queues healthy
See PostgreSQL:Queue#Keeping Postgres queues healthy for details.
Troubleshooting
initdb: error: directory "/var/lib/postgresql/data" exists but is not empty
postgres-1 | initdb: error: directory "/var/lib/postgresql/data" exists but is not empty
postgres-1 | initdb: detail: It contains a dot-prefixed/invisible file, perhaps due to it being a mount point.
postgres-1 | initdb: hint: Using a mount point directly as the data directory is not recommended.
postgres-1 | Create a subdirectory under the mount point.
postgres-1 | chmod: changing permissions of '/var/run/postgresql': Operation not permitted
postgres-1 | The files belonging to this database system will be owned by user "postgres".
postgres-1 | This user must also own the server process.
Just empty the directory, as it says.
error: could not change permissions of directory "/var/lib/postgresql/data": Operation not permitted
postgres-1 | initdb: error: could not change permissions of directory "/var/lib/postgresql/data": Operation not permitted
postgres-1 | fixing permissions on existing directory /var/lib/postgresql/data ... chmod: changing permissions of '/var/lib/postgresql/data': Operation not permitted
postgres-1 | chmod: changing permissions of '/var/run/postgresql': Operation not permitted
postgres-1 | The files belonging to this database system will be owned by user "postgres".
postgres-1 | This user must also own the server process.
Setting PGDATA to a subdirectory of the mount point solves it. This is the method recommended by the official PostgreSQL image.
See also
Favorite site
- PostgreSQL web site
- [Recommended] Korean PostgreSQL homepage
- Wikipedia (en) entry on PostgreSQL
- PostgreSQL at a glance 2
- PostgreSQL special 1) Installation / JDBC installation
- w3ii.com - PostgreSQL C / C ++ interface
- Slideshare - Learning SQL basics with PostgreSQL
- [Recommended] Github - Awesome Postgres
- [Recommended] PostgreSQL INSERT statement (DBeaver, PostgreSQL)
- Basics of creating tables in PostgreSQL
- PostgreSQL 14 Internals : Postgres Professional - free ebook introducing the internals of PostgreSQL 14 (snapshots, buffer cache, WAL, locks, query execution, various indexes)
Article
- [Original] When Did Postgres Become Cool?
- Postgres started becoming Cool with the release of Postgres 9.0 and 9.1 in 2010
- [Original] Just use Postgres
- [Original] Maciej Walkowiak | PostgreSQL and UUID as primary key
- This post does not focus on "whether UUIDs are a suitable format for keys" but explains how to use UUIDs efficiently as primary keys in PostgreSQL
- Before considering Serial vs. Random UUID vs. Ordered UUID in Postgres, there are many other things you should worry about
- [Original] OpenAI: Scaling PostgreSQL to the Next Level | PixelsTech
- At PGConf.dev 2025, OpenAI shared how it effectively handles traffic from hundreds of millions of users while using PostgreSQL without sharding
- To address write bottlenecks, it introduced various approaches such as distributing writes, query optimization, and schema management
- As key issues, it mentioned limitations and operational difficulties inherent in PostgreSQL's architecture, such as table/index bloat from the MVCC design and replication lag caused by WAL
- Query optimization strategies such as distributing read load, limiting long transactions, and minimizing ORM use are central
- OpenAI achieves 1 million QPS through more than 40 geographically distributed replicas and ensures high availability even during failures
Guide
- Why did I choose PostgreSQL? ( PostgreSQL vs MySQL ) (translation)
- PostgreSQL at a glance
- What Notion learned from sharding Postgres | GeekNews
- VACUUM started stalling continuously, and TXID wraparound was expected soon, so sharding work began
- (Original) Herding elephants: Lessons learned from sharding Postgres at Notion
- (Original) Index Merges vs Composite Indexes in Postgres and MySQL
- Composite indexes are 10x faster than index merges. The gap is larger in Postgres than in MySQL, because Postgres does not support index-only scans for queries involving index merges
- If you use Postgres, be careful with using index merges
- Self-hosting Postgres is neither complex nor risky; it is cheaper than managed services and gives freedom in performance tuning
- Most cloud database services run slightly modified open-source Postgres, and the real difference lies in the level of operational automation
- In real-world operation, self-hosted Postgres reliably handles thousands of users and tens of millions of queries, with very little maintenance time
- Due to rising prices of managed services such as AWS RDS, you can run a much higher-spec server yourself for the same cost
- For mid-sized teams for whom infrastructure management is not complex, self-hosting is a realistic alternative in terms of cost efficiency and performance
- Key configuration points
- Memory settings: adjust shared_buffers, effective_cache_size, work_mem, maintenance_work_mem, etc. to fit the hardware
- e.g. set shared_buffers to 25% of RAM and effective_cache_size to 75%
- Connection management: configure connection pooling with pgbouncer; works efficiently in Python asyncio environments
- Provides example base settings such as max_connections = 200, log_connections = on
- Storage tuning: in NVMe SSD environments, adjust to random_page_cost = 1.1, effective_io_concurrency = 200, etc.
- Faster random reads optimize query planning
- WAL settings: adjust wal_level = replica, max_wal_size = 2GB, checkpoint_completion_target = 0.9, etc. for durability and performance
- Memory settings: adjust shared_buffers, effective_cache_size, work_mem, maintenance_work_mem, etc. to fit the hardware
Tip & Trick
- Postgres Tips | Crunchy Data
- Postgres Tips And Tricks - pgDash
- Custom Column for Auto-Updating updated_at in PostgreSQL (Duplicate) - Postgresql
- The osom-db page has several queries.
- How to get the most out of Postgres memory settings | GeekNews
- [Original] Unconventional PostgreSQL Optimizations | Haki Benita
- Introduces 3 creative, out-of-the-box approaches to boosting PostgreSQL query performance that are not adding indexes or rewriting queries
- Eliminating full table scans based on check constraints, optimizing low cardinality with function-based indexes, and implementing unique constraints with Hash indexes
- Uses the constraint_exclusion setting to prevent unnecessary scans for queries with invalid conditions
- Uses function-based indexes and virtual generated columns to reduce index size and ensure query consistency
- Combines Hash indexes with exclusion constraints to efficiently implement unique constraints on large text columns, greatly reducing storage space
- [Original] Postgres-backed Durable Workflow Execution | DBOS
- Durable workflows checkpoint execution state to the database, so after a crash they recover from the last completed step
- External orchestration in the style of Temporal, Airflow, or AWS Step Functions adds a central orchestrator and storage, increasing complexity
- A Postgres-based architecture makes recovery possible by having application servers poll a workflow table and store step outputs directly
- Multiple servers prevent duplicate execution with locks and integrity constraints, and scalability, availability, and observability problems are handled with Postgres solutions
- A single Postgres can scale vertically to tens of thousands of workflows per second, and replication, multi-AZ, and SQL analytics can be used as-is
Project
References
- Amazon Simple Queue Service
- Apache AGE
- Asyncpg
- Babelfish for PostgreSQL
- Biscuit
- Broadcaster
- Calendso
- Citus
- Cloud Computing
- Computer Sciences
- Curriculum
- Database
- Database management system
- Dbcrossbar
- Docker Compose
- EdgeDB
- Entity–relationship model
- Fly:Postgres
- Foreign data wrappers
- GBrain
- Hermes
- Hstore
- HydraDatabase
- IaSQL
- Inquery
- InsForge
- Ivory
- Key-value database
- Libpq
- Lightweight Kubernetes
- MangoDB
- Methesar
- Multiversion concurrency control
- Nano ID
- NocoDB
- OpenAI
- OpenWorkflow
- Optio
- OrioleDB
- PeerDB
- Pg crdt
- Pg flo
- Pg freespacemap
- Pg graphql
- Pg lake
- Pg-status
- Pgactive
- PgAdmin
- PgBackRest
- PgBouncer
- Pgcrypto
- Pgdbtemplate
- PgDog
- PgEdge
- PgFormatter
- PGlite
- Pglite-fusion
- Pgmicro
- PGMQ
- PgQueuer
and 79 more