11# syntax=docker/dockerfile:1.6
2- ARG postgresql_major=18
3- ARG postgresql_release=${postgresql_major}.1
4-
5- # Bump default build arg to build a package from source
6- # Bump vars.yml to specify runtime package version
7- ARG sfcgal_release=1.3.10
8- ARG postgis_release=3.3.2
9- ARG pgrouting_release=3.4.1
10- ARG pgtap_release=1.2.0
11- ARG pg_cron_release=1.6.2
12- ARG pgaudit_release=1.7.0
13- ARG pgjwt_release=9742dab1b2f297ad3811120db7b21451bca2d3c9
14- ARG pgsql_http_release=1.5.0
15- ARG plpgsql_check_release=2.2.5
16- ARG pg_safeupdate_release=1.4
17- ARG timescaledb_release=2.9.1
18- ARG wal2json_release=2_5
19- ARG pljava_release=1.6.4
20- ARG plv8_release=3.1.5
21- ARG pg_plan_filter_release=5081a7b5cb890876e67d8e7486b6a64c38c9a492
22- ARG pg_net_release=0.7.1
23- ARG rum_release=1.3.13
24- ARG pg_hashids_release=cd0e1b31d52b394a0df64079406a14a4f7387cd6
25- ARG libsodium_release=1.0.18
26- ARG pgsodium_release=3.1.6
27- ARG pg_graphql_release=1.5.11
28- ARG pg_stat_monitor_release=1.1.1
29- ARG pg_jsonschema_release=0.1.4
30- ARG pg_repack_release=1.4.8
31- ARG vault_release=0.2.8
32- ARG groonga_release=12.0.8
33- ARG pgroonga_release=2.4.0
34- ARG wrappers_release=0.5.7
35- ARG hypopg_release=1.3.1
36- ARG pgvector_release=0.4.0
37- ARG pg_tle_release=1.3.2
38- ARG index_advisor_release=0.2.0
39- ARG supautils_release=2.2.0
40- ARG wal_g_release=3.0.5
41-
42- FROM ubuntu:noble as base
43-
44- # Create reusable apt mirror fallback function
45- RUN echo '#!/bin/bash\n\
46- apt_update_with_fallback() {\n\
47- local sources_file="/etc/apt/sources.list.d/ubuntu.sources"\n\
48- local max_attempts=2\n\
49- local attempt=1\n\
50- local mirrors="archive.ubuntu.com us.archive.ubuntu.com"\n\
51- \n\
52- for mirror in $mirrors; do\n\
53- echo "========================================="\n\
54- echo "Attempting apt-get update with mirror: ${mirror}"\n\
55- echo "Attempt ${attempt} of ${max_attempts}"\n\
56- echo "========================================="\n\
57- \n\
58- if [ -f "${sources_file}" ]; then\n\
59- sed -i "s|http://[^/]*/ubuntu/|http://${mirror}/ubuntu/|g" "${sources_file}"\n\
60- fi\n\
61- \n\
62- if timeout 300 apt-get update 2>&1; then\n\
63- echo "========================================="\n\
64- echo "✓ Successfully updated apt cache using mirror: ${mirror}"\n\
65- echo "========================================="\n\
66- return 0\n\
67- else\n\
68- local exit_code=$?\n\
69- echo "========================================="\n\
70- echo "✗ Failed to update using mirror: ${mirror}"\n\
71- echo "Exit code: ${exit_code}"\n\
72- echo "========================================="\n\
73- \n\
74- apt-get clean\n\
75- rm -rf /var/lib/apt/lists/*\n\
76- \n\
77- if [ ${attempt} -lt ${max_attempts} ]; then\n\
78- local sleep_time=$((attempt * 5))\n\
79- echo "Waiting ${sleep_time} seconds before trying next mirror..."\n\
80- sleep ${sleep_time}\n\
81- fi\n\
82- fi\n\
83- \n\
84- attempt=$((attempt + 1))\n\
85- done\n\
86- \n\
87- echo "========================================="\n\
88- echo "ERROR: All mirror tiers failed after ${max_attempts} attempts"\n\
89- echo "========================================="\n\
90- return 1\n\
91- }' > /usr/local/bin/apt-update-fallback.sh && chmod +x /usr/local/bin/apt-update-fallback.sh
92-
93- RUN bash -c 'source /usr/local/bin/apt-update-fallback.sh && apt_update_with_fallback' && apt install -y \
2+ # Alpine-based slim PostgreSQL 18 image with Nix extensions
3+
4+ ####################
5+ # Stage 1: Nix builder
6+ ####################
7+ FROM alpine:3.21 AS nix-builder
8+
9+ # Install dependencies for nix installer (coreutils for GNU cp, sudo for installer)
10+ RUN apk add --no-cache \
11+ bash \
12+ coreutils \
9413 curl \
95- gnupg \
96- lsb-release \
97- software-properties-common \
98- wget \
14+ shadow \
9915 sudo \
100- tree \
101- && apt clean
16+ xz
10217
18+ # Create users (Alpine syntax)
19+ RUN addgroup -S postgres && \
20+ adduser -S -h /var/lib/postgresql -s /bin/bash -G postgres postgres && \
21+ addgroup -S wal-g && \
22+ adduser -S -s /bin/bash -G wal-g wal-g
10323
104- RUN adduser --system --home /var/lib/postgresql --no-create-home --shell /bin/bash --group --gecos "PostgreSQL administrator" postgres
105- RUN adduser --system --no-create-home --shell /bin/bash --group wal-g
24+ # Create nix config
10625RUN cat <<EOF > /tmp/extra-nix.conf
10726extra-experimental-features = nix-command flakes
10827extra-substituters = https://nix-postgres-artifacts.s3.amazonaws.com
10928extra-trusted-public-keys = nix-postgres-artifacts:dGZlQOvKcNEjvT7QEAJbcV6b6uk7VF/hWMjhYleiaLI=
11029EOF
111- RUN curl -L https://releases.nixos.org/nix/nix-2.33.1 /install | sh -s -- --daemon --no-channel-add --yes --nix-extra-conf-file /tmp/extra-nix.conf
30+ 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
11231
11332ENV PATH="${PATH}:/nix/var/nix/profiles/default/bin"
11433
115- COPY . /nixpg
116-
11734WORKDIR /nixpg
35+ COPY . .
11836
119- RUN nix profile add path:.#psql_18/bin
37+ # Build PostgreSQL with extensions
38+ RUN nix profile add path:.#psql_18_slim/bin
12039
12140RUN nix store gc
12241
123- WORKDIR /
124-
125-
126- RUN mkdir -p /usr/lib/postgresql/bin \
127- /usr/lib/postgresql/share/postgresql \
128- /usr/share/postgresql \
129- /var/lib/postgresql \
130- && chown -R postgres:postgres /usr/lib/postgresql \
131- && chown -R postgres:postgres /var/lib/postgresql \
132- && chown -R postgres:postgres /usr/share/postgresql
133-
134- # Create symbolic links
135- RUN ln -s /nix/var/nix/profiles/default/bin/* /usr/lib/postgresql/bin/ \
136- && ln -s /nix/var/nix/profiles/default/bin/* /usr/bin/ \
137- && chown -R postgres:postgres /usr/bin
138-
139- # Create symbolic links for PostgreSQL shares
140- RUN ln -s /nix/var/nix/profiles/default/share/postgresql/* /usr/lib/postgresql/share/postgresql/
141- RUN ln -s /nix/var/nix/profiles/default/share/postgresql/* /usr/share/postgresql/
142- RUN chown -R postgres:postgres /usr/lib/postgresql/share/postgresql/
143- RUN chown -R postgres:postgres /usr/share/postgresql/
144-
145- RUN tree /nix > /tmp/tree.txt && cat /tmp/tree.txt && cat /tmp/tree.txt >&2
146-
147- RUN chown -R postgres:postgres /usr/lib/postgresql
148-
149- RUN ln -sf /usr/lib/postgresql/share/postgresql/timezonesets /usr/share/postgresql/timezonesets
42+ # Build groonga and copy plugins
43+ RUN nix profile add path:.#supabase-groonga && \
44+ mkdir -p /tmp/groonga-plugins && \
45+ cp -r /nix/var/nix/profiles/default/lib/groonga/plugins /tmp/groonga-plugins/
15046
47+ RUN nix store gc
15148
152- RUN bash -c 'source /usr/local/bin/apt-update-fallback.sh && apt_update_with_fallback' && \
153- apt-get install -y --no-install-recommends tzdata
49+ ####################
50+ # Stage 2: Gosu builder
51+ ####################
52+ FROM alpine:3.21 AS gosu-builder
15453
155- RUN ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime && \
156- dpkg-reconfigure --frontend noninteractive tzdata
54+ ARG TARGETARCH
55+ ARG GOSU_VERSION=1.16
15756
158- RUN bash -c 'source /usr/local/bin/apt-update-fallback.sh && apt_update_with_fallback' && \
159- apt-get install -y --no-install-recommends \
160- build-essential \
161- checkinstall \
162- cmake
57+ RUN apk add --no-cache gnupg curl
16358
164- ENV PGDATA=/var/lib/postgresql/data
59+ # Download and verify gosu
60+ RUN curl -fsSL "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-${TARGETARCH}" -o /usr/local/bin/gosu && \
61+ curl -fsSL "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-${TARGETARCH}.asc" -o /usr/local/bin/gosu.asc && \
62+ GNUPGHOME="$(mktemp -d)" && \
63+ export GNUPGHOME && \
64+ gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 && \
65+ gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu && \
66+ rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc && \
67+ chmod +x /usr/local/bin/gosu
16568
166- WORKDIR /
16769####################
168- # setup-groonga
70+ # Stage 3: Final production image
16971####################
170- FROM base as groonga
171-
172- WORKDIR /nixpg
72+ FROM alpine:3.21 AS production
17373
174- RUN nix profile add path:.#supabase-groonga && \
175- mkdir -p /tmp/groonga-plugins && \
176- cp -r /nix/var/nix/profiles/default/lib/groonga/plugins /tmp/groonga-plugins/
74+ # Install minimal runtime dependencies
75+ RUN apk add --no-cache \
76+ bash \
77+ curl \
78+ shadow \
79+ su-exec \
80+ tzdata \
81+ musl-locales \
82+ musl-locales-lang \
83+ && rm -rf /var/cache/apk/*
84+
85+ # Create postgres user/group
86+ RUN addgroup -S postgres && \
87+ adduser -S -G postgres -h /var/lib/postgresql -s /bin/bash postgres && \
88+ addgroup -S wal-g && \
89+ adduser -S -G wal-g -s /bin/bash wal-g && \
90+ adduser postgres wal-g
91+
92+ # Copy Nix store and profiles from builder (profile already created by nix profile install)
93+ COPY --from=nix-builder /nix /nix
94+
95+ # Copy groonga plugins
96+ COPY --from=nix-builder /tmp/groonga-plugins/plugins /usr/lib/groonga/plugins
97+
98+ # Copy gosu
99+ COPY --from=gosu-builder /usr/local/bin/gosu /usr/local/bin/gosu
100+
101+ # Setup PostgreSQL directories
102+ RUN mkdir -p /usr/lib/postgresql/bin \
103+ /usr/lib/postgresql/share/postgresql \
104+ /usr/share/postgresql \
105+ /var/lib/postgresql/data \
106+ /var/run/postgresql \
107+ && chown -R postgres:postgres /usr/lib/postgresql \
108+ && chown -R postgres:postgres /var/lib/postgresql \
109+ && chown -R postgres:postgres /usr/share/postgresql \
110+ && chown -R postgres:postgres /var/run/postgresql
177111
178- RUN nix store gc
112+ # Create symbolic links for binaries
113+ RUN for f in /nix/var/nix/profiles/default/bin/*; do \
114+ ln -sf "$f" /usr/lib/postgresql/bin/ 2>/dev/null || true; \
115+ ln -sf "$f" /usr/bin/ 2>/dev/null || true; \
116+ done
179117
180- WORKDIR /
181- # ####################
182- # # Download gosu for easy step-down from root
183- # ####################
184- FROM base as gosu
185- ARG TARGETARCH
186- # Install dependencies
187- RUN bash -c 'source /usr/local/bin/apt-update-fallback.sh && apt_update_with_fallback' && apt-get install -y --no-install-recommends \
188- gnupg \
189- ca-certificates \
190- && rm -rf /var/lib/apt/lists/*
191- # Download binary
192- ARG GOSU_VERSION=1.16
193- ARG GOSU_GPG_KEY=B42F6819007F00F88E364FD4036A9C25BF357DD4
194- ADD https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$TARGETARCH \
195- /usr/local/bin/gosu
196- ADD https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$TARGETARCH.asc \
197- /usr/local/bin/gosu.asc
198- # Verify checksum
199- RUN gpg --batch --keyserver hkps://keys.openpgp.org --recv-keys $GOSU_GPG_KEY && \
200- gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu && \
201- gpgconf --kill all && \
202- chmod +x /usr/local/bin/gosu
118+ # Create symbolic links for PostgreSQL shares
119+ RUN ln -sf /nix/var/nix/profiles/default/share/postgresql/* /usr/lib/postgresql/share/postgresql/ 2>/dev/null || true && \
120+ ln -sf /nix/var/nix/profiles/default/share/postgresql/* /usr/share/postgresql/ 2>/dev/null || true && \
121+ ln -sf /usr/lib/postgresql/share/postgresql/timezonesets /usr/share/postgresql/timezonesets 2>/dev/null || true
203122
204- # ####################
205- # # Build final image
206- # ####################
207- FROM gosu as production
208- RUN id postgres || (echo "postgres user does not exist" && exit 1)
209- # # Setup extensions
210- COPY --from=groonga /tmp/groonga-plugins/plugins /usr/lib/groonga/plugins
123+ # Set permissions
124+ RUN chown -R postgres:postgres /usr/lib/postgresql && \
125+ chown -R postgres:postgres /usr/share/postgresql
211126
212- # # Initialise configs
127+ # Setup configs
213128COPY --chown=postgres:postgres ansible/files/postgresql_config/postgresql.conf.j2 /etc/postgresql/postgresql.conf
214129COPY --chown=postgres:postgres ansible/files/postgresql_config/pg_hba.conf.j2 /etc/postgresql/pg_hba.conf
215130COPY --chown=postgres:postgres ansible/files/postgresql_config/pg_ident.conf.j2 /etc/postgresql/pg_ident.conf
@@ -223,61 +138,53 @@ COPY --chown=postgres:postgres ansible/files/postgresql_config/custom_read_repli
223138COPY --chown=postgres:postgres ansible/files/walg_helper_scripts/wal_fetch.sh /home/postgres/wal_fetch.sh
224139COPY ansible/files/walg_helper_scripts/wal_change_ownership.sh /root/wal_change_ownership.sh
225140
141+ # Configure PostgreSQL settings
226142RUN sed -i \
227143 -e "s|#unix_socket_directories = '/tmp'|unix_socket_directories = '/var/run/postgresql'|g" \
228144 -e "s|#session_preload_libraries = ''|session_preload_libraries = 'supautils'|g" \
229145 -e "s|#include = '/etc/postgresql-custom/supautils.conf'|include = '/etc/postgresql-custom/supautils.conf'|g" \
230146 -e "s|#include = '/etc/postgresql-custom/wal-g.conf'|include = '/etc/postgresql-custom/wal-g.conf'|g" /etc/postgresql/postgresql.conf && \
231147 echo "pgsodium.getkey_script= '/usr/lib/postgresql/bin/pgsodium_getkey.sh'" >> /etc/postgresql/postgresql.conf && \
232148 echo "vault.getkey_script= '/usr/lib/postgresql/bin/pgsodium_getkey.sh'" >> /etc/postgresql/postgresql.conf && \
233- usermod -aG postgres wal-g && \
234149 chown -R postgres:postgres /etc/postgresql-custom
235150
236- # Remove items from postgresql.conf
237- RUN sed -i 's/ timescaledb,//g;' "/etc/postgresql/postgresql.conf"
238- #as of pg 16.4 + this db_user_namespace totally deprecated and will break the server if setting is present
239- RUN sed -i 's/db_user_namespace = off/#db_user_namespace = off/g;' "/etc/postgresql/postgresql.conf"
240- RUN sed -i 's/ timescaledb,//g; s/ plv8,//g' "/etc/postgresql-custom/supautils.conf"
241-
151+ # Remove timescaledb and plv8 references (not in pg18)
152+ RUN sed -i 's/ timescaledb,//g;' "/etc/postgresql/postgresql.conf" && \
153+ sed -i 's/db_user_namespace = off/#db_user_namespace = off/g;' "/etc/postgresql/postgresql.conf" && \
154+ sed -i 's/ timescaledb,//g; s/ plv8,//g' "/etc/postgresql-custom/supautils.conf"
242155
243-
244- # # Include schema migrations
156+ # Include schema migrations
245157COPY migrations/db /docker-entrypoint-initdb.d/
246158COPY ansible/files/pgbouncer_config/pgbouncer_auth_schema.sql /docker-entrypoint-initdb.d/init-scripts/00-schema.sql
247159COPY ansible/files/stat_extension.sql /docker-entrypoint-initdb.d/migrations/00-extension.sql
248160
249- # # Add upstream entrypoint script pinned for now to last tested version
250- COPY --from=gosu /usr/local/bin/gosu /usr/local/bin/gosu
161+ # Add entrypoint script
251162ADD --chmod=0755 \
252163 https://github.com/docker-library/postgres/raw/889f9447cd2dfe21cccfbe9bb7945e3b037e02d8/17/bullseye/docker-entrypoint.sh \
253164 /usr/local/bin/docker-entrypoint.sh
254165
255- RUN mkdir -p /var/run/postgresql && chown postgres:postgres /var/run/postgresql
256-
257- ENTRYPOINT ["docker-entrypoint.sh"]
258-
259- HEALTHCHECK --interval=2s --timeout=2s --retries=10 CMD pg_isready -U postgres -h localhost
260- STOPSIGNAL SIGINT
261- EXPOSE 5432
166+ # Setup pgsodium key script
167+ RUN mkdir -p /usr/share/postgresql/extension/ && \
168+ ln -s /usr/lib/postgresql/bin/pgsodium_getkey.sh /usr/share/postgresql/extension/pgsodium_getkey && \
169+ chmod +x /usr/lib/postgresql/bin/pgsodium_getkey.sh
262170
171+ # Environment variables
172+ ENV PATH="/nix/var/nix/profiles/default/bin:/usr/lib/postgresql/bin:${PATH}"
173+ ENV PGDATA=/var/lib/postgresql/data
263174ENV POSTGRES_HOST=/var/run/postgresql
264175ENV POSTGRES_USER=supabase_admin
265176ENV POSTGRES_DB=postgres
266177ENV POSTGRES_INITDB_ARGS="--allow-group-access --locale-provider=icu --encoding=UTF-8 --icu-locale=en_US.UTF-8"
267- RUN bash -c 'source /usr/local/bin/apt-update-fallback.sh && apt_update_with_fallback' && apt-get install -y --no-install-recommends \
268- locales \
269- && rm -rf /var/lib/apt/lists/* && \
270- localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 \
271- && localedef -i C -c -f UTF-8 -A /usr/share/locale/locale.alias C.UTF-8
272- RUN echo "C.UTF-8 UTF-8" > /etc/locale.gen && echo "en_US.UTF-8 UTF-8" >> /etc/locale.gen && locale-gen
273- ENV LANG en_US.UTF-8
274- ENV LANGUAGE en_US:en
275- ENV LC_ALL en_US.UTF-8
276- ENV LOCALE_ARCHIVE /usr/lib/locale/locale-archive
277- RUN mkdir -p /usr/share/postgresql/extension/ && \
278- ln -s /usr/lib/postgresql/bin/pgsodium_getkey.sh /usr/share/postgresql/extension/pgsodium_getkey && \
279- chmod +x /usr/lib/postgresql/bin/pgsodium_getkey.sh
280-
178+ ENV LANG=en_US.UTF-8
179+ ENV LANGUAGE=en_US:en
180+ ENV LC_ALL=en_US.UTF-8
281181ENV GRN_PLUGINS_DIR=/usr/lib/groonga/plugins
182+ # Point to minimal glibc locales included in slim Nix package for initdb locale support
183+ ENV LOCALE_ARCHIVE=/nix/var/nix/profiles/default/lib/locale/locale-archive
184+
185+ ENTRYPOINT ["docker-entrypoint.sh"]
186+ HEALTHCHECK --interval=2s --timeout=2s --retries=10 CMD pg_isready -U postgres -h localhost
187+ STOPSIGNAL SIGINT
188+ EXPOSE 5432
282189
283190CMD ["postgres", "-D", "/etc/postgresql"]
0 commit comments