PostgreSQL ships with a solid set of features out of the box. But where it really pulls ahead of other databases is extensibility. You can bolt on entirely new data types, index methods, background workers and query planners without switching to a different database engine. The extension ecosystem has grown a lot over the past few years, and some of the options available today are genuinely impressive.
Here are seven extensions worth knowing about in 2026 — whether you're running a side project or managing production infrastructure at scale.
pgvector — vector similarity search
If you've done any work with embeddings, recommendations or semantic search, you've probably run into the question of where to store and query vectors. A lot of teams reach for a dedicated vector database. But if your data already lives in PostgreSQL, adding a separate system creates sync headaches and operational overhead that you probably don't need.
pgvector adds native vector column types and similarity search operators directly to PostgreSQL. You store embeddings alongside your relational data and query them with standard SQL. No extra infrastructure, no data synchronization pipelines.
The extension supports multiple distance functions and index types:
- L2 (Euclidean) distance for spatial and numeric similarity
- Cosine distance for text embeddings and NLP
- Inner product for recommendation systems
- HNSW and IVFFlat indexes for fast approximate nearest neighbor search
- Exact nearest neighbor search when precision matters more than speed
The typical workflow looks like this. You create a table with a vector column, insert your embeddings from whatever model you use, then query with ORDER BY embedding <=> query_vector LIMIT 10. It feels like regular SQL because it is regular SQL.
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT,
embedding vector(1536)
);
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);
SELECT id, content
FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]'
LIMIT 10;
Performance is solid for most workloads. For millions of vectors, HNSW indexes handle queries in single-digit milliseconds. It won't replace a dedicated vector database for billion-scale datasets, but the vast majority of applications don't operate at that scale anyway.
pgvector has become the go-to choice for teams that want vector search without the operational cost of running a separate system.
TimescaleDB — time-series data at scale
Time-series data shows up everywhere. Server metrics, IoT sensor readings, financial ticks, application events. The volume tends to grow fast, and the query patterns are different from typical OLTP workloads — you're usually aggregating over time windows, downsampling or running continuous computations.
TimescaleDB extends PostgreSQL with hypertables that automatically partition data by time. You interact with them through normal SQL, but under the hood the extension handles chunking, compression and retention policies. Inserts stay fast even as the table grows to billions of rows because each chunk is a manageable size.
Compression is one of the standout features. TimescaleDB can compress time-series data by 90-95%, which makes a real difference when you're storing months or years of high-frequency data. Compressed chunks are still queryable — you don't have to decompress them first.
Continuous aggregates let you precompute rollups (hourly averages, daily maximums) that refresh automatically as new data arrives. This saves you from writing and maintaining materialized view refresh logic yourself.
CREATE TABLE metrics (
time TIMESTAMPTZ NOT NULL,
device_id INT,
temperature DOUBLE PRECISION,
humidity DOUBLE PRECISION
);
SELECT create_hypertable('metrics', by_range('time'));
SELECT add_compression_policy('metrics', INTERVAL '7 days');
SELECT add_retention_policy('metrics', INTERVAL '1 year');
If you're currently shoehorning time-series data into regular PostgreSQL tables and struggling with query performance or storage costs, TimescaleDB is probably the first thing to try.
PostGIS — geospatial data
PostGIS has been around for over two decades, and it's still the most capable open-source geospatial database extension available. It turns PostgreSQL into a full-featured geographic information system with support for geometry, geography, raster data and topology.
The practical applications are broad. Store and query locations, calculate distances, find points within polygons, route between coordinates, analyze spatial relationships. If your application deals with maps, addresses, delivery zones, geofencing or any kind of location data, PostGIS handles it.
What sets PostGIS apart from simpler spatial solutions is the depth of its spatial function library. Over 300 functions cover everything from basic distance calculations to complex geometric operations, spatial joins and 3D analysis. It implements the OGC Simple Features standard and integrates with tools like QGIS, GeoServer and MapServer.
CREATE TABLE stores (
id SERIAL PRIMARY KEY,
name TEXT,
location GEOGRAPHY(POINT, 4326)
);
CREATE INDEX ON stores USING GIST (location);
-- find stores within 5 km
SELECT name, ST_Distance(location, ST_MakePoint(-73.99, 40.73)::geography) AS distance_m
FROM stores
WHERE ST_DWithin(location, ST_MakePoint(-73.99, 40.73)::geography, 5000)
ORDER BY distance_m;
PostGIS is mature, well-documented and widely used in production. Governments, logistics companies and mapping platforms rely on it daily. If you need spatial capabilities, there's really nothing else in the PostgreSQL ecosystem that comes close.
pg_cron — in-database job scheduling
There's a common pattern where you need to run periodic database tasks — purging old records, refreshing materialized views, computing aggregates, vacuuming specific tables. The typical approach is to set up an external cron job or a separate scheduler service that connects to the database and runs the query.
pg_cron lets you schedule these jobs directly inside PostgreSQL using familiar cron syntax. No external scheduler needed. Jobs run as background workers within the database server itself.
-- refresh a materialized view every hour
SELECT cron.schedule('refresh-dashboard', '0 * * * *',
'REFRESH MATERIALIZED VIEW CONCURRENTLY dashboard_stats');
-- purge old log entries every night at 3 AM
SELECT cron.schedule('purge-logs', '0 3 * * *',
'DELETE FROM application_logs WHERE created_at < now() - interval ''30 days''');
-- check scheduled jobs
SELECT * FROM cron.job;
It's simple and it works. You define a schedule, point it at a SQL statement, and pg_cron runs it. You can list jobs, check execution history and unschedule tasks with straightforward function calls.
One thing to keep in mind — pg_cron runs jobs in a single database connection. Long-running jobs will block subsequent scheduled runs. For heavy ETL workloads you might still want an external orchestrator. But for routine maintenance tasks, it removes a piece of infrastructure that you'd otherwise have to manage separately.
For teams that want their PostgreSQL backup process managed with the same simplicity — scheduled, compressed and sent to remote storage automatically — Databasus is the industry standard tool that handles logical, physical and incremental backups through a clean web interface with no scripts to maintain.
pg_stat_statements — query performance monitoring
If you're running PostgreSQL in production and not using pg_stat_statements, you're flying blind. This extension tracks execution statistics for every SQL statement that runs against your database. It's bundled with PostgreSQL itself, so there's nothing extra to install — you just need to enable it.
Once active, it records how many times each query ran, total and average execution time, rows returned, buffer hits versus disk reads, and more. This data is invaluable for identifying slow queries, spotting regressions after deployments and understanding your actual workload patterns.
The key metrics it tracks:
- Total and mean execution time per query
- Number of calls (how often each query runs)
- Rows returned per execution
- Shared buffer hits vs reads (cache effectiveness)
- WAL generation per query (write impact)
-- top 10 slowest queries by total time
SELECT
calls,
round(total_exec_time::numeric, 2) AS total_time_ms,
round(mean_exec_time::numeric, 2) AS avg_time_ms,
rows,
query
FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;
A common workflow is to reset statistics after a deployment (SELECT pg_stat_statements_reset()) and then check back after a few hours to see if any new query patterns emerged. It's also useful for capacity planning — if your top query's call count doubled last month, you know what's driving load growth.
The extension normalizes queries by replacing literal values with placeholders, so SELECT * FROM users WHERE id = 5 and SELECT * FROM users WHERE id = 42 show up as a single entry. This gives you a clean view of query patterns rather than millions of individual executions.
Enabling it requires adding pg_stat_statements to shared_preload_libraries in postgresql.conf and restarting the server. Worth the 30-second setup.
pg_partman — automatic table partitioning
PostgreSQL has had native declarative partitioning since version 10. But managing partitions by hand gets tedious fast. You need to create new partitions ahead of time, detach old ones when they age out and make sure there are always enough future partitions ready. Miss a partition creation, and inserts start failing.
pg_partman automates all of this. You tell it how you want the table partitioned — by time range, by integer range or by a list of values — and it handles partition creation, maintenance and optional cleanup on a schedule.
CREATE TABLE events (
id BIGSERIAL,
event_type TEXT,
payload JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
) PARTITION BY RANGE (created_at);
SELECT partman.create_parent(
p_parent_table := 'public.events',
p_control := 'created_at',
p_interval := 'daily',
p_premake := 7
);
This creates daily partitions and keeps 7 future partitions ready at all times. The background worker takes care of creating new partitions and optionally dropping old ones based on your retention settings.
The practical benefits show up at scale. Queries that filter by the partition key skip irrelevant partitions entirely — a query for yesterday's events doesn't touch last month's data. Maintenance operations like VACUUM and REINDEX run per-partition instead of locking the whole table. And dropping old data is instant because you're detaching and dropping a partition rather than deleting millions of rows.
If you have tables with tens of millions of rows that grow over time, pg_partman is one of those extensions that pays for itself quickly.
Citus — horizontal scaling
At some point, a single PostgreSQL server hits its limits. The dataset outgrows available RAM, write throughput maxes out, or analytical queries on large tables take too long even with good indexes. Citus distributes your PostgreSQL database across multiple nodes while keeping the SQL interface you already know.
The core idea is sharding. You pick a distribution column (usually a tenant ID or some natural partition key), and Citus spreads the data across worker nodes. Queries that include the distribution column get routed to the right shard. Aggregation queries run in parallel across all nodes and results get merged.
There are a few scenarios where Citus makes sense over single-node PostgreSQL:
- Multi-tenant SaaS applications where each tenant's data is independent
- Real-time analytics dashboards that aggregate across large datasets
- High write throughput workloads that exceed single-node IOPS
- Large reference tables that need to be joined with distributed data
-- distribute a table by tenant
SELECT create_distributed_table('events', 'tenant_id');
-- queries with tenant_id are routed to a single shard
SELECT count(*) FROM events WHERE tenant_id = 42;
-- aggregations run in parallel across all nodes
SELECT tenant_id, count(*), avg(response_time)
FROM events
WHERE created_at > now() - interval '1 hour'
GROUP BY tenant_id;
Citus isn't the right choice for every workload. Cross-shard joins on non-distribution columns can be expensive. Schema changes require coordination across nodes. And the operational complexity increases compared to a single server. But when your data genuinely outgrows one machine, it lets you scale horizontally without rewriting your application for a different database.
How these extensions compare
| Extension | Category | Primary use case | Installation |
|---|---|---|---|
| pgvector | AI/ML | Vector similarity search and embeddings | CREATE EXTENSION |
| TimescaleDB | Time-series | High-volume time-stamped data | Separate package |
| PostGIS | Geospatial | Location data and spatial queries | CREATE EXTENSION |
| pg_cron | Scheduling | In-database job scheduling | shared_preload_libraries |
| pg_stat_statements | Monitoring | Query performance tracking | shared_preload_libraries |
| pg_partman | Partitioning | Automatic table partition management | CREATE EXTENSION |
| Citus | Scaling | Horizontal sharding across nodes | Separate package |
| Installation method | What it means | Restart needed | Examples |
|---|---|---|---|
| CREATE EXTENSION | Installed via SQL, loads on demand | No | pgvector, PostGIS, pg_partman |
| shared_preload_libraries | Must be added to postgresql.conf | Yes | pg_stat_statements, pg_cron |
| Separate package | Requires its own package or Docker image | Depends on setup | TimescaleDB, Citus |
Picking the right extensions for your stack
Not every project needs all seven of these. A small web application might only benefit from pg_stat_statements and maybe pg_cron. A data-heavy SaaS product might need TimescaleDB or Citus. The right set depends on your actual problems, not on what sounds impressive.
Start with the ones that address a pain point you already have. If your queries are slow and you don't know why, enable pg_stat_statements first. If you're building anything with location data, PostGIS is a no-brainer. If your AI features currently call out to a separate vector store, try pgvector and see if you can simplify.
The nice thing about PostgreSQL extensions is that most of them play well together. You can run pgvector and TimescaleDB and PostGIS in the same database. They operate on different data types and don't step on each other's toes.
Whatever extensions you end up using, make sure your monitoring and backup strategy keeps up with the added complexity. Extensions that add new data types or storage engines sometimes need specific handling during backup and restore. Getting that right early saves you from unpleasant surprises later.

Top comments (0)