Back to Dashboard

postgres

18.4-alpine

Pre-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

📋 SBOM ATTESTED🛡 TRIVY: 1 CRITICAL (advisory) · SCANNED 2026-07-10🏗 AMD64 + ARM64 🔗 10/10 deps tracked daily 🔍 REPRODUCE THESE CHECKS LOCALLY
PULLS 72.5K
STARS 0
COVERAGE 21 variants · 3 versions · amd64+arm64

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 oorabona guide →

Security scan results

Trivy · last scan 2026-07-10

Last scan: (advisory mode — Trivy runs continue-on-error in CI; we surface findings, we do not block builds on them).

1Critical
14High
21Medium
2Low
2Info

Top advisories (upstream, advisory)

    → Full report via gh api — see Verify Images.

    Explore

    Available variants

    TagSize (amd64) Build statusVersion
    18-alpine default114.4MB OK18-alpine
    18-alpine-vector278.6MB OK18-alpine
    18-alpine-analytics181.8MB OK18-alpine
    18-alpine-timeseries265.6MB OK18-alpine
    18-alpine-spatial179.5MB OK18-alpine
    18-alpine-distributed116.7MB OK18-alpine
    18-alpine-full431.8MB OK18-alpine
    17-alpine default111.7MB OK17-alpine
    17-alpine-vector276.0MB OK17-alpine
    17-alpine-analytics180.9MB OK17-alpine
    17-alpine-timeseries264.7MB OK17-alpine
    17-alpine-spatial178.6MB OK17-alpine
    17-alpine-distributed115.8MB OK17-alpine
    17-alpine-full430.9MB OK17-alpine
    16-alpine default110.7MB OK16-alpine
    16-alpine-vector274.9MB OK16-alpine
    16-alpine-analytics179.8MB OK16-alpine
    16-alpine-timeseries263.6MB OK16-alpine
    16-alpine-spatial177.5MB OK16-alpine
    16-alpine-distributed114.7MB OK16-alpine
    16-alpine-full429.8MB OK16-alpine
    Variant comparison 7 flavors · 3 versions

    Showing PostgreSQL 18. Switch a variant tag from another version to see its rows.

    VariantExtensionsImage 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.
    VariantExtensionsImage 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.
    VariantExtensionsImage 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:
    Build Lineage
    Build Digest sha256:35e93acf41466a22547c01c134d3d74b82c8b8ac92f2a0dee96e27a281d97679
    Base Image
    pg_stat_statements built-in
    pg_buffercache built-in
    pg_prewarm built-in
    pgcrypto built-in
    uuid-ossp built-in
    btree_gin built-in
    btree_gist built-in
    pg_trgm built-in
    file_fdw built-in
    postgres_fdw built-in
    adminpack built-in
    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
    10/10 dependencies monitored
    Dependency Current Latest Type
    paradedb 0.24.1 0.24.2 patch
    Up-to-date dependencies
    pgvector 0.8.5 pg_partman 5.4.3 hypopg 1.4.3 pg_qualstats 2.1.4 citus 14.1.0 timescaledb 2.28.2 pg_cron 1.6.7 pg_ivm 1.15 postgis 3.6.4

    Documentation

    README

    PostgreSQL Database Container

    Docker Hub GHCR Build

    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 statistics
    • pgcrypto - Cryptographic functions
    • uuid-ossp - UUID generation
    • btree_gin, btree_gist - Additional index types
    • pg_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-alpine or 17-base-alpine - PG17 base
    • 17-vector-alpine - PG17 with pgvector
    • 16-analytics-alpine - PG16 with analytics extensions
    • 17-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 statistics
    • pgcrypto - Cryptographic functions
    • uuid-ossp - UUID generation
    • btree_gin / btree_gist - Additional index types
    • pg_trgm - Fuzzy string matching

    Analytics and full flavors also include:

    • pg_buffercache - Shared buffer inspection
    • pg_prewarm - Buffer cache preloading
    • file_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 timeseries or full flavors locally (e.g. ./make build postgres --flavor timeseries) requires skopeo on 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 via skopeo list-tags. Install with sudo apt-get install -y skopeo (Debian/Ubuntu) or brew install skopeo (macOS). macOS also requires GNU coreutils (brew install coreutils) because the version-set resolution uses sort -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 manual ALTER EXTENSION command 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.1 for 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:

    1. 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
      
    2. Add to config.yaml:
      extensions:
        myext:
          version: "1.0.0"
          description: "My custom extension"
          repo: "org/myext"
          build_deps:
            - build-base
          shared_preload: false
      
    3. Build the extension:
      ./scripts/build-extensions.sh postgres myext
      
    4. 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