From 03c459ca10ddced13c97a94d7b9e451d728aced0 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 17 Feb 2026 14:11:29 -0500 Subject: [PATCH 1/2] feat: images tests and release on pg 17 and orioledb for multigres --- .github/workflows/docker-image-test.yml | 26 +++ .../workflows/dockerhub-release-matrix.yml | 46 +++- .github/workflows/manual-docker-release.yml | 54 +++-- Dockerfile-multigres-17 | 187 ++++++++++++++++ Dockerfile-multigres-orioledb-17 | 203 ++++++++++++++++++ nix/packages/docker-image-inputs.nix | 2 + 6 files changed, 493 insertions(+), 25 deletions(-) create mode 100644 Dockerfile-multigres-17 create mode 100644 Dockerfile-multigres-orioledb-17 diff --git a/.github/workflows/docker-image-test.yml b/.github/workflows/docker-image-test.yml index 1ebd48aee6..3367c20c5f 100644 --- a/.github/workflows/docker-image-test.yml +++ b/.github/workflows/docker-image-test.yml @@ -64,6 +64,8 @@ jobs: - Dockerfile-15 - Dockerfile-17 - Dockerfile-orioledb-17 + - Dockerfile-multigres-17 + - Dockerfile-multigres-orioledb-17 steps: - name: Checkout Repo uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -96,15 +98,39 @@ jobs: . - name: Run image size analysis + if: ${{ !startsWith(matrix.dockerfile, 'Dockerfile-multigres-') }} run: | echo "=== Image Size Analysis for ${{ matrix.dockerfile }} ===" nix run --accept-flake-config .#image-size-analyzer -- --image ${{ matrix.dockerfile }} --no-build - name: Run Docker image tests + if: ${{ !startsWith(matrix.dockerfile, 'Dockerfile-multigres-') }} run: | echo "=== Running tests for ${{ matrix.dockerfile }} ===" nix run --accept-flake-config .#docker-image-test -- --no-build ${{ matrix.dockerfile }} + - name: Verify multigres image (build-only smoke test) + if: ${{ startsWith(matrix.dockerfile, 'Dockerfile-multigres-') }} + run: | + echo "=== Smoke test for ${{ matrix.dockerfile }} ===" + VERSION="${{ matrix.dockerfile }}" + VERSION="${VERSION#Dockerfile-}" + # --entrypoint "" overrides the tail entrypoint so we can run commands directly + echo "Verifying PostgreSQL binaries are available..." + docker run --rm --entrypoint "" "pg-docker-test:${VERSION}" which initdb + docker run --rm --entrypoint "" "pg-docker-test:${VERSION}" which pg_ctl + docker run --rm --entrypoint "" "pg-docker-test:${VERSION}" which pg_isready + docker run --rm --entrypoint "" "pg-docker-test:${VERSION}" which psql + docker run --rm --entrypoint "" "pg-docker-test:${VERSION}" initdb --version + echo "Verifying configs are present..." + docker run --rm --entrypoint "" "pg-docker-test:${VERSION}" test -f /etc/postgresql/postgresql.conf + docker run --rm --entrypoint "" "pg-docker-test:${VERSION}" test -f /etc/postgresql-custom/supautils.conf + docker run --rm --entrypoint "" "pg-docker-test:${VERSION}" test -f /usr/lib/postgresql/bin/pgsodium_getkey.sh + docker run --rm --entrypoint "" "pg-docker-test:${VERSION}" test -d /docker-entrypoint-initdb.d/migrations + echo "Verifying docker-entrypoint.sh is NOT present..." + docker run --rm --entrypoint "" "pg-docker-test:${VERSION}" sh -c '! test -f /usr/local/bin/docker-entrypoint.sh' + echo "Multigres smoke test passed." + - name: Show container logs on failure if: failure() run: | diff --git a/.github/workflows/dockerhub-release-matrix.yml b/.github/workflows/dockerhub-release-matrix.yml index 539d29d16c..defae0f9ec 100644 --- a/.github/workflows/dockerhub-release-matrix.yml +++ b/.github/workflows/dockerhub-release-matrix.yml @@ -27,7 +27,7 @@ jobs: id: set-matrix run: | nix run nixpkgs#nushell -- -c 'let versions = (open ansible/vars.yml | get postgres_major) - let matrix = ($versions | each { |ver| + let base_matrix = ($versions | each { |ver| let version = ($ver | str trim) let dockerfile = $"Dockerfile-($version)" if ($dockerfile | path exists) { @@ -40,6 +40,23 @@ jobs: } } | compact) + # Also discover multigres variants for each base version + let multigres_matrix = ($versions | each { |ver| + let version = ($ver | str trim) + let mg_version = $"multigres-($version)" + let mg_dockerfile = $"Dockerfile-($mg_version)" + if ($mg_dockerfile | path exists) { + { + version: $mg_version, + dockerfile: $mg_dockerfile + } + } else { + null + } + } | compact) + + let matrix = ($base_matrix | append $multigres_matrix) + let matrix_config = { include: $matrix } @@ -95,23 +112,29 @@ jobs: if [[ "${{ matrix.arch }}" == "arm64" ]]; then pg_version=$(nix run nixpkgs#nushell -- -c ' let version = "${{ matrix.postgres.version }}" - let release_key = if ($version | str contains "orioledb") { + let is_multigres = ($version | str starts-with "multigres-") + let base_version = if $is_multigres { $version | str replace "multigres-" "" } else { $version } + let release_key = if ($base_version | str contains "orioledb") { $"postgresorioledb-17" } else { - $"postgres($version)" + $"postgres($base_version)" } - open ansible/vars.yml | get postgres_release | get $release_key | str trim + let base_tag = (open ansible/vars.yml | get postgres_release | get $release_key | str trim) + if $is_multigres { $"($base_tag)-multigres" } else { $base_tag } ') echo "pg_version=supabase/postgres:$pg_version" >> $GITHUB_OUTPUT else pg_version=$(nix run nixpkgs#nushell -- -c ' let version = "${{ matrix.postgres.version }}" - let release_key = if ($version | str contains "orioledb") { + let is_multigres = ($version | str starts-with "multigres-") + let base_version = if $is_multigres { $version | str replace "multigres-" "" } else { $version } + let release_key = if ($base_version | str contains "orioledb") { $"postgresorioledb-17" } else { - $"postgres($version)" + $"postgres($base_version)" } - open ansible/vars.yml | get postgres_release | get $release_key | str trim + let base_tag = (open ansible/vars.yml | get postgres_release | get $release_key | str trim) + if $is_multigres { $"($base_tag)-multigres" } else { $base_tag } ') echo "pg_version=supabase/postgres:$pg_version" >> $GITHUB_OUTPUT fi @@ -147,12 +170,15 @@ jobs: run: | nix run nixpkgs#nushell -- -c ' let version = "${{ matrix.version }}" - let release_key = if ($version | str contains "orioledb") { + let is_multigres = ($version | str starts-with "multigres-") + let base_version = if $is_multigres { $version | str replace "multigres-" "" } else { $version } + let release_key = if ($base_version | str contains "orioledb") { $"postgresorioledb-17" } else { - $"postgres($version)" + $"postgres($base_version)" } - let pg_version = (open ansible/vars.yml | get postgres_release | get $release_key | str trim) + let base_tag = (open ansible/vars.yml | get postgres_release | get $release_key | str trim) + let pg_version = if $is_multigres { $"($base_tag)-multigres" } else { $base_tag } $"pg_version=supabase/postgres:($pg_version)" | save --append $env.GITHUB_OUTPUT ' - name: Output version diff --git a/.github/workflows/manual-docker-release.yml b/.github/workflows/manual-docker-release.yml index cbc296ef4a..2fa63fa88b 100644 --- a/.github/workflows/manual-docker-release.yml +++ b/.github/workflows/manual-docker-release.yml @@ -24,7 +24,7 @@ jobs: id: set-matrix run: | nix run nixpkgs#nushell -- -c 'let versions = (open ansible/vars.yml | get postgres_major) - let matrix = ($versions | each { |ver| + let base_matrix = ($versions | each { |ver| let version = ($ver | str trim) let dockerfile = $"Dockerfile-($version)" if ($dockerfile | path exists) { @@ -37,6 +37,23 @@ jobs: } } | compact) + # Also discover multigres variants for each base version + let multigres_matrix = ($versions | each { |ver| + let version = ($ver | str trim) + let mg_version = $"multigres-($version)" + let mg_dockerfile = $"Dockerfile-($mg_version)" + if ($mg_dockerfile | path exists) { + { + version: $mg_version, + dockerfile: $mg_dockerfile + } + } else { + null + } + } | compact) + + let matrix = ($base_matrix | append $multigres_matrix) + let matrix_config = { include: $matrix } @@ -92,35 +109,39 @@ jobs: if [[ "${{ matrix.arch }}" == "arm64" ]]; then pg_version=$(nix run nixpkgs#nushell -- -c ' let version = "${{ matrix.postgres.version }}" - let release_key = if ($version | str contains "orioledb") { + let is_multigres = ($version | str starts-with "multigres-") + let base_version = if $is_multigres { $version | str replace "multigres-" "" } else { $version } + let release_key = if ($base_version | str contains "orioledb") { $"postgresorioledb-17" } else { - $"postgres($version)" + $"postgres($base_version)" } - let base_version = (open ansible/vars.yml | get postgres_release | get $release_key | str trim) + let base_tag = (open ansible/vars.yml | get postgres_release | get $release_key | str trim) let final_version = if "${{ inputs.postgresVersion }}" != "" { "${{ inputs.postgresVersion }}" } else { - $base_version + $base_tag } - $final_version | str trim + if $is_multigres { $"($final_version)-multigres" | str trim } else { $final_version | str trim } ') echo "pg_version=supabase/postgres:$pg_version" >> $GITHUB_OUTPUT else pg_version=$(nix run nixpkgs#nushell -- -c ' let version = "${{ matrix.postgres.version }}" - let release_key = if ($version | str contains "orioledb") { + let is_multigres = ($version | str starts-with "multigres-") + let base_version = if $is_multigres { $version | str replace "multigres-" "" } else { $version } + let release_key = if ($base_version | str contains "orioledb") { $"postgresorioledb-17" } else { - $"postgres($version)" + $"postgres($base_version)" } - let base_version = (open ansible/vars.yml | get postgres_release | get $release_key | str trim) + let base_tag = (open ansible/vars.yml | get postgres_release | get $release_key | str trim) let final_version = if "${{ inputs.postgresVersion }}" != "" { "${{ inputs.postgresVersion }}" } else { - $base_version + $base_tag } - $final_version | str trim + if $is_multigres { $"($final_version)-multigres" | str trim } else { $final_version | str trim } ') echo "pg_version=supabase/postgres:$pg_version" >> $GITHUB_OUTPUT fi @@ -156,12 +177,15 @@ jobs: run: | nix run nixpkgs#nushell -- -c ' let version = "${{ matrix.version }}" - let release_key = if ($version | str contains "orioledb") { + let is_multigres = ($version | str starts-with "multigres-") + let base_version = if $is_multigres { $version | str replace "multigres-" "" } else { $version } + let release_key = if ($base_version | str contains "orioledb") { $"postgresorioledb-17" } else { - $"postgres($version)" + $"postgres($base_version)" } - let pg_version = (open ansible/vars.yml | get postgres_release | get $release_key | str trim) + let base_tag = (open ansible/vars.yml | get postgres_release | get $release_key | str trim) + let pg_version = if $is_multigres { $"($base_tag)-multigres" } else { $base_tag } $"pg_version=supabase/postgres:($pg_version)" | save --append $env.GITHUB_OUTPUT ' - name: Output version @@ -258,5 +282,5 @@ jobs: matrix: ${{ fromJson(needs.combine_results.outputs.matrix) }} uses: ./.github/workflows/mirror.yml with: - version: ${{ inputs.postgresVersion != '' && inputs.postgresVersion || matrix.version }} + version: ${{ inputs.postgresVersion != '' && (contains(matrix.version, '-multigres') && format('{0}-multigres', inputs.postgresVersion) || inputs.postgresVersion) || matrix.version }} secrets: inherit diff --git a/Dockerfile-multigres-17 b/Dockerfile-multigres-17 new file mode 100644 index 0000000000..3930f6cfa3 --- /dev/null +++ b/Dockerfile-multigres-17 @@ -0,0 +1,187 @@ +# syntax=docker/dockerfile:1.6 +# Alpine-based slim PostgreSQL 17 image with Nix extensions — multigres variant +# Identical to Dockerfile-17 except: no docker-entrypoint.sh, no WAL-G helper +# scripts. pgctld manages PostgreSQL lifecycle externally. + +#################### +# Stage 1: Nix builder +#################### +FROM alpine:3.21 AS nix-builder + +# Install dependencies for nix installer (coreutils for GNU cp, sudo for installer) +RUN apk add --no-cache \ + bash \ + coreutils \ + curl \ + shadow \ + sudo \ + xz + +# Create users (Alpine syntax) +RUN addgroup -S postgres && \ + adduser -S -h /var/lib/postgresql -s /bin/bash -G postgres postgres && \ + addgroup -S wal-g && \ + adduser -S -s /bin/bash -G wal-g wal-g + +# Create nix config +RUN cat < /tmp/extra-nix.conf +extra-experimental-features = nix-command flakes +extra-substituters = https://nix-postgres-artifacts.s3.amazonaws.com +extra-trusted-public-keys = nix-postgres-artifacts:dGZlQOvKcNEjvT7QEAJbcV6b6uk7VF/hWMjhYleiaLI= +EOF +RUN curl -L https://releases.nixos.org/nix/nix-2.33.2/install | sh -s -- --daemon --no-channel-add --yes --nix-extra-conf-file /tmp/extra-nix.conf + +ENV PATH="${PATH}:/nix/var/nix/profiles/default/bin" + +WORKDIR /nixpg +COPY . . + +# Build PostgreSQL with extensions +RUN nix profile add path:.#psql_17_slim/bin + +RUN nix store gc + +# Build groonga and copy plugins +RUN nix profile add path:.#supabase-groonga && \ + mkdir -p /tmp/groonga-plugins && \ + cp -r /nix/var/nix/profiles/default/lib/groonga/plugins /tmp/groonga-plugins/ + +RUN nix store gc + +#################### +# Stage 2: Gosu builder +#################### +FROM alpine:3.21 AS gosu-builder + +ARG TARGETARCH +ARG GOSU_VERSION=1.16 + +RUN apk add --no-cache gnupg curl + +# Download and verify gosu +RUN curl -fsSL "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-${TARGETARCH}" -o /usr/local/bin/gosu && \ + curl -fsSL "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-${TARGETARCH}.asc" -o /usr/local/bin/gosu.asc && \ + GNUPGHOME="$(mktemp -d)" && \ + export GNUPGHOME && \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 && \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu && \ + rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc && \ + chmod +x /usr/local/bin/gosu + +#################### +# Stage 3: Final production image +#################### +FROM alpine:3.21 AS production + +# Install minimal runtime dependencies +RUN apk add --no-cache \ + bash \ + curl \ + shadow \ + su-exec \ + tzdata \ + musl-locales \ + musl-locales-lang \ + && rm -rf /var/cache/apk/* + +# Create postgres user/group +RUN addgroup -S postgres && \ + adduser -S -G postgres -h /var/lib/postgresql -s /bin/bash postgres && \ + addgroup -S wal-g && \ + adduser -S -G wal-g -s /bin/bash wal-g && \ + adduser postgres wal-g + +# Copy Nix store and profiles from builder (profile already created by nix profile install) +COPY --from=nix-builder /nix /nix + +# Copy groonga plugins +COPY --from=nix-builder /tmp/groonga-plugins/plugins /usr/lib/groonga/plugins + +# Copy gosu +COPY --from=gosu-builder /usr/local/bin/gosu /usr/local/bin/gosu + +# Setup PostgreSQL directories +RUN mkdir -p /usr/lib/postgresql/bin \ + /usr/lib/postgresql/share/postgresql \ + /usr/share/postgresql \ + /var/lib/postgresql/data \ + /var/run/postgresql \ + && chown -R postgres:postgres /usr/lib/postgresql \ + && chown -R postgres:postgres /var/lib/postgresql \ + && chown -R postgres:postgres /usr/share/postgresql \ + && chown -R postgres:postgres /var/run/postgresql + +# Create symbolic links for binaries +RUN for f in /nix/var/nix/profiles/default/bin/*; do \ + ln -sf "$f" /usr/lib/postgresql/bin/ 2>/dev/null || true; \ + ln -sf "$f" /usr/bin/ 2>/dev/null || true; \ + done + +# Create symbolic links for PostgreSQL shares +RUN ln -sf /nix/var/nix/profiles/default/share/postgresql/* /usr/lib/postgresql/share/postgresql/ 2>/dev/null || true && \ + ln -sf /nix/var/nix/profiles/default/share/postgresql/* /usr/share/postgresql/ 2>/dev/null || true && \ + ln -sf /usr/lib/postgresql/share/postgresql/timezonesets /usr/share/postgresql/timezonesets 2>/dev/null || true + +# Set permissions +RUN chown -R postgres:postgres /usr/lib/postgresql && \ + chown -R postgres:postgres /usr/share/postgresql + +# Setup configs +COPY --chown=postgres:postgres ansible/files/postgresql_config/postgresql.conf.j2 /etc/postgresql/postgresql.conf +COPY --chown=postgres:postgres ansible/files/postgresql_config/pg_hba.conf.j2 /etc/postgresql/pg_hba.conf +COPY --chown=postgres:postgres ansible/files/postgresql_config/pg_ident.conf.j2 /etc/postgresql/pg_ident.conf +COPY --chown=postgres:postgres ansible/files/postgresql_config/conf.d /etc/postgresql-custom/conf.d +COPY --chown=postgres:postgres ansible/files/postgresql_config/postgresql-stdout-log.conf /etc/postgresql/logging.conf +COPY --chown=postgres:postgres ansible/files/postgresql_config/supautils.conf.j2 /etc/postgresql-custom/supautils.conf +COPY --chown=postgres:postgres ansible/files/postgresql_extension_custom_scripts /etc/postgresql-custom/extension-custom-scripts +COPY --chown=postgres:postgres ansible/files/pgsodium_getkey_urandom.sh.j2 /usr/lib/postgresql/bin/pgsodium_getkey.sh +COPY --chown=postgres:postgres ansible/files/postgresql_config/custom_walg.conf /etc/postgresql-custom/wal-g.conf +COPY --chown=postgres:postgres ansible/files/postgresql_config/custom_read_replica.conf /etc/postgresql-custom/read-replica.conf + +# Configure PostgreSQL settings +RUN sed -i \ + -e "s|#unix_socket_directories = '/tmp'|unix_socket_directories = '/var/run/postgresql'|g" \ + -e "s|#session_preload_libraries = ''|session_preload_libraries = 'supautils'|g" \ + -e "s|#include = '/etc/postgresql-custom/supautils.conf'|include = '/etc/postgresql-custom/supautils.conf'|g" \ + -e "s|#include = '/etc/postgresql-custom/wal-g.conf'|include = '/etc/postgresql-custom/wal-g.conf'|g" /etc/postgresql/postgresql.conf && \ + echo "pgsodium.getkey_script= '/usr/lib/postgresql/bin/pgsodium_getkey.sh'" >> /etc/postgresql/postgresql.conf && \ + echo "vault.getkey_script= '/usr/lib/postgresql/bin/pgsodium_getkey.sh'" >> /etc/postgresql/postgresql.conf && \ + chown -R postgres:postgres /etc/postgresql-custom + +# Remove timescaledb and plv8 references (not in pg17) +RUN sed -i 's/ timescaledb,//g;' "/etc/postgresql/postgresql.conf" && \ + sed -i 's/db_user_namespace = off/#db_user_namespace = off/g;' "/etc/postgresql/postgresql.conf" && \ + sed -i 's/ timescaledb,//g; s/ plv8,//g' "/etc/postgresql-custom/supautils.conf" + +# Include schema migrations +COPY migrations/db /docker-entrypoint-initdb.d/ +COPY ansible/files/pgbouncer_config/pgbouncer_auth_schema.sql /docker-entrypoint-initdb.d/init-scripts/00-schema.sql +COPY ansible/files/stat_extension.sql /docker-entrypoint-initdb.d/migrations/00-extension.sql + +# Setup pgsodium key script +RUN mkdir -p /usr/share/postgresql/extension/ && \ + ln -s /usr/lib/postgresql/bin/pgsodium_getkey.sh /usr/share/postgresql/extension/pgsodium_getkey && \ + chmod +x /usr/lib/postgresql/bin/pgsodium_getkey.sh + +# Environment variables +ENV PATH="/nix/var/nix/profiles/default/bin:/usr/lib/postgresql/bin:${PATH}" +ENV PGDATA=/var/lib/postgresql/data +ENV POSTGRES_HOST=/var/run/postgresql +ENV POSTGRES_USER=supabase_admin +ENV POSTGRES_DB=postgres +ENV POSTGRES_INITDB_ARGS="--allow-group-access --locale-provider=icu --encoding=UTF-8 --icu-locale=en_US.UTF-8" +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US:en +ENV LC_ALL=en_US.UTF-8 +ENV GRN_PLUGINS_DIR=/usr/lib/groonga/plugins +# Point to minimal glibc locales included in slim Nix package for initdb locale support +ENV LOCALE_ARCHIVE=/nix/var/nix/profiles/default/lib/locale/locale-archive + +# pgctld manages PostgreSQL lifecycle — container stays running for external control +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD pg_isready -U postgres -h localhost || true +STOPSIGNAL SIGINT +EXPOSE 5432 + +ENTRYPOINT ["tail"] +CMD ["-f", "/dev/null"] diff --git a/Dockerfile-multigres-orioledb-17 b/Dockerfile-multigres-orioledb-17 new file mode 100644 index 0000000000..37a953edb8 --- /dev/null +++ b/Dockerfile-multigres-orioledb-17 @@ -0,0 +1,203 @@ +# syntax=docker/dockerfile:1.6 +# Alpine-based slim PostgreSQL 17 with OrioleDB image using Nix extensions — multigres variant +# Identical to Dockerfile-orioledb-17 except: no docker-entrypoint.sh, no WAL-G helper +# scripts. pgctld manages PostgreSQL lifecycle externally. + +#################### +# Stage 1: Nix builder +#################### +FROM alpine:3.21 AS nix-builder + +# Install dependencies for nix installer (coreutils for GNU cp, sudo for installer) +RUN apk add --no-cache \ + bash \ + coreutils \ + curl \ + shadow \ + sudo \ + xz + +# Create users (Alpine syntax) +RUN addgroup -S postgres && \ + adduser -S -h /var/lib/postgresql -s /bin/bash -G postgres postgres && \ + addgroup -S wal-g && \ + adduser -S -s /bin/bash -G wal-g wal-g + +# Create nix config +RUN cat < /tmp/extra-nix.conf +extra-experimental-features = nix-command flakes +extra-substituters = https://nix-postgres-artifacts.s3.amazonaws.com +extra-trusted-public-keys = nix-postgres-artifacts:dGZlQOvKcNEjvT7QEAJbcV6b6uk7VF/hWMjhYleiaLI= +EOF +RUN curl -L https://releases.nixos.org/nix/nix-2.33.2/install | sh -s -- --daemon --no-channel-add --yes --nix-extra-conf-file /tmp/extra-nix.conf + +ENV PATH="${PATH}:/nix/var/nix/profiles/default/bin" + +WORKDIR /nixpg +COPY . . + +# Build PostgreSQL with extensions +RUN nix profile add path:.#psql_orioledb-17_slim/bin + +RUN nix store gc + +# Build groonga and copy plugins +RUN nix profile add path:.#supabase-groonga && \ + mkdir -p /tmp/groonga-plugins && \ + cp -r /nix/var/nix/profiles/default/lib/groonga/plugins /tmp/groonga-plugins/ + +RUN nix store gc + +#################### +# Stage 2: Gosu builder +#################### +FROM alpine:3.21 AS gosu-builder + +ARG TARGETARCH +ARG GOSU_VERSION=1.16 + +RUN apk add --no-cache gnupg curl + +# Download and verify gosu +RUN curl -fsSL "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-${TARGETARCH}" -o /usr/local/bin/gosu && \ + curl -fsSL "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-${TARGETARCH}.asc" -o /usr/local/bin/gosu.asc && \ + GNUPGHOME="$(mktemp -d)" && \ + export GNUPGHOME && \ + gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 && \ + gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu && \ + rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc && \ + chmod +x /usr/local/bin/gosu + +#################### +# Stage 3: Final production image +#################### +FROM alpine:3.21 AS production + +# Install minimal runtime dependencies +RUN apk add --no-cache \ + bash \ + curl \ + shadow \ + su-exec \ + tzdata \ + musl-locales \ + musl-locales-lang \ + && rm -rf /var/cache/apk/* + +# Create postgres user/group +RUN addgroup -S postgres && \ + adduser -S -G postgres -h /var/lib/postgresql -s /bin/bash postgres && \ + addgroup -S wal-g && \ + adduser -S -G wal-g -s /bin/bash wal-g && \ + adduser postgres wal-g + +# Copy Nix store and profiles from builder (profile already created by nix profile install) +COPY --from=nix-builder /nix /nix + +# Copy groonga plugins +COPY --from=nix-builder /tmp/groonga-plugins/plugins /usr/lib/groonga/plugins + +# Copy gosu +COPY --from=gosu-builder /usr/local/bin/gosu /usr/local/bin/gosu + +# Setup PostgreSQL directories +RUN mkdir -p /usr/lib/postgresql/bin \ + /usr/lib/postgresql/share/postgresql \ + /usr/share/postgresql \ + /var/lib/postgresql/data \ + /var/run/postgresql \ + && chown -R postgres:postgres /usr/lib/postgresql \ + && chown -R postgres:postgres /var/lib/postgresql \ + && chown -R postgres:postgres /usr/share/postgresql \ + && chown -R postgres:postgres /var/run/postgresql + +# Create symbolic links for binaries +RUN for f in /nix/var/nix/profiles/default/bin/*; do \ + ln -sf "$f" /usr/lib/postgresql/bin/ 2>/dev/null || true; \ + ln -sf "$f" /usr/bin/ 2>/dev/null || true; \ + done + +# Create symbolic links for PostgreSQL shares +RUN ln -sf /nix/var/nix/profiles/default/share/postgresql/* /usr/lib/postgresql/share/postgresql/ 2>/dev/null || true && \ + ln -sf /nix/var/nix/profiles/default/share/postgresql/* /usr/share/postgresql/ 2>/dev/null || true && \ + ln -sf /usr/lib/postgresql/share/postgresql/timezonesets /usr/share/postgresql/timezonesets 2>/dev/null || true + +# Set permissions +RUN chown -R postgres:postgres /usr/lib/postgresql && \ + chown -R postgres:postgres /usr/share/postgresql + +# Setup configs +COPY --chown=postgres:postgres ansible/files/postgresql_config/postgresql.conf.j2 /etc/postgresql/postgresql.conf +COPY --chown=postgres:postgres ansible/files/postgresql_config/pg_hba.conf.j2 /etc/postgresql/pg_hba.conf +COPY --chown=postgres:postgres ansible/files/postgresql_config/pg_ident.conf.j2 /etc/postgresql/pg_ident.conf +COPY --chown=postgres:postgres ansible/files/postgresql_config/conf.d /etc/postgresql-custom/conf.d +COPY --chown=postgres:postgres ansible/files/postgresql_config/postgresql-stdout-log.conf /etc/postgresql/logging.conf +COPY --chown=postgres:postgres ansible/files/postgresql_config/supautils.conf.j2 /etc/postgresql-custom/supautils.conf +COPY --chown=postgres:postgres ansible/files/postgresql_extension_custom_scripts /etc/postgresql-custom/extension-custom-scripts +COPY --chown=postgres:postgres ansible/files/pgsodium_getkey_urandom.sh.j2 /usr/lib/postgresql/bin/pgsodium_getkey.sh +COPY --chown=postgres:postgres ansible/files/postgresql_config/custom_walg.conf /etc/postgresql-custom/wal-g.conf +COPY --chown=postgres:postgres ansible/files/postgresql_config/custom_read_replica.conf /etc/postgresql-custom/read-replica.conf + +# Configure PostgreSQL settings +RUN sed -i \ + -e "s|#unix_socket_directories = '/tmp'|unix_socket_directories = '/var/run/postgresql'|g" \ + -e "s|#session_preload_libraries = ''|session_preload_libraries = 'supautils'|g" \ + -e "s|#include = '/etc/postgresql-custom/supautils.conf'|include = '/etc/postgresql-custom/supautils.conf'|g" \ + -e "s|#include = '/etc/postgresql-custom/wal-g.conf'|include = '/etc/postgresql-custom/wal-g.conf'|g" /etc/postgresql/postgresql.conf && \ + echo "pgsodium.getkey_script= '/usr/lib/postgresql/bin/pgsodium_getkey.sh'" >> /etc/postgresql/postgresql.conf && \ + echo "vault.getkey_script= '/usr/lib/postgresql/bin/pgsodium_getkey.sh'" >> /etc/postgresql/postgresql.conf && \ + chown -R postgres:postgres /etc/postgresql-custom + +# Remove timescaledb, plv8, postgis, pgrouting references (not available in orioledb build) +RUN sed -i 's/ timescaledb,//g;' "/etc/postgresql/postgresql.conf" && \ + sed -i 's/db_user_namespace = off/#db_user_namespace = off/g;' "/etc/postgresql/postgresql.conf" && \ + sed -i 's/ timescaledb,//g; s/ plv8,//g; s/ postgis,//g; s/ pgrouting,//g' "/etc/postgresql-custom/supautils.conf" + +# OrioleDB configuration +RUN sed -i 's/\(shared_preload_libraries.*\)'\''\(.*\)$/\1, orioledb'\''\2/' "/etc/postgresql/postgresql.conf" && \ + echo "default_table_access_method = 'orioledb'" >> "/etc/postgresql/postgresql.conf" + +# OrioleDB rewind configuration +# Enables time-based rewind capability for up to 20 minutes (1200 seconds) +# Buffer size: 1280 buffers * 8KB = 10MB for transaction retention +RUN echo "orioledb.enable_rewind = true" >> "/etc/postgresql/postgresql.conf" && \ + echo "orioledb.rewind_max_time = 1200" >> "/etc/postgresql/postgresql.conf" && \ + echo "orioledb.rewind_max_transactions = 100000" >> "/etc/postgresql/postgresql.conf" && \ + echo "orioledb.rewind_buffers = 1280" >> "/etc/postgresql/postgresql.conf" + +# Include schema migrations +COPY migrations/db /docker-entrypoint-initdb.d/ +COPY ansible/files/pgbouncer_config/pgbouncer_auth_schema.sql /docker-entrypoint-initdb.d/init-scripts/00-schema.sql +COPY ansible/files/stat_extension.sql /docker-entrypoint-initdb.d/migrations/00-extension.sql + +# Enable orioledb extension first +RUN echo "CREATE EXTENSION orioledb;" > /docker-entrypoint-initdb.d/init-scripts/00-pre-init.sql && \ + chown postgres:postgres /docker-entrypoint-initdb.d/init-scripts/00-pre-init.sql + +# Setup pgsodium key script +RUN mkdir -p /usr/share/postgresql/extension/ && \ + ln -s /usr/lib/postgresql/bin/pgsodium_getkey.sh /usr/share/postgresql/extension/pgsodium_getkey && \ + chmod +x /usr/lib/postgresql/bin/pgsodium_getkey.sh + +# Environment variables +ENV PATH="/nix/var/nix/profiles/default/bin:/usr/lib/postgresql/bin:${PATH}" +ENV PGDATA=/var/lib/postgresql/data +ENV POSTGRES_HOST=/var/run/postgresql +ENV POSTGRES_USER=supabase_admin +ENV POSTGRES_DB=postgres +ENV POSTGRES_INITDB_ARGS="--allow-group-access --locale-provider=icu --encoding=UTF-8 --icu-locale=en_US.UTF-8" +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US:en +ENV LC_ALL=en_US.UTF-8 +ENV GRN_PLUGINS_DIR=/usr/lib/groonga/plugins +# Point to minimal glibc locales included in slim Nix package for initdb locale support +ENV LOCALE_ARCHIVE=/nix/var/nix/profiles/default/lib/locale/locale-archive + +# pgctld manages PostgreSQL lifecycle — container stays running for external control +HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ + CMD pg_isready -U postgres -h localhost || true +STOPSIGNAL SIGINT +EXPOSE 5432 + +ENTRYPOINT ["tail"] +CMD ["-f", "/dev/null"] diff --git a/nix/packages/docker-image-inputs.nix b/nix/packages/docker-image-inputs.nix index 76784a8c1b..983c7d30af 100644 --- a/nix/packages/docker-image-inputs.nix +++ b/nix/packages/docker-image-inputs.nix @@ -25,6 +25,8 @@ let (root + "/Dockerfile-15") (root + "/Dockerfile-17") (root + "/Dockerfile-orioledb-17") + (root + "/Dockerfile-multigres-17") + (root + "/Dockerfile-multigres-orioledb-17") # PostgreSQL configuration files (copied into images) (root + "/ansible/files/postgresql_config") From 5c6fd9f25385a10ff9ddb6df3a1a16b99a0b71e4 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Tue, 17 Feb 2026 14:20:01 -0500 Subject: [PATCH 2/2] feat: ssh and ps --- Dockerfile-multigres-17 | 2 ++ Dockerfile-multigres-orioledb-17 | 2 ++ 2 files changed, 4 insertions(+) diff --git a/Dockerfile-multigres-17 b/Dockerfile-multigres-17 index 3930f6cfa3..47e8a4de52 100644 --- a/Dockerfile-multigres-17 +++ b/Dockerfile-multigres-17 @@ -77,6 +77,8 @@ FROM alpine:3.21 AS production RUN apk add --no-cache \ bash \ curl \ + openssh \ + procps \ shadow \ su-exec \ tzdata \ diff --git a/Dockerfile-multigres-orioledb-17 b/Dockerfile-multigres-orioledb-17 index 37a953edb8..f84326c796 100644 --- a/Dockerfile-multigres-orioledb-17 +++ b/Dockerfile-multigres-orioledb-17 @@ -77,6 +77,8 @@ FROM alpine:3.21 AS production RUN apk add --no-cache \ bash \ curl \ + openssh \ + procps \ shadow \ su-exec \ tzdata \