Skip to content

Add ParadeDB image - #21419

Open
isaacvando wants to merge 1 commit into
docker-library:masterfrom
paradedb:main
Open

Add ParadeDB image#21419
isaacvando wants to merge 1 commit into
docker-library:masterfrom
paradedb:main

Conversation

@isaacvando

Copy link
Copy Markdown

Adds ParadeDB as an official image. Thank you for reviewing!

Docs PR: docker-library/docs#2671

@github-actions

This comment has been minimized.

@isaacvando
isaacvando marked this pull request as ready for review May 8, 2026 22:34
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@isaacvando

Copy link
Copy Markdown
Author

Hi @tianon @yosifkit, could one of you take a look at this PR? Thanks for your help!

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@tianon

tianon commented Jun 5, 2026

Copy link
Copy Markdown
Member

Please, don't merge into this PR -- we'll review the PR when we can. If you wish to update it, please rebase on our branch, then force push (which should also remove all the errant merge commits). Generally, this change adds a brand new file, so there shouldn't be any conflicts and thus very little reason to update/rebase unless you have something about the PR itself you need to update (or there's some new test, etc we merged into our branch that you'd like the CI to test, but that's pretty rare too).

Beyond that, please, please be patient -- our new-image queue takes a lot more maintainer energy to review / process, so it tends to be a lower priority but you are in queue and we'll get to it eventually. ❤️

@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown
Diff for b8ce632:
diff --git a/_bashbrew-arches b/_bashbrew-arches
index 8b13789..e85a97f 100644
--- a/_bashbrew-arches
+++ b/_bashbrew-arches
@@ -1 +1,2 @@
-
+amd64
+arm64v8
diff --git a/_bashbrew-cat b/_bashbrew-cat
index bdfae4a..04e512d 100644
--- a/_bashbrew-cat
+++ b/_bashbrew-cat
@@ -1 +1,21 @@
-Maintainers: New Image! :D (@docker-library-bot)
+Maintainers: Isaac Van Doren <isaac@paradedb.com> (@isaacvando), Philippe Noël <phil@paradedb.com> (@philippemnoel), Ming Ying <ming@paradedb.com> (@rebasedming)
+Architectures: amd64, arm64v8
+GitRepo: https://github.com/paradedb/paradedb.git
+GitFetch: refs/heads/main
+GitCommit: d5b3c63b4d325e1aa4b75cee2c4770bf5c18d2ba
+
+Tags: 0.23.4-pg15, v0.23.4-pg15, 15-v0.23.4, pg15
+Directory: docker
+File: Dockerfile.official-15
+
+Tags: 0.23.4-pg16, v0.23.4-pg16, 16-v0.23.4, pg16
+Directory: docker
+File: Dockerfile.official-16
+
+Tags: 0.23.4-pg17, v0.23.4-pg17, 17-v0.23.4, pg17
+Directory: docker
+File: Dockerfile.official-17
+
+Tags: latest, 0.23.4, 0.23.4-pg18, v0.23.4, v0.23.4-pg18, 18-v0.23.4, pg18
+Directory: docker
+File: Dockerfile.official-18
diff --git a/_bashbrew-list b/_bashbrew-list
index e69de29..9faed7d 100644
--- a/_bashbrew-list
+++ b/_bashbrew-list
@@ -0,0 +1,19 @@
+paradedb:0.23.4
+paradedb:0.23.4-pg15
+paradedb:0.23.4-pg16
+paradedb:0.23.4-pg17
+paradedb:0.23.4-pg18
+paradedb:15-v0.23.4
+paradedb:16-v0.23.4
+paradedb:17-v0.23.4
+paradedb:18-v0.23.4
+paradedb:latest
+paradedb:pg15
+paradedb:pg16
+paradedb:pg17
+paradedb:pg18
+paradedb:v0.23.4
+paradedb:v0.23.4-pg15
+paradedb:v0.23.4-pg16
+paradedb:v0.23.4-pg17
+paradedb:v0.23.4-pg18
diff --git a/_bashbrew-list-build-order b/_bashbrew-list-build-order
index e69de29..12e5989 100644
--- a/_bashbrew-list-build-order
+++ b/_bashbrew-list-build-order
@@ -0,0 +1,4 @@
+paradedb:pg15
+paradedb:pg16
+paradedb:pg17
+paradedb:pg18
diff --git a/paradedb_pg15/Dockerfile.official-15 b/paradedb_pg15/Dockerfile.official-15
new file mode 100644
index 0000000..bfd844c
--- /dev/null
+++ b/paradedb_pg15/Dockerfile.official-15
@@ -0,0 +1,65 @@
+# Note: Debian Trixie = Debian 13
+FROM postgres:15-trixie AS paradedb
+
+LABEL maintainer="ParadeDB - https://paradedb.com" \
+    org.opencontainers.image.description="Simple, Elastic-quality search for Postgres" \
+    org.opencontainers.image.source="https://github.com/paradedb/paradedb"
+
+SHELL ["/bin/bash", "-o", "pipefail", "-c", "-e"]
+
+# Install pg_search from GitHub Releases
+RUN apt-get update && \
+    apt-get install -y --no-install-recommends ca-certificates curl && \
+    arch="$(dpkg --print-architecture)" && \
+    case "$arch" in \
+        amd64) checksum="a51be4f76a21e6387e5558f98695eca6a84ab8ae6e4b87bf2fdc9adeb8f3a36e" ;; \
+        arm64) checksum="5988d83174bbbb301da82df2045d774661cedd2f3ae0966615bbf49708a5938f" ;; \
+        *) echo "unsupported architecture: $arch" >&2; exit 1 ;; \
+    esac && \
+    curl -fsSL -o /tmp/pg_search.deb "https://github.com/paradedb/paradedb/releases/download/v0.23.4/postgresql-15-pg-search_0.23.4-1PARADEDB-trixie_${arch}.deb" && \
+    echo "${checksum}  /tmp/pg_search.deb" | sha256sum -c - && \
+    apt-get install -y --no-install-recommends /tmp/pg_search.deb && \
+    rm /tmp/pg_search.deb && \
+    rm -rf /var/lib/apt/lists/*
+
+# Install core extensions
+# Use the PGDG archive so these pinned extension versions remain available after newer
+# versions replace them in the live Debian and PGDG apt repositories.
+ENV POSTGIS_VERSION_MAJOR=3
+RUN apt-get update \
+    && apt-get install -y --no-install-recommends ca-certificates \
+    && echo "deb [ signed-by=/usr/local/share/keyrings/postgres.gpg.asc ] https://apt-archive.postgresql.org/pub/repos/apt trixie-pgdg-archive main" > /etc/apt/sources.list.d/pgdg-archive.list \
+    && apt-get update \
+    && apt-get install -y --no-install-recommends \
+    postgresql-15-pgvector=0.8.1-2.pgdg13+1 \
+    postgresql-15-cron=1.6.7-2.pgdg13+1 \
+    postgresql-15-pg-ivm=1.13-1.pgdg13+1 \
+    postgresql-15-postgis-$POSTGIS_VERSION_MAJOR \
+    postgresql-15-postgis-$POSTGIS_VERSION_MAJOR-scripts \
+    && rm /etc/apt/sources.list.d/pgdg-archive.list \
+    && rm -rf /var/lib/apt/lists/* && \
+    update-ca-certificates
+
+# The postgresql.conf.sample file is used as a template for the postgresql.conf file, which
+# does not exist until the first time the container is started. By adding our settings to the
+# postgresql.conf.sample file, we ensure that our settings are applied onto the postgresql.conf file.
+#
+# The `postgres` database is the default database that exists in every Postgres installation. The pg_cron
+# extension requires a database to store its metadata tables. By using `postgres`, we ensure that it has a
+# stable, always-available database for its operations, no matter what other databases are created or deleted.
+RUN sed -i "s/^#shared_preload_libraries = ''/shared_preload_libraries = 'pg_search,pg_cron,pg_stat_statements'/" /usr/share/postgresql/postgresql.conf.sample && \
+    grep "shared_preload_libraries = 'pg_search,pg_cron,pg_stat_statements'" /usr/share/postgresql/postgresql.conf.sample && \
+    echo "cron.database_name = 'postgres'" >> /usr/share/postgresql/postgresql.conf.sample && \
+    echo "pg_stat_statements.track = 'top'" >> /usr/share/postgresql/postgresql.conf.sample
+
+# Reset the working directory to the root directory
+WORKDIR /
+
+# Copy ParadeDB bootstrap script to install extensions and configure postgresql.conf
+COPY ./bootstrap.sh /docker-entrypoint-initdb.d/10_bootstrap_paradedb.sh
+
+# The upstream `postgres` Docker image comes with its own `entrypoint.sh` script which
+# starts as `root` and then switches to the `postgres` user after running chown and chmod
+# on the PostgreSQL data directory. To maintain compatibility with the upstream image and
+# ensure that the `postgres` user has the correct permissions on the data directory, we let
+# the upstream `entrypoint.sh` script run as the entrypoint and don't specify a custom user.
diff --git a/paradedb_pg15/bootstrap.sh b/paradedb_pg15/bootstrap.sh
new file mode 100755
index 0000000..8711dbd
--- /dev/null
+++ b/paradedb_pg15/bootstrap.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+# shellcheck disable=SC2154
+
+# Executed at container start to bootstrap ParadeDB extensions and Postgres settings.
+
+# Exit on subcommand errors
+set -Eeuo pipefail
+
+# Perform all actions as $POSTGRES_USER
+export PGUSER="$POSTGRES_USER"
+
+# The `pg_cron` extension can only be installed in the `postgres` database, as per
+# our configuration in our Dockerfile. Therefore, we install it separately here.
+psql -d postgres -c "CREATE EXTENSION IF NOT EXISTS pg_cron;"
+
+# Always create a `paradedb` database, regardless of what $POSTGRES_DB is set to
+if [ "$POSTGRES_DB" != "paradedb" ]; then
+  echo "Creating default 'paradedb' database"
+  psql -d postgres -c "CREATE DATABASE paradedb;"
+fi
+
+# Load ParadeDB and third-party extensions into template1, paradedb, and $POSTGRES_DB
+# Creating extensions in template1 ensures that they are available in all new databases.
+for DB in template1 paradedb "$POSTGRES_DB"; do
+  echo "Loading ParadeDB extensions into $DB"
+  psql -d "$DB" <<-'EOSQL'
+    CREATE EXTENSION IF NOT EXISTS pg_search;
+    CREATE EXTENSION IF NOT EXISTS pg_ivm;
+    CREATE EXTENSION IF NOT EXISTS vector;
+    CREATE EXTENSION IF NOT EXISTS postgis;
+    CREATE EXTENSION IF NOT EXISTS postgis_topology;
+    CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
+    CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder;
+    CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
+EOSQL
+done
+
+# Add the `paradedb` schema to template1, paradedb, and $POSTGRES_DB
+for DB in template1 paradedb "$POSTGRES_DB"; do
+  echo "Adding 'paradedb' search_path to $DB"
+  psql -d "$DB" -c "ALTER DATABASE \"$DB\" SET search_path TO public,paradedb;"
+done
+
+echo "ParadeDB bootstrap completed!"
diff --git a/paradedb_pg16/Dockerfile.official-16 b/paradedb_pg16/Dockerfile.official-16
new file mode 100644
index 0000000..6942307
--- /dev/null
+++ b/paradedb_pg16/Dockerfile.official-16
@@ -0,0 +1,65 @@
+# Note: Debian Trixie = Debian 13
+FROM postgres:16-trixie AS paradedb
+
+LABEL maintainer="ParadeDB - https://paradedb.com" \
+    org.opencontainers.image.description="Simple, Elastic-quality search for Postgres" \
+    org.opencontainers.image.source="https://github.com/paradedb/paradedb"
+
+SHELL ["/bin/bash", "-o", "pipefail", "-c", "-e"]
+
+# Install pg_search from GitHub Releases
+RUN apt-get update && \
+    apt-get install -y --no-install-recommends ca-certificates curl && \
+    arch="$(dpkg --print-architecture)" && \
+    case "$arch" in \
+        amd64) checksum="fef3abb8b571dbaf125c472bb89639e36c27da165647dd6608b7cb09ff264b1b" ;; \
+        arm64) checksum="b96bd6e5e3ac32c4128b5091e9088cfcffa6468303288466f60ab25270c7803a" ;; \
+        *) echo "unsupported architecture: $arch" >&2; exit 1 ;; \
+    esac && \
+    curl -fsSL -o /tmp/pg_search.deb "https://github.com/paradedb/paradedb/releases/download/v0.23.4/postgresql-16-pg-search_0.23.4-1PARADEDB-trixie_${arch}.deb" && \
+    echo "${checksum}  /tmp/pg_search.deb" | sha256sum -c - && \
+    apt-get install -y --no-install-recommends /tmp/pg_search.deb && \
+    rm /tmp/pg_search.deb && \
+    rm -rf /var/lib/apt/lists/*
+
+# Install core extensions
+# Use the PGDG archive so these pinned extension versions remain available after newer
+# versions replace them in the live Debian and PGDG apt repositories.
+ENV POSTGIS_VERSION_MAJOR=3
+RUN apt-get update \
+    && apt-get install -y --no-install-recommends ca-certificates \
+    && echo "deb [ signed-by=/usr/local/share/keyrings/postgres.gpg.asc ] https://apt-archive.postgresql.org/pub/repos/apt trixie-pgdg-archive main" > /etc/apt/sources.list.d/pgdg-archive.list \
+    && apt-get update \
+    && apt-get install -y --no-install-recommends \
+    postgresql-16-pgvector=0.8.1-2.pgdg13+1 \
+    postgresql-16-cron=1.6.7-2.pgdg13+1 \
+    postgresql-16-pg-ivm=1.13-1.pgdg13+1 \
+    postgresql-16-postgis-$POSTGIS_VERSION_MAJOR \
+    postgresql-16-postgis-$POSTGIS_VERSION_MAJOR-scripts \
+    && rm /etc/apt/sources.list.d/pgdg-archive.list \
+    && rm -rf /var/lib/apt/lists/* && \
+    update-ca-certificates
+
+# The postgresql.conf.sample file is used as a template for the postgresql.conf file, which
+# does not exist until the first time the container is started. By adding our settings to the
+# postgresql.conf.sample file, we ensure that our settings are applied onto the postgresql.conf file.
+#
+# The `postgres` database is the default database that exists in every Postgres installation. The pg_cron
+# extension requires a database to store its metadata tables. By using `postgres`, we ensure that it has a
+# stable, always-available database for its operations, no matter what other databases are created or deleted.
+RUN sed -i "s/^#shared_preload_libraries = ''/shared_preload_libraries = 'pg_search,pg_cron,pg_stat_statements'/" /usr/share/postgresql/postgresql.conf.sample && \
+    grep "shared_preload_libraries = 'pg_search,pg_cron,pg_stat_statements'" /usr/share/postgresql/postgresql.conf.sample && \
+    echo "cron.database_name = 'postgres'" >> /usr/share/postgresql/postgresql.conf.sample && \
+    echo "pg_stat_statements.track = 'top'" >> /usr/share/postgresql/postgresql.conf.sample
+
+# Reset the working directory to the root directory
+WORKDIR /
+
+# Copy ParadeDB bootstrap script to install extensions and configure postgresql.conf
+COPY ./bootstrap.sh /docker-entrypoint-initdb.d/10_bootstrap_paradedb.sh
+
+# The upstream `postgres` Docker image comes with its own `entrypoint.sh` script which
+# starts as `root` and then switches to the `postgres` user after running chown and chmod
+# on the PostgreSQL data directory. To maintain compatibility with the upstream image and
+# ensure that the `postgres` user has the correct permissions on the data directory, we let
+# the upstream `entrypoint.sh` script run as the entrypoint and don't specify a custom user.
diff --git a/paradedb_pg16/bootstrap.sh b/paradedb_pg16/bootstrap.sh
new file mode 100755
index 0000000..8711dbd
--- /dev/null
+++ b/paradedb_pg16/bootstrap.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+# shellcheck disable=SC2154
+
+# Executed at container start to bootstrap ParadeDB extensions and Postgres settings.
+
+# Exit on subcommand errors
+set -Eeuo pipefail
+
+# Perform all actions as $POSTGRES_USER
+export PGUSER="$POSTGRES_USER"
+
+# The `pg_cron` extension can only be installed in the `postgres` database, as per
+# our configuration in our Dockerfile. Therefore, we install it separately here.
+psql -d postgres -c "CREATE EXTENSION IF NOT EXISTS pg_cron;"
+
+# Always create a `paradedb` database, regardless of what $POSTGRES_DB is set to
+if [ "$POSTGRES_DB" != "paradedb" ]; then
+  echo "Creating default 'paradedb' database"
+  psql -d postgres -c "CREATE DATABASE paradedb;"
+fi
+
+# Load ParadeDB and third-party extensions into template1, paradedb, and $POSTGRES_DB
+# Creating extensions in template1 ensures that they are available in all new databases.
+for DB in template1 paradedb "$POSTGRES_DB"; do
+  echo "Loading ParadeDB extensions into $DB"
+  psql -d "$DB" <<-'EOSQL'
+    CREATE EXTENSION IF NOT EXISTS pg_search;
+    CREATE EXTENSION IF NOT EXISTS pg_ivm;
+    CREATE EXTENSION IF NOT EXISTS vector;
+    CREATE EXTENSION IF NOT EXISTS postgis;
+    CREATE EXTENSION IF NOT EXISTS postgis_topology;
+    CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
+    CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder;
+    CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
+EOSQL
+done
+
+# Add the `paradedb` schema to template1, paradedb, and $POSTGRES_DB
+for DB in template1 paradedb "$POSTGRES_DB"; do
+  echo "Adding 'paradedb' search_path to $DB"
+  psql -d "$DB" -c "ALTER DATABASE \"$DB\" SET search_path TO public,paradedb;"
+done
+
+echo "ParadeDB bootstrap completed!"
diff --git a/paradedb_pg17/Dockerfile.official-17 b/paradedb_pg17/Dockerfile.official-17
new file mode 100644
index 0000000..69b1296
--- /dev/null
+++ b/paradedb_pg17/Dockerfile.official-17
@@ -0,0 +1,65 @@
+# Note: Debian Trixie = Debian 13
+FROM postgres:17-trixie AS paradedb
+
+LABEL maintainer="ParadeDB - https://paradedb.com" \
+    org.opencontainers.image.description="Simple, Elastic-quality search for Postgres" \
+    org.opencontainers.image.source="https://github.com/paradedb/paradedb"
+
+SHELL ["/bin/bash", "-o", "pipefail", "-c", "-e"]
+
+# Install pg_search from GitHub Releases
+RUN apt-get update && \
+    apt-get install -y --no-install-recommends ca-certificates curl && \
+    arch="$(dpkg --print-architecture)" && \
+    case "$arch" in \
+        amd64) checksum="fd4ec8fec076910f60824176adddd57d192877a9ed271c1994202fdf4edb77cf" ;; \
+        arm64) checksum="0716d85321905a75ef5e088a6350161b32304901e14b31827ab0b6dd06306d2e" ;; \
+        *) echo "unsupported architecture: $arch" >&2; exit 1 ;; \
+    esac && \
+    curl -fsSL -o /tmp/pg_search.deb "https://github.com/paradedb/paradedb/releases/download/v0.23.4/postgresql-17-pg-search_0.23.4-1PARADEDB-trixie_${arch}.deb" && \
+    echo "${checksum}  /tmp/pg_search.deb" | sha256sum -c - && \
+    apt-get install -y --no-install-recommends /tmp/pg_search.deb && \
+    rm /tmp/pg_search.deb && \
+    rm -rf /var/lib/apt/lists/*
+
+# Install core extensions
+# Use the PGDG archive so these pinned extension versions remain available after newer
+# versions replace them in the live Debian and PGDG apt repositories.
+ENV POSTGIS_VERSION_MAJOR=3
+RUN apt-get update \
+    && apt-get install -y --no-install-recommends ca-certificates \
+    && echo "deb [ signed-by=/usr/local/share/keyrings/postgres.gpg.asc ] https://apt-archive.postgresql.org/pub/repos/apt trixie-pgdg-archive main" > /etc/apt/sources.list.d/pgdg-archive.list \
+    && apt-get update \
+    && apt-get install -y --no-install-recommends \
+    postgresql-17-pgvector=0.8.1-2.pgdg13+1 \
+    postgresql-17-cron=1.6.7-2.pgdg13+1 \
+    postgresql-17-pg-ivm=1.13-1.pgdg13+1 \
+    postgresql-17-postgis-$POSTGIS_VERSION_MAJOR \
+    postgresql-17-postgis-$POSTGIS_VERSION_MAJOR-scripts \
+    && rm /etc/apt/sources.list.d/pgdg-archive.list \
+    && rm -rf /var/lib/apt/lists/* && \
+    update-ca-certificates
+
+# The postgresql.conf.sample file is used as a template for the postgresql.conf file, which
+# does not exist until the first time the container is started. By adding our settings to the
+# postgresql.conf.sample file, we ensure that our settings are applied onto the postgresql.conf file.
+#
+# The `postgres` database is the default database that exists in every Postgres installation. The pg_cron
+# extension requires a database to store its metadata tables. By using `postgres`, we ensure that it has a
+# stable, always-available database for its operations, no matter what other databases are created or deleted.
+RUN sed -i "s/^#shared_preload_libraries = ''/shared_preload_libraries = 'pg_search,pg_cron,pg_stat_statements'/" /usr/share/postgresql/postgresql.conf.sample && \
+    grep "shared_preload_libraries = 'pg_search,pg_cron,pg_stat_statements'" /usr/share/postgresql/postgresql.conf.sample && \
+    echo "cron.database_name = 'postgres'" >> /usr/share/postgresql/postgresql.conf.sample && \
+    echo "pg_stat_statements.track = 'top'" >> /usr/share/postgresql/postgresql.conf.sample
+
+# Reset the working directory to the root directory
+WORKDIR /
+
+# Copy ParadeDB bootstrap script to install extensions and configure postgresql.conf
+COPY ./bootstrap.sh /docker-entrypoint-initdb.d/10_bootstrap_paradedb.sh
+
+# The upstream `postgres` Docker image comes with its own `entrypoint.sh` script which
+# starts as `root` and then switches to the `postgres` user after running chown and chmod
+# on the PostgreSQL data directory. To maintain compatibility with the upstream image and
+# ensure that the `postgres` user has the correct permissions on the data directory, we let
+# the upstream `entrypoint.sh` script run as the entrypoint and don't specify a custom user.
diff --git a/paradedb_pg17/bootstrap.sh b/paradedb_pg17/bootstrap.sh
new file mode 100755
index 0000000..8711dbd
--- /dev/null
+++ b/paradedb_pg17/bootstrap.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+# shellcheck disable=SC2154
+
+# Executed at container start to bootstrap ParadeDB extensions and Postgres settings.
+
+# Exit on subcommand errors
+set -Eeuo pipefail
+
+# Perform all actions as $POSTGRES_USER
+export PGUSER="$POSTGRES_USER"
+
+# The `pg_cron` extension can only be installed in the `postgres` database, as per
+# our configuration in our Dockerfile. Therefore, we install it separately here.
+psql -d postgres -c "CREATE EXTENSION IF NOT EXISTS pg_cron;"
+
+# Always create a `paradedb` database, regardless of what $POSTGRES_DB is set to
+if [ "$POSTGRES_DB" != "paradedb" ]; then
+  echo "Creating default 'paradedb' database"
+  psql -d postgres -c "CREATE DATABASE paradedb;"
+fi
+
+# Load ParadeDB and third-party extensions into template1, paradedb, and $POSTGRES_DB
+# Creating extensions in template1 ensures that they are available in all new databases.
+for DB in template1 paradedb "$POSTGRES_DB"; do
+  echo "Loading ParadeDB extensions into $DB"
+  psql -d "$DB" <<-'EOSQL'
+    CREATE EXTENSION IF NOT EXISTS pg_search;
+    CREATE EXTENSION IF NOT EXISTS pg_ivm;
+    CREATE EXTENSION IF NOT EXISTS vector;
+    CREATE EXTENSION IF NOT EXISTS postgis;
+    CREATE EXTENSION IF NOT EXISTS postgis_topology;
+    CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
+    CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder;
+    CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
+EOSQL
+done
+
+# Add the `paradedb` schema to template1, paradedb, and $POSTGRES_DB
+for DB in template1 paradedb "$POSTGRES_DB"; do
+  echo "Adding 'paradedb' search_path to $DB"
+  psql -d "$DB" -c "ALTER DATABASE \"$DB\" SET search_path TO public,paradedb;"
+done
+
+echo "ParadeDB bootstrap completed!"
diff --git a/paradedb_pg18/Dockerfile.official-18 b/paradedb_pg18/Dockerfile.official-18
new file mode 100644
index 0000000..863f3a7
--- /dev/null
+++ b/paradedb_pg18/Dockerfile.official-18
@@ -0,0 +1,65 @@
+# Note: Debian Trixie = Debian 13
+FROM postgres:18-trixie AS paradedb
+
+LABEL maintainer="ParadeDB - https://paradedb.com" \
+    org.opencontainers.image.description="Simple, Elastic-quality search for Postgres" \
+    org.opencontainers.image.source="https://github.com/paradedb/paradedb"
+
+SHELL ["/bin/bash", "-o", "pipefail", "-c", "-e"]
+
+# Install pg_search from GitHub Releases
+RUN apt-get update && \
+    apt-get install -y --no-install-recommends ca-certificates curl && \
+    arch="$(dpkg --print-architecture)" && \
+    case "$arch" in \
+        amd64) checksum="6b042d61d156ca5fdcb1c417e291d90bffe3026848890be30bf6e578146b4676" ;; \
+        arm64) checksum="5ad13a80b76c46590914e0c366bd8deaf807d5b352f5ad489876ec836d06d3d1" ;; \
+        *) echo "unsupported architecture: $arch" >&2; exit 1 ;; \
+    esac && \
+    curl -fsSL -o /tmp/pg_search.deb "https://github.com/paradedb/paradedb/releases/download/v0.23.4/postgresql-18-pg-search_0.23.4-1PARADEDB-trixie_${arch}.deb" && \
+    echo "${checksum}  /tmp/pg_search.deb" | sha256sum -c - && \
+    apt-get install -y --no-install-recommends /tmp/pg_search.deb && \
+    rm /tmp/pg_search.deb && \
+    rm -rf /var/lib/apt/lists/*
+
+# Install core extensions
+# Use the PGDG archive so these pinned extension versions remain available after newer
+# versions replace them in the live Debian and PGDG apt repositories.
+ENV POSTGIS_VERSION_MAJOR=3
+RUN apt-get update \
+    && apt-get install -y --no-install-recommends ca-certificates \
+    && echo "deb [ signed-by=/usr/local/share/keyrings/postgres.gpg.asc ] https://apt-archive.postgresql.org/pub/repos/apt trixie-pgdg-archive main" > /etc/apt/sources.list.d/pgdg-archive.list \
+    && apt-get update \
+    && apt-get install -y --no-install-recommends \
+    postgresql-18-pgvector=0.8.1-2.pgdg13+1 \
+    postgresql-18-cron=1.6.7-2.pgdg13+1 \
+    postgresql-18-pg-ivm=1.13-1.pgdg13+1 \
+    postgresql-18-postgis-$POSTGIS_VERSION_MAJOR \
+    postgresql-18-postgis-$POSTGIS_VERSION_MAJOR-scripts \
+    && rm /etc/apt/sources.list.d/pgdg-archive.list \
+    && rm -rf /var/lib/apt/lists/* && \
+    update-ca-certificates
+
+# The postgresql.conf.sample file is used as a template for the postgresql.conf file, which
+# does not exist until the first time the container is started. By adding our settings to the
+# postgresql.conf.sample file, we ensure that our settings are applied onto the postgresql.conf file.
+#
+# The `postgres` database is the default database that exists in every Postgres installation. The pg_cron
+# extension requires a database to store its metadata tables. By using `postgres`, we ensure that it has a
+# stable, always-available database for its operations, no matter what other databases are created or deleted.
+RUN sed -i "s/^#shared_preload_libraries = ''/shared_preload_libraries = 'pg_search,pg_cron,pg_stat_statements'/" /usr/share/postgresql/postgresql.conf.sample && \
+    grep "shared_preload_libraries = 'pg_search,pg_cron,pg_stat_statements'" /usr/share/postgresql/postgresql.conf.sample && \
+    echo "cron.database_name = 'postgres'" >> /usr/share/postgresql/postgresql.conf.sample && \
+    echo "pg_stat_statements.track = 'top'" >> /usr/share/postgresql/postgresql.conf.sample
+
+# Reset the working directory to the root directory
+WORKDIR /
+
+# Copy ParadeDB bootstrap script to install extensions and configure postgresql.conf
+COPY ./bootstrap.sh /docker-entrypoint-initdb.d/10_bootstrap_paradedb.sh
+
+# The upstream `postgres` Docker image comes with its own `entrypoint.sh` script which
+# starts as `root` and then switches to the `postgres` user after running chown and chmod
+# on the PostgreSQL data directory. To maintain compatibility with the upstream image and
+# ensure that the `postgres` user has the correct permissions on the data directory, we let
+# the upstream `entrypoint.sh` script run as the entrypoint and don't specify a custom user.
diff --git a/paradedb_pg18/bootstrap.sh b/paradedb_pg18/bootstrap.sh
new file mode 100755
index 0000000..8711dbd
--- /dev/null
+++ b/paradedb_pg18/bootstrap.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+# shellcheck disable=SC2154
+
+# Executed at container start to bootstrap ParadeDB extensions and Postgres settings.
+
+# Exit on subcommand errors
+set -Eeuo pipefail
+
+# Perform all actions as $POSTGRES_USER
+export PGUSER="$POSTGRES_USER"
+
+# The `pg_cron` extension can only be installed in the `postgres` database, as per
+# our configuration in our Dockerfile. Therefore, we install it separately here.
+psql -d postgres -c "CREATE EXTENSION IF NOT EXISTS pg_cron;"
+
+# Always create a `paradedb` database, regardless of what $POSTGRES_DB is set to
+if [ "$POSTGRES_DB" != "paradedb" ]; then
+  echo "Creating default 'paradedb' database"
+  psql -d postgres -c "CREATE DATABASE paradedb;"
+fi
+
+# Load ParadeDB and third-party extensions into template1, paradedb, and $POSTGRES_DB
+# Creating extensions in template1 ensures that they are available in all new databases.
+for DB in template1 paradedb "$POSTGRES_DB"; do
+  echo "Loading ParadeDB extensions into $DB"
+  psql -d "$DB" <<-'EOSQL'
+    CREATE EXTENSION IF NOT EXISTS pg_search;
+    CREATE EXTENSION IF NOT EXISTS pg_ivm;
+    CREATE EXTENSION IF NOT EXISTS vector;
+    CREATE EXTENSION IF NOT EXISTS postgis;
+    CREATE EXTENSION IF NOT EXISTS postgis_topology;
+    CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
+    CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder;
+    CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
+EOSQL
+done
+
+# Add the `paradedb` schema to template1, paradedb, and $POSTGRES_DB
+for DB in template1 paradedb "$POSTGRES_DB"; do
+  echo "Adding 'paradedb' search_path to $DB"
+  psql -d "$DB" -c "ALTER DATABASE \"$DB\" SET search_path TO public,paradedb;"
+done
+
+echo "ParadeDB bootstrap completed!"

@philippemnoel
philippemnoel deleted the main branch June 6, 2026 22:20
@philippemnoel

Copy link
Copy Markdown

Please, don't merge into this PR -- we'll review the PR when we can. If you wish to update it, please rebase on our branch, then force push (which should also remove all the errant merge commits). Generally, this change adds a brand new file, so there shouldn't be any conflicts and thus very little reason to update/rebase unless you have something about the PR itself you need to update (or there's some new test, etc we merged into our branch that you'd like the CI to test, but that's pretty rare too).

Beyond that, please, please be patient -- our new-image queue takes a lot more maintainer energy to review / process, so it tends to be a lower priority but you are in queue and we'll get to it eventually. ❤️

Hi! Thank you for the kind message, and really sorry about the noise. If you want, we can open a new PR that doesn't have all these commits. I went ahead and force-pushed as you suggested, and our main is now exactly one commit ahead of yours. We won't be touching it until you're ready to review it.

Thanks again for taking a look and we'll sit tight. Thank you 🙏

@philippemnoel

Copy link
Copy Markdown

Hi @tianon @yosifkit. Just wanted to follow up on this PR. We'd still appreciate your review, and have tried our best to implement all of the best practices your team recommends to save you review time.

@tianon

tianon commented Aug 13, 2026

Copy link
Copy Markdown
Member
+Tags: 0.23.4-pg15, v0.23.4-pg15, 15-v0.23.4, pg15
+Directory: docker
+File: Dockerfile.official-15
+
+Tags: 0.23.4-pg16, v0.23.4-pg16, 16-v0.23.4, pg16

Four PostgreSQL major versions at once is a lot for a new image submission -- for reviewability it would be easier to start with just the most recent (pg18) and add older versions once that's in good shape. It's less disruptive (for you and for us) to add them later than to revisit four Dockerfiles for every concern that comes up (however, I do think it's manageable at this scale). 😅


+LABEL maintainer="ParadeDB - https://paradedb.com" \

The bare maintainer key is a hard blocker -- label keys need to be namespaced per the OCI image spec (https://github.com/opencontainers/image-spec/blob/v1.0.1/annotations.md). The predefined OCI key for maintainer contact is org.opencontainers.image.authors.

That said, we don't actively recommend LABEL at all -- labels are inherited by any downstream image built FROM this one, which is almost never desired. See #3540 (especially #3540 (comment) for what the build system already sets on its own) for context. The org.opencontainers.image.description and org.opencontainers.image.source keys are fine by name -- maintainer either needs to become org.opencontainers.image.authors or LABEL gets dropped entirely.

(Minor aside: "Simple, Elastic-quality search for Postgres" reads like a tagline -- a more neutral factual description would be more appropriate there, though also not a blocker.)


+SHELL ["/bin/bash", "-o", "pipefail", "-c", "-e"]

SHELL is one of those instructions that's inherited by child images -- anyone who does FROM paradedb:pg18 will find their RUN blocks unexpectedly executing under bash with -e and pipefail enforced, without having set that themselves. That's a real footgun that's hard to diagnose when it breaks something.

Worth noting too: looking at the actual RUN blocks, there are no bash-specific features being used -- case, $(), and && are all plain POSIX sh. So there's no particular reason to be on bash here at all. The right approach is to drop SHELL entirely and put set -eux at the top of each RUN block (and then use ; at the end of each line instead of && so the failure behavior works consistently and correctly); the scope of the setting is then explicit and self-contained rather than silently inherited.


+FROM postgres:15-trixie AS paradedb

The AS paradedb is dead code -- this is a single-stage build with no COPY --from= anywhere. Can just be dropped.


+    echo "${checksum}  /tmp/pg_search.deb" | sha256sum -c - && \

Two minor hardening notes (neither a blocker): the *-prefixed binary mode is the conventional format for verifying downloaded binaries, and since this is Debian-based, GNU coreutils sha256sum supports --strict which exits non-zero on malformed input rather than silently skipping:

echo "${checksum} */tmp/pg_search.deb" | sha256sum --strict --check -

+    && apt-get install -y --no-install-recommends ca-certificates \

ca-certificates was already installed in the first RUN block -- redundant here. 👀


+    postgresql-15-postgis-$POSTGIS_VERSION_MAJOR \
+    postgresql-15-postgis-$POSTGIS_VERSION_MAJOR-scripts \

The comment above this block says these come from the pgdg-archive specifically so "pinned extension versions remain available" -- but then PostGIS itself has no version pin, unlike pgvector (0.8.1-2.pgdg13+1), pg-cron (1.6.7-2.pgdg13+1), and pg-ivm (1.13-1.pgdg13+1) which are all pinned. Looks like an oversight.


+    && rm /etc/apt/sources.list.d/pgdg-archive.list \

This seems strange -- why remove the pgdg-archive sources file from the image? If the source was trusted enough to install from, removing it afterwards just makes life harder for anyone extending the image -- they'd need to re-add it before they can install anything else from the archive. The right cleanup is discarding the index cache, not the configuration.

(Related: since these Dockerfiles target trixie, apt-get dist-clean is available and handles the cache cleanup more carefully than rm -rf /var/lib/apt/lists/* -- it preserves important cryptographic state. Worth switching.)


+    update-ca-certificates

apt-get install ca-certificates already runs update-ca-certificates via the package postinst -- this explicit call at the end of the RUN block is redundant.


+WORKDIR /

The postgres base image doesn't set a custom WORKDIR, so the default is already /. None of the RUN blocks change the working directory, and the subsequent COPY uses an absolute destination path anyway. This is a no-op.


+RUN apt-get update && \
+    apt-get install -y --no-install-recommends ca-certificates curl && \

None of the RUN blocks start with set -eux. The SHELL instruction was covering -e and pipefail, but as noted above, SHELL should be dropped entirely -- which means the recommended pattern throughout is:

RUN set -eux; \
    apt-get update; \
    apt-get install -y --no-install-recommends ca-certificates curl; \
    ...

+for DB in template1 paradedb "$POSTGRES_DB"; do
+  echo "Loading ParadeDB extensions into $DB"
+  psql -d "$DB" <<-'EOSQL'
+    CREATE EXTENSION IF NOT EXISTS pg_search;
+    CREATE EXTENSION IF NOT EXISTS pg_ivm;
+    CREATE EXTENSION IF NOT EXISTS vector;
+    CREATE EXTENSION IF NOT EXISTS postgis;
+    CREATE EXTENSION IF NOT EXISTS postgis_topology;
+    CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
+    CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder;
+    CREATE EXTENSION IF NOT EXISTS pg_stat_statements;
+EOSQL
+done

Loading all of this into template1 means every database created in this container -- by the user, by an application, by anything -- gets pg_search, pgvector, pg_ivm, PostGIS, PostGIS topology, fuzzystrmatch, TIGER geocoder, and pg_stat_statements pre-created whether they wanted them or not. That's a very heavy and opinionated default, especially for something whose core value is a search extension.

With that said, I'm uncertain whether this image is a good fit for the Docker Official Images. pg_search is a Postgres extension -- there's no "ParadeDB" process to run, just Postgres with an extension pre-installed and some initialization done at first startup. The image is essentially FROM postgres plus a few apt packages, some config surgery, and an init script.

What gives me the most pause is the PostGIS bundle specifically -- postgis, postgis_topology, postgis_tiger_geocoder are a full GIS stack that doesn't have an obvious connection to a search extension. Is all of this actually required by or for pg_search, or is it more "everything ParadeDB happens to ship together"? Understanding that would help me understand whether the image is offering something focused and necessary vs. something that would be better served by documentation and a reference Dockerfile users can adapt.

Not trying to close the door here -- just being honest about what I'm thinking as I look at this. 🙇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants