postgres
18.4-alpinePre-baked PostgreSQL extension variants — vector / paradedb / timescale / postgis / citus — built fresh against each PG major. One image per use case instead of a hand-rolled Dockerfile.
Trust posture
VERIFIABLE TRUST ARTIFACTS
Provenance
- Build commit
ed67d41- Build digest
-
sha256:35e93acf41466a22547c01c134d3d74b82c8b8ac92f2a0dee96e27a281d97679 - Index digest
-
sha256:7f49f5a93815e31b7a9b74cee939fd08c4129a626097bf0f6ab98124cc246ea1 - Manifest digest (amd64)
-
sha256:cf198d9fb8f504248476bfd8257ffda18310785790aff78dedd723622c7d9b78 - Manifest digest (arm64)
-
sha256:e33118daff03e23da2ba9b091ae5ce8ae8e425fca8056f320d8c2fed79bc2765 - SBOM attestation
-
34817625… - Trivy last scan
- 2026-07-10 20:41 UTC
- Base image
- Verify
gh attestation verify oci://ghcr.io/oorabona/postgres:18-alpine --owner oorabonaguide →
Security scan results
Trivy · last scan 2026-07-10
→ Full report via gh api — see Verify Images.
Explore
Available variants
| Tag | Size (amd64) | Build status | Version |
|---|---|---|---|
18-alpine default | 114.4MB | OK | 18-alpine |
18-alpine-vector | 278.6MB | OK | 18-alpine |
18-alpine-analytics | 181.8MB | OK | 18-alpine |
18-alpine-timeseries | 265.6MB | OK | 18-alpine |
18-alpine-spatial | 179.5MB | OK | 18-alpine |
18-alpine-distributed | 116.7MB | OK | 18-alpine |
18-alpine-full | 431.8MB | OK | 18-alpine |
17-alpine default | 111.7MB | OK | 17-alpine |
17-alpine-vector | 276.0MB | OK | 17-alpine |
17-alpine-analytics | 180.9MB | OK | 17-alpine |
17-alpine-timeseries | 264.7MB | OK | 17-alpine |
17-alpine-spatial | 178.6MB | OK | 17-alpine |
17-alpine-distributed | 115.8MB | OK | 17-alpine |
17-alpine-full | 430.9MB | OK | 17-alpine |
16-alpine default | 110.7MB | OK | 16-alpine |
16-alpine-vector | 274.9MB | OK | 16-alpine |
16-alpine-analytics | 179.8MB | OK | 16-alpine |
16-alpine-timeseries | 263.6MB | OK | 16-alpine |
16-alpine-spatial | 177.5MB | OK | 16-alpine |
16-alpine-distributed | 114.7MB | OK | 16-alpine |
16-alpine-full | 429.8MB | OK | 16-alpine |
Available variants 21 variants · 3 versions
18-alpine
17-alpine
16-alpine
Variant comparison 7 flavors · 3 versions
Showing PostgreSQL 18. Switch a variant tag from another version to see its rows.
| Variant | Extensions | Image size (amd64) | When to use |
|---|---|---|---|
base |
114.4MB | Standard PostgreSQL with built-in extensions only — pick when you don't need vector search, analytics, or geospatial. | |
vector |
pgvectorparadedbpg_cronpg_ivm | 278.6MB | PG with pgvector and paradedb — pick for AI/ML embeddings, similarity search, or full-text search. |
analytics |
pg_partmanhypopgpg_qualstatspostgispg_cronpg_ivm | 181.8MB | PG with paradedb, pg_ivm, pg_partman — pick for OLAP, materialized views, partitioning. |
timeseries |
timescaledbpg_partmanpostgispg_cronpg_ivm | 265.6MB | PG with timescaledb — pick for IoT, metrics, time-series workloads. |
spatial |
postgispg_cronpg_ivm | 179.5MB | PG with postgis and pg_partman — pick for GIS, geospatial queries, partitioned spatial data. |
distributed |
cituspg_cronpg_ivm | 116.7MB | PG with citus and pg_partman — pick for sharding, multi-tenant, horizontal scale. |
full |
pgvectorparadedbpg_partmanhypopgpg_qualstatspostgiscitustimescaledbpg_cronpg_ivm | 431.8MB | All extensions enabled — pick when prototyping or when scope is not yet defined. |
| Variant | Extensions | Image size (amd64) | When to use |
|---|---|---|---|
base |
111.7MB | Standard PostgreSQL with built-in extensions only — pick when you don't need vector search, analytics, or geospatial. | |
vector |
pgvectorparadedbpg_cronpg_ivm | 276.0MB | PG with pgvector and paradedb — pick for AI/ML embeddings, similarity search, or full-text search. |
analytics |
pg_partmanhypopgpg_qualstatspostgispg_cronpg_ivm | 180.9MB | PG with paradedb, pg_ivm, pg_partman — pick for OLAP, materialized views, partitioning. |
timeseries |
timescaledbpg_partmanpostgispg_cronpg_ivm | 264.7MB | PG with timescaledb — pick for IoT, metrics, time-series workloads. |
spatial |
postgispg_cronpg_ivm | 178.6MB | PG with postgis and pg_partman — pick for GIS, geospatial queries, partitioned spatial data. |
distributed |
cituspg_cronpg_ivm | 115.8MB | PG with citus and pg_partman — pick for sharding, multi-tenant, horizontal scale. |
full |
pgvectorparadedbpg_partmanhypopgpg_qualstatspostgiscitustimescaledbpg_cronpg_ivm | 430.9MB | All extensions enabled — pick when prototyping or when scope is not yet defined. |
| Variant | Extensions | Image size (amd64) | When to use |
|---|---|---|---|
base |
110.7MB | Standard PostgreSQL with built-in extensions only — pick when you don't need vector search, analytics, or geospatial. | |
vector |
pgvectorparadedbpg_cronpg_ivm | 274.9MB | PG with pgvector and paradedb — pick for AI/ML embeddings, similarity search, or full-text search. |
analytics |
pg_partmanhypopgpg_qualstatspostgispg_cronpg_ivm | 179.8MB | PG with paradedb, pg_ivm, pg_partman — pick for OLAP, materialized views, partitioning. |
timeseries |
timescaledbpg_partmanpostgispg_cronpg_ivm | 263.6MB | PG with timescaledb — pick for IoT, metrics, time-series workloads. |
spatial |
postgispg_cronpg_ivm | 177.5MB | PG with postgis and pg_partman — pick for GIS, geospatial queries, partitioned spatial data. |
distributed |
cituspg_cronpg_ivm | 114.7MB | PG with citus and pg_partman — pick for sharding, multi-tenant, horizontal scale. |
full |
pgvectorparadedbpg_partmanhypopgpg_qualstatspostgiscitustimescaledbpg_cronpg_ivm | 429.8MB | All extensions enabled — pick when prototyping or when scope is not yet defined. |
Build lineagesha256:
Package summary n/a — runtime parsed
Recent changes n/a — runtime parsed
Build history n/a — runtime fetched
Dependency health 1 update available
Dependency Health
1 update available| Dependency | Current | Latest | Type |
|---|---|---|---|
| paradedb | 0.24.1 | 0.24.2 | patch |
Up-to-date dependencies
Documentation
README
PostgreSQL Database Container
Production-ready PostgreSQL containers with multiple flavors optimized for different workloads: AI/RAG, analytics, or general purpose. Built on Alpine Linux with pre-compiled extensions.
Verify this image
Every build ships a Sigstore-signed SBOM and a full Trivy scan — verify them yourself, no login required:
gh attestation verify oci://ghcr.io/oorabona/postgres:17-alpine --owner oorabona
Full walkthrough (SBOM payload, Trivy findings, multi-arch manifest inspection, upstream dependency tracking) → https://oorabona.github.io/docker-containers/verify-images/
Quick Start
# Base PostgreSQL (smallest image)
docker pull ghcr.io/oorabona/postgres:17-alpine
# With pgvector for AI/RAG applications
docker pull ghcr.io/oorabona/postgres:17-vector-alpine
# With analytics extensions
docker pull ghcr.io/oorabona/postgres:17-analytics-alpine
# With TimescaleDB for time-series data
docker pull ghcr.io/oorabona/postgres:17-timeseries-alpine
# With PostGIS for geospatial data
docker pull ghcr.io/oorabona/postgres:17-spatial-alpine
# With Citus for distributed PostgreSQL
docker pull ghcr.io/oorabona/postgres:17-distributed-alpine
# All extensions included
docker pull ghcr.io/oorabona/postgres:17-full-alpine
Available Flavors
| Flavor | Description | Extensions | Use Case |
|---|---|---|---|
| base | Standard PostgreSQL | Built-in only | General purpose, smallest size |
| vector | AI/ML optimized | + pgvector, paradedb, pg_cron, pg_ivm | RAG, embeddings, full-text search |
| analytics | Data warehouse | + pg_partman, hypopg, pg_qualstats, postgis, pg_cron, pg_ivm | Large tables, query tuning, geospatial |
| timeseries | Time-series data | + TimescaleDB, pg_partman, postgis, pg_cron, pg_ivm | IoT, metrics, logs |
| spatial | Geospatial | + postgis, pg_cron, pg_ivm | GIS, mapping, location data |
| distributed | Horizontal scaling | + Citus, pg_cron, pg_ivm | Multi-node clusters, sharding |
| full | Everything | All extensions | Development, testing |
Flavor Details
Base (*-alpine)
Standard PostgreSQL with built-in extensions:
pg_stat_statements- Query statisticspgcrypto- Cryptographic functionsuuid-ossp- UUID generationbtree_gin,btree_gist- Additional index typespg_trgm- Trigram matching for fuzzy search
Vector (*-vector-alpine)
Includes base + pgvector for AI/ML workloads:
-- Store embeddings from OpenAI, Anthropic, etc.
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT,
embedding vector(1536) -- OpenAI ada-002 dimension
);
-- Create HNSW index for fast similarity search
CREATE INDEX ON documents USING hnsw (embedding vector_cosine_ops);
-- Find similar documents
SELECT * FROM documents
ORDER BY embedding <=> '[0.1, 0.2, ...]'::vector
LIMIT 10;
Analytics (*-analytics-alpine)
Includes base + extensions for data warehousing:
- pg_partman - Automatic partition management for time-series data
- hypopg - Hypothetical indexes for query planning
- pg_qualstats - Predicate statistics for index suggestions
- pg_buffercache - Buffer cache inspection
- pg_prewarm - Data preloading
-- Auto-partition a time-series table
SELECT partman.create_parent(
p_parent_table := 'public.events',
p_control := 'created_at',
p_interval := 'daily'
);
-- Check hypothetical index benefit
SELECT hypopg_create_index('CREATE INDEX ON users(email)');
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';
Timeseries (*-timeseries-alpine)
Includes base + extensions for time-series workloads:
- TimescaleDB - High-performance time-series database
- pg_partman - Automatic partition management
-- Create a hypertable for time-series data
CREATE TABLE metrics (
time TIMESTAMPTZ NOT NULL,
device_id TEXT,
temperature DOUBLE PRECISION
);
SELECT create_hypertable('metrics', by_range('time'));
-- Use time_bucket for aggregations
SELECT time_bucket('1 hour', time) AS bucket,
device_id,
AVG(temperature) AS avg_temp
FROM metrics
WHERE time > NOW() - INTERVAL '1 day'
GROUP BY bucket, device_id
ORDER BY bucket DESC;
Spatial (*-spatial-alpine)
Includes base + PostGIS for geospatial workloads:
- PostGIS - Geospatial types, indexing, and functions
- pg_cron - Scheduled jobs
- pg_ivm - Incremental materialized views
CREATE EXTENSION postgis;
CREATE TABLE places (
id SERIAL PRIMARY KEY,
name TEXT,
location GEOGRAPHY(POINT, 4326)
);
-- Find places within 10km
SELECT name, ST_Distance(location, ST_MakePoint(-73.99, 40.73)::geography) AS dist
FROM places
WHERE ST_DWithin(location, ST_MakePoint(-73.99, 40.73)::geography, 10000)
ORDER BY dist;
Distributed (*-distributed-alpine)
Includes base + Citus for horizontal scaling:
- Citus - Distributed PostgreSQL for multi-node clusters
-- Create a distributed table
SELECT citus_set_coordinator_host('coordinator', 5432);
SELECT create_distributed_table('events', 'tenant_id');
-- Or create a reference table (replicated across nodes)
SELECT create_reference_table('config');
-- Queries are automatically distributed
SELECT tenant_id, COUNT(*)
FROM events
GROUP BY tenant_id;
Full (*-full-alpine)
All extensions for development and testing. Includes everything from all other flavors (vector, analytics, timeseries, spatial, distributed).
Supported Versions
| Version | Flavors | Status |
|---|---|---|
| PostgreSQL 18 | base, vector, analytics, timeseries, spatial, distributed, full | Latest |
| PostgreSQL 17 | base, vector, analytics, timeseries, spatial, distributed, full | Recommended |
| PostgreSQL 16 | base, vector, analytics, timeseries, spatial, distributed, full | LTS |
Image Tags
ghcr.io/oorabona/postgres:{version}-{flavor}-alpine
Examples:
17-alpineor17-base-alpine- PG17 base17-vector-alpine- PG17 with pgvector16-analytics-alpine- PG16 with analytics extensions17-full-alpine- PG17 with all extensions
Usage
Docker Compose
services:
postgres:
image: ghcr.io/oorabona/postgres:17-vector-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: myuser
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U myuser -d myapp"]
interval: 30s
timeout: 10s
retries: 3
volumes:
postgres_data:
Docker Run
docker run -d \
--name postgres \
-e POSTGRES_DB=myapp \
-e POSTGRES_USER=myuser \
-e POSTGRES_PASSWORD=secret \
-p 5432:5432 \
-v postgres_data:/var/lib/postgresql/data \
ghcr.io/oorabona/postgres:17-vector-alpine
Building Locally
# Build base flavor
docker build --build-arg VERSION=17-alpine --build-arg FLAVOR=base -t postgres:17 .
# Build vector flavor
docker build --build-arg VERSION=17-alpine --build-arg FLAVOR=vector -t postgres:17-vector .
# Build with all extensions
docker build --build-arg VERSION=17-alpine --build-arg FLAVOR=full -t postgres:17-full .
Configuration
Environment Variables
| Variable | Description | Default |
|---|---|---|
POSTGRES_DB |
Database name | postgres |
POSTGRES_USER |
Database user | postgres |
POSTGRES_PASSWORD |
User password | (required) |
POSTGRES_INITDB_ARGS |
Additional initdb arguments | |
PGDATA |
Data directory | /var/lib/postgresql/data |
Initialization Scripts
Place .sql or .sh files in a volume mounted to /docker-entrypoint-initdb.d/:
volumes:
- ./init:/docker-entrypoint-initdb.d
Scripts run alphabetically on first container start:
init/
├── 01-schema.sql
├── 02-seed-data.sql
└── 03-setup.sh
Performance Tuning
For production workloads, consider these settings in postgresql.conf:
# Memory
shared_buffers = 256MB
effective_cache_size = 1GB
work_mem = 16MB
maintenance_work_mem = 128MB
# Write-Ahead Log
wal_buffers = 16MB
checkpoint_completion_target = 0.9
max_wal_size = 2GB
# Query Planner
random_page_cost = 1.1
effective_io_concurrency = 200
default_statistics_target = 100
Extensions Reference
Compiled Extensions
Extension versions as of the last dashboard build (2026-07-10).
| Extension | Version | Description | Flavors | License |
|---|---|---|---|---|
| pgvector | 0.8.1 | Vector similarity search | vector, full | PostgreSQL |
| ParadeDB (pg_search) | 0.21.8 | BM25 full-text search | vector, full | AGPL-3.0 |
| pg_partman | 5.4.2 | Partition management | analytics, timeseries, full | PostgreSQL |
| hypopg | 1.4.2 | Hypothetical indexes | analytics, full | PostgreSQL |
| pg_qualstats | 2.1.3 | Predicate statistics | analytics, full | PostgreSQL |
| PostGIS | 3.6.2 | Geospatial types and functions | analytics, timeseries, spatial, full | GPL-2.0 |
| TimescaleDB | 2.27.1 | Time-series database | timeseries, full | Apache-2.0 + TSL |
| Citus | 14.0.0 | Distributed PostgreSQL | distributed, full | AGPL-3.0 |
| pg_cron | 1.6.7 | Job scheduler | vector, analytics, timeseries, spatial, distributed, full | PostgreSQL |
| pg_ivm | 1.13 | Incremental materialized views | vector, analytics, timeseries, spatial, distributed, full | PostgreSQL |
Built-in Extensions
All flavors include these PostgreSQL contrib extensions:
pg_stat_statements- Query performance statisticspgcrypto- Cryptographic functionsuuid-ossp- UUID generationbtree_gin/btree_gist- Additional index typespg_trgm- Fuzzy string matching
Analytics and full flavors also include:
pg_buffercache- Shared buffer inspectionpg_prewarm- Buffer cache preloadingfile_fdw/postgres_fdw- Foreign data wrappers (full only)
TimescaleDB Version Retention
Why this exists. TimescaleDB uses a version-agnostic loader: on first backend access it dlopens the exact timescaledb-<version>.so recorded in the database’s pg_extension catalog. If the image shipped only the current .so, a persisted database created on an older TimescaleDB version would become unstartable after an image upgrade (FATAL: could not access file "$libdir/timescaledb-<old>.so"). To prevent that, the timeseries and full flavors retain a window of TimescaleDB versions.
What is retained. The image ships up to version_set.retain_count (default 12) of the most-recent TimescaleDB versions per PostgreSQL major that successfully build for the Alpine/musl platform. A persisted database on any version within that window starts without any migration — PostgreSQL loads the .so matching the catalog version. Versions that fail to compile under musl are tolerated and excluded; they are recorded in the versionset artifact (.build-lineage/ext-timescaledb-pg<major>-versionset.json, excluded[]) and not shipped. The retain count is configurable via version_set.retain_count in postgres/extensions/config.yaml. The retained window is derived from the upstream timescale/timescaledb-ha support matrix, so it differs per PostgreSQL major (a TimescaleDB version that never supported a given PostgreSQL major is not retained for it):
| PostgreSQL major | Upstream support window (floor → ceiling) | Default image retains |
|---|---|---|
| 18 | 2.23.0 → 2.27.1 | up to 12 most-recent |
| 17 | 2.17.2 → 2.27.1 | up to 12 most-recent |
| 16 | 2.13.0 → 2.27.1 | up to 12 most-recent |
The table shows the upstream support window per PostgreSQL major. The shipped image retains the 12 most-recent versions from that window (configurable via version_set.retain_count in postgres/extensions/config.yaml). Databases on versions older than the retained window use ALTER EXTENSION timescaledb UPDATE to migrate to the ceiling.
The ceiling — currently 2.27.1 — is the pinned version in postgres/extensions/config.yaml; the floor and retained count track upstream and refresh automatically as new TimescaleDB versions are released and the pin is bumped.
The timescaledb.control file sets default_version to the pinned ceiling, so a fresh CREATE EXTENSION timescaledb installs the latest version.
How retained versions are shipped. All retained versions for a given PostgreSQL major are assembled into a single per-major bundle image (ghcr.io/<owner>/ext-timescaledb:pg<major>-bundle). The bundle stores each version’s files under /<ver>/{extension,lib}/. The postgres Dockerfile consumes the bundle with one COPY --from=<bundle> / /tmp/ext/timescaledb/ instruction — one layer regardless of how many versions are retained (up to retain_count default 12). The install_ext shell function in the Dockerfile iterates /tmp/ext/timescaledb/ subdirectories and installs each version’s .so; the ceiling version’s .control file is written last so default_version reflects the pinned ceiling. To move an existing database to a newer version, run ALTER EXTENSION timescaledb UPDATE; (see below).
Local build requirement: Building the
timeseriesorfullflavors locally (e.g../make build postgres --flavor timeseries) requiresskopeoon the build host. When the versionset artifact is not present (it is produced by the CI extension build job), the Dockerfile generator resolves the retained TimescaleDB set by querying the upstream registry viaskopeo list-tags. Install withsudo apt-get install -y skopeo(Debian/Ubuntu) orbrew install skopeo(macOS). macOS also requires GNU coreutils (brew install coreutils) because the version-set resolution usessort -V, which is not available in the BSD sort shipped with macOS.
Moving an existing database to the pinned version
To upgrade TimescaleDB in a persisted database (e.g. after pulling an image with a newer ceiling), connect to each database that has TimescaleDB installed and run:
ALTER EXTENSION timescaledb UPDATE;
The migration SQL for all retained versions is bundled in the image. The database continues to start on the old .so until you run this command — no forced migration on boot.
Note:
/docker-entrypoint-initdb.d/scripts run only on a fresh (empty) data directory and have no effect on existing volumes. To upgrade TimescaleDB in an existing database, always use the manualALTER EXTENSIONcommand above.
Security
Credential Management
Never hardcode passwords:
# BAD
environment:
POSTGRES_PASSWORD: mysecretpassword
# GOOD - Environment variable
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
# BETTER - Docker secrets
secrets:
postgres_password:
file: ./secrets/postgres_password.txt
Runtime Hardening
services:
postgres:
image: ghcr.io/oorabona/postgres:17-alpine
read_only: true
tmpfs:
- /tmp
- /run/postgresql
cap_drop:
- ALL
cap_add:
- CHOWN
- SETGID
- SETUID
- DAC_OVERRIDE
security_opt:
- no-new-privileges:true
ports:
- "127.0.0.1:5432:5432" # Local only
Network Security
- Bind to
127.0.0.1for local-only access - Use Docker networks for service communication
- Enable SSL for remote connections
Monitoring
Health Check
# Check if PostgreSQL is ready
docker exec postgres pg_isready -U myuser -d myapp
# Connection test
docker exec postgres psql -U myuser -d myapp -c "SELECT 1"
Query Statistics
-- Enable pg_stat_statements (already enabled in all flavors)
-- Top 10 slowest queries
SELECT query, calls, mean_exec_time, total_exec_time
FROM pg_stat_statements
ORDER BY mean_exec_time DESC
LIMIT 10;
Buffer Cache Analysis (analytics/full)
-- Buffer cache usage by table
SELECT c.relname,
count(*) AS buffers,
pg_size_pretty(count(*) * 8192) AS size
FROM pg_buffercache b
JOIN pg_class c ON b.relfilenode = c.relfilenode
GROUP BY c.relname
ORDER BY buffers DESC
LIMIT 10;
Backup & Restore
Create Backup
# SQL dump
docker exec postgres pg_dump -U myuser myapp > backup.sql
# Binary backup (faster for large DBs)
docker exec postgres pg_basebackup -U myuser -D /backup -Ft -z
Restore
# From SQL dump
docker exec -i postgres psql -U myuser -d myapp < backup.sql
# Create fresh database from backup
docker exec postgres createdb -U myuser myapp_restored
docker exec -i postgres psql -U myuser -d myapp_restored < backup.sql
Version Management
# Check current version
./version.sh
# Check latest upstream version
./version.sh latest
# Output format (JSON for CI integration)
./version.sh --json
Architecture
postgres/
├── Dockerfile # Multi-flavor build
├── variants.yaml # Version/flavor matrix
├── extensions/
│ ├── config.yaml # Extension definitions
│ ├── build/ # Build scripts per extension
│ └── artifacts/ # Compiled extension tarballs
├── flavors/
│ ├── base.yaml # Base flavor config
│ ├── vector.yaml # Vector flavor config
│ ├── analytics.yaml # Analytics flavor config
│ ├── timeseries.yaml # Timeseries flavor config
│ ├── spatial.yaml # Spatial flavor config
│ ├── distributed.yaml # Distributed flavor config
│ └── full.yaml # Full flavor config
└── custom-init/ # Custom initialization scripts
Creating Custom Flavors
You can create your own flavor by combining extensions to match your specific needs.
Step 1: Create a Flavor Definition
Create a new file in flavors/:
# flavors/myapp.yaml
name: myapp
description: "Custom flavor for my application"
extends: base
# Select which compiled extensions to include
extensions:
- pgvector # For AI features
- pg_partman # For time-series data
# Additional built-in extensions
builtin_extensions:
- pg_buffercache
# Extensions requiring shared_preload_libraries
shared_preload_libraries: []
# Image tags to publish
tags:
- "{version}-myapp-alpine"
- "{major}-myapp-alpine"
Step 2: Update variants.yaml
Add your variant to the version matrix:
# variants.yaml
versions:
- tag: "17"
variants:
# ... existing variants ...
- name: myapp
suffix: "-myapp"
flavor: myapp
description: "Custom flavor for my application"
Step 3: Update Dockerfile
Add your flavor to the install script in the Dockerfile:
case "${FLAVOR}" in
# ... existing flavors ...
myapp) \
install_ext pgvector; \
install_ext pg_partman \
;; \
esac
And create the initialization script:
RUN case "${FLAVOR}" in
# ... existing flavors ...
myapp) \
printf '%s\n' \
'-- MyApp flavor extensions' \
'CREATE EXTENSION IF NOT EXISTS vector;' \
'CREATE EXTENSION IF NOT EXISTS pg_partman;' \
> /docker-entrypoint-initdb.d/01-init-flavor.sql \
;; \
esac
Step 4: Build Your Flavor
# Build locally
docker build \
--build-arg VERSION=17-alpine \
--build-arg FLAVOR=myapp \
-t postgres:17-myapp .
# Test it
docker run -d --name pg-test \
-e POSTGRES_PASSWORD=test \
postgres:17-myapp
# Verify extensions
docker exec pg-test psql -U postgres -c "\dx"
Adding New Extensions
To add an extension not yet supported:
- Create build script in
extensions/build/:# extensions/build/myext.sh #!/bin/bash git clone https://github.com/org/myext.git cd myext make USE_PGXS=1 make USE_PGXS=1 install DESTDIR=/output - Add to config.yaml:
extensions: myext: version: "1.0.0" description: "My custom extension" repo: "org/myext" build_deps: - build-base shared_preload: false - Build the extension:
./scripts/build-extensions.sh postgres myext - Reference in Dockerfile using
COPY --from=
Roadmap
Planned Extensions
(none currently — all target extensions have been integrated)
Future Improvements
- PostgreSQL 18 extension support (once extensions are updated)
- ARM64 optimized builds (native CI runners)
- pg_stat_monitor integration