>
kubernetes, Planet, PostgreSQL, Technical

Waiting for Postgres 18 – Docker Containers 34% Smaller

On February 25, 2025 Christoph Berg committed the patch:

Subject: [PATCH] Move JIT to new postgresql-18-jit package. (Closes: #927182)

Make LLVM architectures a inclusion list so it works in the Architecture field.

This closed Debian bug 927182 which had been opened in April 2019 by Laurence Parry. That bug had raised concerns over the significant size increase of adding LLVM as a requirement to support Postgres JIT functionality.

Postgres supports packaging LLVM as a separate optional package without needing to recompile database binaries. Postgres is compiled once, and it performs a runtime check whether LLVM libraries are present. It gracefully disables JIT functionality in the database if LLVM libraries are not installed.

From: Andres Freund
Subject: Re: llvm dependency and space concerns
Date: 2025-01-12 00:03:43
Lists: pgsql-hackers

Hi,

On 2025-01-11 13:22:39 -0800, Jeremy Schneider wrote:
> It's a cleaner solution if JIT works more like an extension, and we can
> run a single build and split JIT into a separate package.

It does work like that. Only llvmjit.so has the llvm dependency, the main
postgres binary doesn't link to llvm. If llvmjit.so isn't available, jit is
silently disabled.

Andres

In the official community Postgres RPM/yum repository LLVM was split out to a separate package – but it had never been prioritized in the official community Debian repositories to make these code updates until now.

Christoph noted on the mailing lists that the work is not yet finished – there is still a little more that needs to be done:

From: Christoph Berg
Cc: pgsql-pkg-debian@lists.postgresql.org
Subject: Re: pg18 patch: separate package for llvm/jit
Date: Fri, 28 Feb 2025 16:02:42 +0100

Re: Jeremy Schneider
> I would like to propose this change for Postgres 18.  

Hi,

I committed a change to the PG18 packaging that implements that split.
The new package is called postgresql-18-jit.

There is more work to do on each of the extension packages currently
depending on "postgresql-18-jit-llvm (= llvmversion)", that needs to be
converted to "Breaks: postgresql-18-jit-llvm (<< llvmversion)". (This
is also the reason the jit package is not called like that because the
version number there is not the PG version number.)

This will likely happen when extensions are moved to PG18 in
September.

I'm unsure if the split should be backported to PG 17 and earlier
since it will affect production systems in some way.

Christoph

CloudNativePG running on Kubernetes is a fully automated control plane that uses container images and keeps your Postgres databases running reliably, backed up, simple to patch/update and able to recover on their own if something goes wrong. The official container images provided by CloudNativePG directly use the official “PGDG” Postgres community Debian packages on top of the official docker-provided “slim” debian base image. This has the benefit that CNPG users are part of the “herd” using the same binaries which are used by Debian Postgres users globally; we’re not going to hit any corner cases from compilation flags or configure options that aren’t widely used. Another commonly used container is the Docker “official image” that you get when you type docker run postgres – these images also directly use the official Postgres community Debian packages by default.

Generally speaking, the size of docker containers can vary widely. Minimizing dependencies and reducing container size has many benefits: less stuff to patch and update over time, fewer things that can get CVEs, fewer unnecessary utilities that can be leveraged by attackers, faster build times, less disk space usage, reduced network consumption and faster startup times in environments where containers are often migrated and rescheduled across fleets (moving to new hardware where those containers had never previously run, and thus images need to be downloaded).

Let’s take a look at the sizes of common Postgres containers:

# docker images

REPOSITORY                              TAG                            SIZE
ghcr.io/cloudnative-pg/postgresql       17.4-standard-bookworm         641MB
ghcr.io/cloudnative-pg/postgresql       17.4-minimal-bookworm          413MB
postgres                                17.4-bookworm                  438MB
postgres                                17.4-alpine3.21                278MB

Docker provides an official Postgres image based on Alpine (a Linux distribution designed to be small, simple and secure). Postgres is built from source for the Alpine container. Alpine uses the musl C library which doesn’t have the same adoption level in the general public and test coverage in the postgres community build farm as the GNU C library. Nonetheless, we see above that Docker’s official Alpine postgres image is 36% smaller than Docker’s official Debian Bookworm image. (From 438MB down to 278MB.)

Interestingly, LLVM alone seems responsible for half of the Alpine image’s size!

# docker run -it postgres:17.4-alpine3.21 apk info llvm19-libs

WARNING: opening from cache https://dl-cdn.alpinelinux.org/alpine/v3.21/main: No such file or directory
WARNING: opening from cache https://dl-cdn.alpinelinux.org/alpine/v3.21/community: No such file or directory
llvm19-libs-19.1.4-r0 description:
LLVM 19 runtime library

llvm19-libs-19.1.4-r0 webpage:
https://llvm.org/

llvm19-libs-19.1.4-r0 installed size:
153 MiB

The Alpine build might be useful for use cases where every byte matters – but it’s lacking in one of Postgres greatest strengths: extensibility. Another significant advantage of leveraging the community Debian packages is that we get free access to all of the Postgres Extensions that are already packaged for Debian. Adding a supported extension to a container is a one-liner change to the Dockerfile!

CloudNativePG builds and provides their own images on the Github Container Registry. At present there are two flavors of CNPG images: “minimal” and “standard”.

The CNPG minimal image is 413MB – about the same size as the docker image without any extensions or extras.

The CNPG standard image 641MB and includes three default extensions (pgvector, pgaudit and pg-failover-slots) as well as all Debian-supported glibc locales. The locales are responsible for 99% of the size increase.

# docker run -it ghcr.io/cloudnative-pg/postgresql:17.4-standard-bookworm dpkg-query --show --showformat='${Package}\t${Installed-Size} KB\n' locales-all postgresql-17-pgvector postgresql-17-pgaudit postgresql-17-pg-failover-slots

locales-all     227366 KB
postgresql-17-pg-failover-slots 101 KB
postgresql-17-pgaudit   106 KB
postgresql-17-pgvector  767 KB

Most users should not need these Debian glibc locales, especially since these images include ICU (which interestingly is much smaller than the Debian locales-all package). Most of us should be starting with the minimal image and customizing it with only the extensions we need.

But what about LLVM? Christoph’s commit should make JIT an optional install instead of a required install; how will this impact the minimal image size?

It took me a little work to get the syntax right, but it turns out this wasn’t too hard to test by tweaking the CNPG Dockerfile to use the official postgres community pg18 debian package snapshots.

https://github.com/ardentperf/postgres-containers/commit/6388113b36824e3a88f47faabf0ddd386b96e41a

Author: Jeremy Schneider <schneider@ardentperf.com>
Date:   Sun Apr 6 21:58:45 2025 -0700

    test pg18 development snapshots https://wiki.postgresql.org/wiki/Apt/FAQ#Development_snapshots

diff --git a/Dockerfile b/Dockerfile
index d4f02b7..8a03349 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -8,7 +8,12 @@ ENV PATH=$PATH:/usr/lib/postgresql/$PG_MAJOR/bin

 RUN apt-get update && \
     apt-get install -y --no-install-recommends postgresql-common ca-certificates gnupg && \
-    /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && \
+    true
+
+COPY pg18snapshot.pref /etc/apt/preferences.d/
+COPY pg18snapshot.list /etc/apt/sources.list.d/
+
+RUN apt-get update && \
     apt-get install -y --no-install-recommends -o Dpkg::::="--force-confdef" -o Dpkg::::="--force-confold" postgresql-common && \
     sed -ri 's/#(create_main_cluster) .*$/\1 = false/' /etc/postgresql-common/createcluster.conf && \
     apt-get install -y --no-install-recommends \

diff --git a/pg18snapshot.list b/pg18snapshot.list

+deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] http://apt.postgresql.org/pub/repos/apt/ bookworm-pgdg-snapshot main 18

diff --git a/pg18snapshot.pref b/pg18snapshot.pref

+Package: *
+Pin: origin apt.postgresql.org
+Pin-Priority: 1001

First I built version 17.4 locally to confirm that I would get the same size container as what’s in the official registry. Then I used the modified Dockerfile to build version 18.

# git checkout main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
# docker build . --target minimal --tag minimal17 --build-arg PG_VERSION=17.4
[+] Building 0.1s (7/7) FINISHED                                                                                      docker:default
 => [internal] load build definition from Dockerfile                                                                            0.0s
 => => transferring dockerfile: 1.35kB                                                                                          0.0s
 => [internal] load metadata for docker.io/library/debian:bookworm-slim                                                         0.0s
 => [internal] load .dockerignore                                                                                               0.0s
 => => transferring context: 2B                                                                                                 0.0s
 => [minimal 1/3] FROM docker.io/library/debian:bookworm-slim                                                                   0.0s
 => CACHED [minimal 2/3] RUN apt-get update &&     apt-get install -y --no-install-recommends postgresql-common ca-certificate  0.0s
 => CACHED [minimal 3/3] RUN usermod -u 26 postgres                                                                             0.0s
 => exporting to image                                                                                                          0.0s
 => => exporting layers                                                                                                         0.0s
 => => writing image sha256:d2d9baf4d2384dbfe58cd14bd0adc5aaca2beff95a4a7e37712ddc26f14a3a7d                                    0.0s
 => => naming to docker.io/library/minimal17                                                                                    0.0s

# git checkout test18
Switched to branch 'test18'
Your branch is up to date with 'ardent/test18'.
# docker build . --target minimal --tag minimal18 --build-arg PG_VERSION=18
[+] Building 0.1s (11/11) FINISHED                                                                                    docker:default
 => [internal] load build definition from Dockerfile                                                                            0.0s
 => => transferring dockerfile: 1.42kB                                                                                          0.0s
 => [internal] load metadata for docker.io/library/debian:bookworm-slim                                                         0.0s
 => [internal] load .dockerignore                                                                                               0.0s
 => => transferring context: 2B                                                                                                 0.0s
 => [minimal 1/6] FROM docker.io/library/debian:bookworm-slim                                                                   0.0s
 => [internal] load build context                                                                                               0.0s
 => => transferring context: 298B                                                                                               0.0s
 => CACHED [minimal 2/6] RUN apt-get update &&     apt-get install -y --no-install-recommends postgresql-common ca-certificate  0.0s
 => CACHED [minimal 3/6] COPY pg18snapshot.pref /etc/apt/preferences.d/                                                         0.0s
 => CACHED [minimal 4/6] COPY pg18snapshot.list /etc/apt/sources.list.d/                                                        0.0s
 => CACHED [minimal 5/6] RUN apt-get update &&     apt-get install -y --no-install-recommends -o Dpkg::::="--force-confdef" -o  0.0s
 => CACHED [minimal 6/6] RUN usermod -u 26 postgres                                                                             0.0s
 => exporting to image                                                                                                          0.0s
 => => exporting layers                                                                                                         0.0s
 => => writing image sha256:c2471c9180750c1983177693ef21b609443c4b5aa89f9f5ffe9cb8e159ebe00d                                    0.0s
 => => naming to docker.io/library/minimal18                                                                                    0.0s

# docker images
REPOSITORY                              TAG                            SIZE
minimal18                               latest                         262MB
minimal17                               latest                         413MB
ghcr.io/cloudnative-pg/postgresql       17.4-standard-bookworm         641MB
ghcr.io/cloudnative-pg/postgresql       17.4-minimal-bookworm          413MB
postgres                                17.4-bookworm                  438MB
postgres                                17.4-alpine3.21                278MB

The minimal CNPG image for Postgres 18 is 262MB – that’s 34% smaller than the minimal CNPG image for Postgres 17 of 413MB.

This is great news – another reason to look forward to Postgres 18!

Unknown's avatar

About Jeremy

Building and running reliable data platforms that scale and perform. about.me/jeremy_schneider

Discussion

No comments yet.

Leave a New Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Disclaimer

This is my personal website. The views expressed here are mine alone and may not reflect the views of my employer.

contact: 312-725-9249 or schneider @ ardentperf.com


https://about.me/jeremy_schneider

oaktableocmaceracattack

(a)

Enter your email address to receive notifications of new posts by email.

Join 76 other subscribers