1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# pg-source: where the pinned PostgreSQL 18 comes from (0006 M0-PG-SOURCE)
Date: 2026-10-11 (measured on hat-8, Fedora 44, glibc 2.43, x86_64, as uid 1000).
Consumers: the gateway kcp store (M2-KCP-STORE, kine -> postgres over a unix socket) and every tenant
cluster store (M2-POSTGRES). Dev and bidder kcp stores are kine-sqlite and need none of this.
## 1. Decision order (user, 2026-10-11, final)
1. **Official binaries first.** The PostgreSQL Global Development Group publishes no generic Linux
binary tarball; its official binaries are the PGDG packages (apt.postgresql.org debs,
yum.postgresql.org rpms). Use them if they can be extracted without root and run relocatably as
a non-root user.
2. **Build from the official source tarball** (ftp.postgresql.org/pub/source) only if (1) cannot be
made to work, with a build-host script `scripts/build-postgres.sh`. This is the fallback.
3. **Third-party repackagers are out**: zonky embedded-postgres-binaries and
theseus-rs/postgresql-binaries are not used. Section 6 keeps the facts measured before that
ruling, for the record only.
## 2. Choice
**PGDG `jammy-pgdg` debs for PostgreSQL 18.6 (`18.6-1.pgdg22.04+2`), amd64 and arm64, plus their
Ubuntu 22.04 (jammy) shared-library dependency closure, extracted without root into one
relocatable tree.** PGDG works on hat-8 and on every distro image tried, so the fallback was not
needed. A source build was prototyped and measured before the user's final order (section 5). Its
script is not committed.
Why jammy and not bookworm, noble or the EL rpms: PGDG binaries link the host distro's ICU,
OpenLDAP and OpenSSL by soname. The oldest PGDG target at or below the plan's floor (Ubuntu 22.04)
is jammy: max GLIBC_2.34 in the PG binaries and GLIBC_2.35 across the bundled libraries. EL8 links
OpenSSL 1.1, and EL9 links ICU 67. Both have the same soname problem as jammy, and neither lowers
the floor.
### What "relocatable" means for the PGDG tree (measured)
- **Layout must be kept.** PGDG compiles in `bindir=/usr/lib/postgresql/18/bin`,
`sharedir=/usr/share/postgresql/18` and `pkglibdir=/usr/lib/postgresql/18/lib`. At startup
PostgreSQL resolves share and pkglib relative to the running binary (`make_relative_path`) only
while the tail `lib/postgresql/18/bin` of the binary's path matches. So the tree is kept as
`<any-root>/usr/lib/postgresql/18/{bin,lib}` plus `<any-root>/usr/share/postgresql/18`, where
`<any-root>` can be any path. That was proven from `.../arbitrary2/x y/z/...` (a path containing a
space) on hat-8, and from `/opt/any/where/...` read-only in containers. Flattening to
`bin/ lib/ share/` breaks it.
- **No RUNPATH in PGDG binaries.** `libpq.so.5` (from the PGDG libpq5 deb) and the jammy libraries
sit in `<any-root>/usr/lib/sys/`, and are found only through
`LD_LIBRARY_PATH=<any-root>/usr/lib/sys`. That variable must be set on the postgres, initdb and
pg_dump exec ONLY, never in the core's own environment. Measured: hat-8's `grep`, spawned under
that variable, loaded jammy's libpcre2 and warned
`libpcre2-8.so.0: no version information available`. `pg_ctl` starts postgres through `/bin/sh`,
which then inherits the variable. That is one more reason the units exec `postgres` directly
(M2-POSTGRES, M2-KCP-STORE already do), not `pg_ctl`.
- **Host requirements left:** glibc >= 2.35 (`libc.so.6 libm.so.6 libresolv.so.2
ld-linux-*.so`) and **host tzdata**. PGDG is built `--with-system-tzdata=/usr/share/zoneinfo`,
an absolute path that is not relocatable. On `ubuntu:22.04` (no tzdata in the image) the server
started with `TimeZone=UTC0`, and `SET timezone='UTC'` failed with
`invalid value for parameter "TimeZone"`. On every image with `/usr/share/zoneinfo` it worked.
check-host (M1) must require `/usr/share/zoneinfo/UTC` on gateway hosts.
- **Non-root:** every run was as uid 1000 on hat-8 and as uid 65534 in the container stand-ins.
initdb refuses root anyway.
- **Port 0:** postgres refuses it in every build:
`FATAL: 0 is outside the valid range for parameter "port" (1 .. 65535)` (the GUC minimum is 1).
This shapes the two stores differently:
- The gateway kcp store opens no TCP port at all: `listen_addresses=''` plus a unix socket
directory. M2-KCP-STORE already specifies this.
- Tenant stores that need TCP must lease a port (M2-POSTGRES "0 = leased"). The bound port is
read back from `ss -ltnp` (matched on the postmaster pid) and from line 4 of
`postmaster.pid`.
- kine itself takes `--listen-address 127.0.0.1:0` and announces the bound port.
- **Collation:** initdb with `--locale-provider=builtin --builtin-locale=C.UTF-8 -E UTF8`, which
gives `datlocprovider=b`, `datlocale=C.UTF-8` and `datcollversion=1`. The builtin provider is
versioned with PostgreSQL itself, independent of glibc and ICU. kine's `name` column is
`COLLATE "C"` anyway (kine v0.17.1 pkg/drivers/pgsql/pgsql.go:40). ICU 70.1 is still linked, and
its `unicode` collation reports `collversion=153.112`. M2-POSTGRES records that value and treats
a change as major-class only for databases that actually use ICU collations.
- **Security update churn:** 44 of the 47 pins are Ubuntu jammy packages (taken from jammy +
jammy-updates + jammy-security, highest version). Every Ubuntu security update to libssl3,
libxml2, krb5 and the rest changes the bundle's pins.
## 3. Verification steps (performed; M1-ASSETS repeats them in its assembly script)
1. PGDG key: `https://www.postgresql.org/media/keys/ACCC4CF8.asc`, imported into a fresh
GNUPGHOME. Fingerprint **B97B 0AFC AA1A 47F0 44F2 44A0 7FCC 7D46 ACCC 4CF8**
("PostgreSQL Debian Repository", rsa4096, 2011-10-13).
2. `https://apt.postgresql.org/pub/repos/apt/dists/jammy-pgdg/InRelease` was verified against that
key: Good signature, made 2026-10-11 09:30:45 UTC.
3. Ubuntu archive key **F6EC B376 2474 EDA9 D21B 7022 8719 20D1 991B C93C** ("Ubuntu Archive
Automatic Signing Key (2018)") was fetched by full fingerprint from keyserver.ubuntu.com. It
verified the `jammy`, `jammy-updates` and `jammy-security` InRelease files: archive.ubuntu.com
for amd64, ports.ubuntu.com for arm64. All Good.
4. Each `main/binary-<arch>/Packages.gz` sha256 was checked against the SHA256 section of its
signed InRelease.
5. Each .deb sha256 was checked against its `Packages` stanza (47 per arch, all matched; table
below).
6. Extraction without root: `ar x <deb>` then `tar -xf data.tar.*` into a scratch root (no dpkg,
no maintainer scripts). Then copy `usr/lib/postgresql/18/{bin,lib}`, `usr/share/postgresql/18`,
and every `*.so*` from `lib/<triplet>` and `usr/lib/<triplet>` into `usr/lib/sys`. Drop
`usr/share/postgresql/18/man` and `usr/lib/postgresql/18/lib/bitcode` (JIT bitcode; llvm-jit is
a separate package and is not installed).
7. Repack: `tar --sort=name --owner=0 --group=0 --numeric-owner --mtime=@1786473511 | zstd -19`.
The epoch is the newest mtime in postgresql-18.6.tar.bz2.
Helper scripts used: `apt_fetch.py`, `jammy_closure.py`, `pgrun.sh`, `pgmini.sh` and
`minisign_verify.py`, all in the session scratchpad. M1-ASSETS turns steps 1-7 into its own
script.
## 4. Pins
Repository bases: `https://apt.postgresql.org/pub/repos/apt/` (pgdg). For ubuntu,
`https://archive.ubuntu.com/ubuntu/` (amd64) and `https://ports.ubuntu.com/ubuntu-ports/` (arm64).
Filename = `<pool dir><package>_<version without epoch>_<arch>.deb`, for example
`https://apt.postgresql.org/pub/repos/apt/pool/main/p/postgresql-18/postgresql-18_18.6-1.pgdg22.04+2_amd64.deb`.
| package | version | repo | pool dir | sha256 amd64 | sha256 arm64 |
|---|---|---|---|---|---|
| postgresql-18 | 18.6-1.pgdg22.04+2 | pgdg | pool/main/p/postgresql-18/ | 2aab00b13babd3d9abc257bc5f6b00efa1f7c27cda071413f358d2e0cdb84420 | 8a379f0684b480b60e79657ac8178e8b00c835e7575e85ae746c091d336563d3 |
| postgresql-client-18 | 18.6-1.pgdg22.04+2 | pgdg | pool/main/p/postgresql-18/ | ad4e491adceaf1453162c62691473ace78ed81ab20f50d11b344a793d13ed4bd | 21f1b418621fc9c90785b18ec2d7fe4fd8ff111cc607fc3b3625464fc6e7886b |
| libpq5 | 18.6-1.pgdg22.04+2 | pgdg | pool/main/p/postgresql-18/ | 8af0b458834e219f3aa7c904fa1237ea484950486e3165a3a90204878e060530 | fc0d787c9da82bd0146e5e365989f3c92dbb3d73c3a32c62a2e4d56ab90933a6 |
| libaudit-common | 1:3.0.7-1ubuntu0.1 | ubuntu | pool/main/a/audit/ | 3e4e8586db5f57f431954a9125556fe6df86610592c0cf46ba1127fae8871e3f | 3e4e8586db5f57f431954a9125556fe6df86610592c0cf46ba1127fae8871e3f |
| libaudit1 | 1:3.0.7-1ubuntu0.1 | ubuntu | pool/main/a/audit/ | 00d7eb086dedc85bd5b9cbde650a1c71460499cb62e439acb761bc7bcb4eaaac | 963474b42258e8caa15eec1db61d8424fd6b7ef717ca4e371b3240506854b3fe |
| libcap-ng0 | 0.7.9-2.2build3 | ubuntu | pool/main/libc/libcap-ng/ | 124d0c8748a841f279e996298ef8aac69a249e294792f4b5e16dc00496f1d3ac | 884c8bc87da241abaa2b0ca80162486cf66eff67dfc545bea56622e1170e9ee5 |
| libcap2 | 1:2.44-1ubuntu0.22.04.3 | ubuntu | pool/main/libc/libcap2/ | c497efb7fa3cd19a7ff297d95c7c5d678063c80c9fc230c9d034a8ee02ca8446 | 7a8eb6549355a16f327ede574f12e6ebed983b52ab38ce4a618e6e9274b9d4f5 |
| libcom-err2 | 1.46.5-2ubuntu1.2 | ubuntu | pool/main/e/e2fsprogs/ | 552bc8169451cfdcf174a8da845b13ced0a55777149af5cf47491193161ed791 | 3a7b6a2b34c2171b7c8f9ddae58c93b6e416fcd9ac4fa8eb5a15a1a8f3f99afa |
| libdb5.3 | 5.3.28+dfsg1-0.8ubuntu3 | ubuntu | pool/main/d/db5.3/ | ce60e446c9cb0e94bb4b4d0fe4f969e772edec250deb186bacf955e4d196548b | e5b672aeaca690b3b7ee97b03dbc07ddb49be7b6ea0a770fa6707666ca1cef59 |
| libffi8 | 3.4.2-4 | ubuntu | pool/main/libf/libffi/ | b4f88c91fa6f4c942097be6abfc365fb133c5e147640168cbb7704fd855eac10 | a0cdf938e8da19bd2252c681f80268609a32d06496a1f8d0ebe1fa41debec697 |
| libgcc-s1 | 12.3.0-1ubuntu1~22.04.3 | ubuntu | pool/main/g/gcc-12/ | d383e642d83263147f7b4b69fb92cf1036e5246c6be3accf10b77f986684589a | 46626e1af6b159061e055f843654ee1f0f1e65fa280331ed47ee76ba1b1f6045 |
| libgcrypt20 | 1.9.4-3ubuntu3.3 | ubuntu | pool/main/libg/libgcrypt20/ | 8f16329a0aabf4900271a3687890721bbef307e60983acae5b92eeff12e799b3 | bd98a709641e7e61c8798bb84030886a96fd56424b053a6e9996a8391ce20597 |
| libgmp10 | 2:6.2.1+dfsg-3ubuntu1 | ubuntu | pool/main/g/gmp/ | d199a77e11701d4d620dd4f62e05740e3072dce893856ea3bbaa6ae9fa19fef1 | 32233a3bf7c8b19e86ef3629334f0bece716bb163605d12e0d3a443d57a401d7 |
| libgnutls30 | 3.7.3-4ubuntu1.9 | ubuntu | pool/main/g/gnutls28/ | 65e08f0b5d1e620cc5cc9478cd1035f83ea752a6fae23141afd2d111a8f6c555 | cce4bf33fa79446ddee42a992e569f12a57c1a6f8916a2e8349249be0f7ad655 |
| libgpg-error0 | 1.43-3 | ubuntu | pool/main/libg/libgpg-error/ | 1fbacdf9bd1e431cee874a697b339f6f925182bc79bba5a112b53669b33265c5 | ffa9029cdeb2786e0db1f614f3d2205f75f466a66b25e22aa89ecdf814491875 |
| libgssapi-krb5-2 | 1.19.2-2ubuntu0.10 | ubuntu | pool/main/k/krb5/ | 08e98a3dc74361cdb13c0d68bc73e0352552490546c1ad3ce5bff5426f6a2dfe | e1001bffd91dd306498ec1824a8330014399a0e0889a562069003726348d7f55 |
| libhogweed6 | 3.7.3-1build2 | ubuntu | pool/main/n/nettle/ | 2402ac51ebc760799b01f3fd4933126c5eb446e5b043832601fb589f5e50c363 | 844e5d093dd3982a85c134d47a9e22b129123fb817eb3eea96e936805b0f5170 |
| libicu70 | 70.1-2 | ubuntu | pool/main/i/icu/ | 58a154f6307289813da2276f900498ef536ae7c0522d2cf31a3c3c5cf62dfd9a | ac68372cf4a976e6a206858fd9b28c68e49d37d650b9b8653270038a6e7bc174 |
| libidn2-0 | 2.3.2-2build1 | ubuntu | pool/main/libi/libidn2/ | bc935e1871aa4a75d3f4ff549af32b7316aecfbea2200d85055878767b8398c8 | aeb29cc8deedc0710a97e44088ef0271dfe1197e77c0a0fc0c5602e14aeb938f |
| libk5crypto3 | 1.19.2-2ubuntu0.10 | ubuntu | pool/main/k/krb5/ | 40429c0099678fbc35045f0c63a10bc3490251d80cf1006095eaa0272c8d5e27 | e51d72b346813b57f26c09827f96eb856adb9b553814fbc61a5e8c8a25d292a3 |
| libkeyutils1 | 1.6.1-2ubuntu3 | ubuntu | pool/main/k/keyutils/ | 940daf78ee0229549b4eabc92ffd79dd038ed96a44e2e912305a23540cf22a0a | fc26b34c487ea5187585ee0f2c67c738b97e8bae434cca4b546d86743b9e6c80 |
| libkrb5-3 | 1.19.2-2ubuntu0.10 | ubuntu | pool/main/k/krb5/ | 0da3e0df7d6314615160a42f65ff345986783be02cd65a6ce797cfbacc7bcb0d | 8de066e35be457bf0c53c995f8baa270ad61566dcd892d0a7cc3c0438ff513b7 |
| libkrb5support0 | 1.19.2-2ubuntu0.10 | ubuntu | pool/main/k/krb5/ | 0406052ac2d267f05088ada6d36df61aaa338cbe8d5e13fb1d7f79da560817ed | c535d5ccf5ea99fb63d86b33082c5ed553a6aa8c7cbf3fed1ef0dac1d7b7dcd6 |
| libldap-2.5-0 | 2.5.20+dfsg-0ubuntu0.22.04.1 | ubuntu | pool/main/o/openldap/ | a3ee4ac116a2c50c9ddfe9de91c0781a50704244771474334340cdce02f5cd4f | 305ec2bf85ad8e8a6309b1a48e601f172503a267ca90d511ccbb098cdf009d6b |
| liblz4-1 | 1.9.3-2build2 | ubuntu | pool/main/l/lz4/ | ac9b54d0feb840345060c74fb687675c5e1eb2b195effafae38c5f9991041e98 | 3e2274893e0963c758f0d18ba80d033736909373247a7deb211e9ff590a09133 |
| liblzma5 | 5.2.5-2ubuntu1.1 | ubuntu | pool/main/x/xz-utils/ | af77f099e7fa95a31267023535832804382fa66572a6b38002187be6e15116cb | 97f4c7e360aa229d3bf0a5b56b204652ce4f1704536dc0256c0e2537b356f397 |
| libnettle8 | 3.7.3-1build2 | ubuntu | pool/main/n/nettle/ | 6da09e685e5587f8873f662b007de45f75424fb33d2c4a51837380e683d33c7e | bcd667d9a51c2474f7ffc52807ab4a24f8b877b6da1a6f5b75e50e48ec4d634a |
| libnuma1 | 2.0.14-3ubuntu2 | ubuntu | pool/main/n/numactl/ | 0721c89001fbbd1ada23e89da5d60e762763c1a7b3dc814a2e9a518480a8043d | ca67a774b20ed639361715ef0981d236f294ff2987579b3d18faede67c3a9152 |
| libp11-kit0 | 0.24.0-6ubuntu0.1 | ubuntu | pool/main/p/p11-kit/ | 9d949950970a062a81051a78ec4075b8dba608f063afaa5c8752a612d139a9b5 | 0cfad0790be90ccb56de67e6c58a67d73e4769028eb19a2c0d799fa19478c2ff |
| libpam0g | 1.4.0-11ubuntu2.8 | ubuntu | pool/main/p/pam/ | faaf4b6d1bfb33a7fc4e195448439c202869d67c8de17c4ecd29b7719e0ac4d6 | f61ae0342dfba2b6c6b61f3d46925371434a4719a251f7585f9baa1341fcc9e2 |
| libpcre2-8-0 | 10.39-3ubuntu0.1 | ubuntu | pool/main/p/pcre2/ | 01a5a74f3d305cad32fffffdc200c2a92fd8e5a01bff97de76ac598fb444d53e | b85beefd54108f1c34a89cb26688eb1b2eeaa099e4995091295a1d39f9c67a91 |
| libreadline8 | 8.1.2-1 | ubuntu | pool/main/r/readline/ | 51fd16f608efa710c987e4b071ffba0bec63a9ac8e55eddd5770f5e5252edc09 | 54291f2dd185d3a759227b31b7de4269e495a084fdad89e24a9cc4d0d20888a7 |
| libsasl2-2 | 2.1.27+dfsg2-3ubuntu1.2 | ubuntu | pool/main/c/cyrus-sasl2/ | 391cb48d7e601d5bb590da8d426750acf6c987241ba280a36227873b94f20cee | d4f60677a0024f2b888a563f1d396a1f43fe22097e01d0a7048695c3c1ef8e18 |
| libsasl2-modules-db | 2.1.27+dfsg2-3ubuntu1.2 | ubuntu | pool/main/c/cyrus-sasl2/ | 0e222c26d70c2cfd132c8de035bcf9673067b6cbfea0a7421dced7b6256ae774 | 438df2296b2f828ab05287fadbf1a29f2e3bc0d618807b01128829313be91895 |
| libselinux1 | 3.3-1build2 | ubuntu | pool/main/libs/libselinux/ | b96c6b40ee2388bd51341cb11c0f1d5bcca29b9180b6e3a77a06b881f2913f7e | 3a0726b2174a40fa28e74712867aa6e40d3d725543e8f668af9e9e59a28c817c |
| libssl3 | 3.0.2-0ubuntu1.30 | ubuntu | pool/main/o/openssl/ | d1b4636e32ac6d3b95d583ac05ac548b1aa248414bae1c51b14f54be0f8fd7bc | 1c4b8cc736eed8a0caafc385f854fa6852382221d84be4795f1d19a52b9fec53 |
| libstdc++6 | 12.3.0-1ubuntu1~22.04.3 | ubuntu | pool/main/g/gcc-12/ | 29ef3d289b272704b75141906c8f683db0f2bdb0942061366691333bfb289bdc | 59d244119e6129c7e9c0897491c0e210e925d15881b2dedcf8965ceb4fbf1aa3 |
| libsystemd0 | 249.11-0ubuntu3.22 | ubuntu | pool/main/s/systemd/ | 4fae4644bcfd86f891faae12cedbf30aca280cf76a034768aec3a03fe42ddc25 | 9805c8c136fd41d9bff355297fdf6f811529960908711f647e38e1fd39a198ec |
| libtasn1-6 | 4.18.0-4ubuntu0.2 | ubuntu | pool/main/libt/libtasn1-6/ | a0903b4fae3ac2548a22d8822382e6d23586e42196d83ff67e82df36cd39fc6c | d9f0ed3ca0cb86f6116bb59388aea92b021c05797b4fba7451a2fed594cb1e1e |
| libtinfo6 | 6.3-2ubuntu0.3 | ubuntu | pool/main/n/ncurses/ | 3b8dff43a31168f5020928f2ab9a97324c5b47ae708311781f6bd01306c96214 | 971b5fd1cf8f7d68ba00f40d8eec6474fae6a6adbe19883aa42dd2b74c672e4a |
| libunistring2 | 1.0-1 | ubuntu | pool/main/libu/libunistring/ | 988fb59de6a0a313844277e8a6e45d3dc16f5ea75a603a46b16b936426f0ac65 | 59e4af5c1fac70d1fbc19f3c732331ace8449f1f7112887e7b56b8370bd8942b |
| liburing2 | 2.1-2build1 | ubuntu | pool/main/libu/liburing/ | f6e9bdf50c9683cd9a74ad92d51dde085f40baf0a5fcd8a56fc68425c1bd3c5f | a9221f0d47fd32819558f7a91677e97c72bd88646d38d369ffa70720ac49c27d |
| libuuid1 | 2.37.2-4ubuntu3.6 | ubuntu | pool/main/u/util-linux/ | 97eaa1090c2e7ddaad55d58f9d0d7907d64b70f89228405d68a7ce7d56a89d67 | 2478ccd8df956d8e5cf087bff1471bcd9991838be9c7999e5737d092034c492d |
| libxml2 | 2.9.13+dfsg-1ubuntu0.14 | ubuntu | pool/main/libx/libxml2/ | 16e395b2c52d6ab232053468febc69f0876956ec2fae357a941a2a8dab36574f | 1e85b731baabd51331d70a6392c652bc39fa2c231ff784555bd6a61631009387 |
| libxslt1.1 | 1.1.34-4ubuntu0.22.04.5 | ubuntu | pool/main/libx/libxslt/ | 5e50bc83e9365942cf63272d8a45ce71170aaf4a1a4e3c4b30f591f6194d77ef | dd533b9980918ca64531254ccbdfd5efc492bbef37c70f0b5559b3b8f306b626 |
| libzstd1 | 1.4.8+dfsg-3build1 | ubuntu | pool/main/libz/libzstd/ | ae7db00ce8b093e50c994518b90203544e063b4bc574836a048bb142b950b2c9 | 0102eb80384c2432c4be819663341a1c4e511d05f13ec1467643696531ec6491 |
| zlib1g | 1:1.2.11.dfsg-2ubuntu9.2 | ubuntu | pool/main/z/zlib/ | 9dc17e51a1be2d9ed63b7b84ef0e4e29c5abe6f1bc62cb03e7181483cce8a2f2 | cfd99d1ca41e710fdd3f68bfdc4d36dd24e14377656764f00b215b89cf6976e8 |
Debs per arch: 47 (3 PGDG + 44 Ubuntu); 30,428,248 bytes (amd64) and 29,440,882 bytes (arm64).
Index digests at pin time (signed InRelease of 2026-10-11): jammy-pgdg
`main/binary-amd64/Packages.gz` ca96279cfb8c49c2ac59dbdb083cb099223ad9e9f23313951eb8f715c5a1577c and
`main/binary-arm64/Packages.gz` 911dc4a3e960d5df5679e29fe7756c6d0eee1f9ffb79fbc0a5d8cd3735f8f32b.
Assembled trees (this spike's assembly; M1-ASSETS re-pins its own output):
| arch | raw bytes | zstd -19 bytes | sha256 of .tar.zst |
|---|---|---|---|
| amd64 | 84,342,619 (pg bin 15.8 M, pg lib 5.7 M, sys libs 61.0 M of which libicudata 29.5 M, share 1.9 M) | 25,654,278 | 467436b73669a7ca00ee3c5c44c468f892cabcf670dd529ebeeb99aeb63123fd |
| arm64 | 81,912,395 | 25,072,723 | 39847c32e8c7d71667ce33c5b625a96d0a3273cb0f1bcfc9f0c42545e4eb12b2 |
Size lever for M0-SIZE-BUDGET: ICU is 30 MB of the 84 MB, and the builtin provider does not use
it. PGDG links it, though, so it cannot be dropped without the fallback build.
## 5. Measurements on hat-8 (uid 1000, arbitrary path, kine v0.17.1 `/usr/local/bin/kine`)
Harness: initdb, pg_ctl on a unix socket, psql, kine against the socket, an etcd v3 put+get through
kine (go.etcd.io/etcd/client/v3 v3.6.8), pg_dump, stop, port 0 attempt, then a leased TCP port.
Commands, PGDG tree (`B=<root>/usr/lib/postgresql/18/bin`, `LD_LIBRARY_PATH=<root>/usr/lib/sys`):
```
$B/initdb -D $T/data -U postgres --auth=trust -E UTF8 --locale-provider=builtin --builtin-locale=C.UTF-8
$B/pg_ctl -D $T/data -l $T/pg.log -w -o "-c listen_addresses='' -c unix_socket_directories='$T/run' -p 5432" start
$B/psql -h $T/run -U postgres -d postgres -Atc 'select 1'
kine --endpoint "postgres://postgres@localhost/kine?host=$T/run&sslmode=disable" --listen-address 127.0.0.1:0
$B/pg_dump -h $T/run -U postgres -d kine -f $T/kine.sql
$B/pg_ctl -D $T/data -m fast -w stop
$B/postgres -D $T/data -c listen_addresses=127.0.0.1 -c unix_socket_directories='' -p 0
```
| step | PGDG bundle (choice) | source build (fallback prototype) |
|---|---|---|
| initdb | 0.338 s | 0.266 s |
| pg_ctl start, unix socket only (no TCP listener: 0) | 0.109 s | 0.103 s |
| first `SELECT 1` after start returns | 0.009 s (0.123 s from start command) | 0.005 s (0.113 s) |
| kine on the socket | announced and bound 127.0.0.1:37867 (port 0) | 127.0.0.1:37055 |
| etcd put+get through kine | ok, rev 3, 4.7 ms | ok, rev 3, 5.2 ms |
| pg_dump of the kine db | 0.030 s, 3,385 bytes, has `CREATE TABLE public.kine` | 0.027 s, 3,329 bytes |
| pg_ctl stop -m fast | 0.102 s | 0.102 s |
| `-p 0` | refused: `0 is outside the valid range for parameter "port" (1 .. 65535)` | same |
| leased TCP port | 33959 bound, read back from ss and postmaster.pid, `inet_server_port()=33959` | 38745, same |
| RSS, postmaster tree idle (sum of VmRSS) | 101,736 kB | 56,812 kB |
| RSS, postmaster tree with kine connected | 183,752 kB | 118,668 kB |
| RSS, kine | 36,696 kB | 36,872 kB |
| fresh data dir | 66.3 MB | 48.6 MB |
Distro stand-ins (container images, uid 65534, `--network none`, tree bind-mounted read-only at
`/opt/any/where/...`): initdb + pg_ctl on a unix socket + `SELECT 1` + pg_dump + stop all passed
for the PGDG bundle and for the source build on these images:
- `ubuntu:22.04@sha256:5ec03bb3441e8b0bf3b4f9cd4629a1ae763010dc3035bb8da3ae6cf026486401` (glibc 2.35, no tzdata)
- `ubuntu:24.04@sha256:534baea6a22c03a63003dbc8dbe78fe34bc0d7e595d9a9dc9834884ff530eb55` (2.39, no tzdata)
- `debian:12-slim@sha256:7c7b2c966bc9ee8cedfeef67e0e279108992c77681fa595db4a9d65c06ccc587` (2.36)
- `debian:13-slim@sha256:a29215f6a35e51e22adffa17f89e9d2ef06214e64a2bad10d765c46aea49f11f` (2.41)
- `fedora:44@sha256:43b29f65a41eb9c35e1cd5323e3bdf3b655c2357a9f4f1ff2f9c2798e5045d80` (2.43)
On the images without tzdata, PGDG ran with `TimeZone=UTC0` and refused `SET timezone='UTC'`. The
source build carries its own `share/timezone` and accepted `UTC` and `Europe/Berlin` everywhere.
arm64: the PGDG arm64 bundle passed the same test in `debian:12-slim` (linux/arm64) under hat-8's
qemu-aarch64 binfmt (`uname -m` = aarch64).
Without the bundle's libraries, the bare PGDG debs on hat-8 failed:
`libldap-2.5.so.0: cannot open shared object file`. libicu*.so.70 and libpq.so.5 were missing as
well. On minimal images, libnuma, liburing, libxml2, readline, krb5 and OpenSSL 3 were also
missing (debian:13 additionally lacked libgnutls30). That is why the bundle carries the whole
jammy closure.
## 6. Fallback: build from the official source tarball (status: prototyped, not committed)
Use only if the user rejects section 2's costs: the 44 Ubuntu pins, LD_LIBRARY_PATH, host tzdata,
25 MB zstd and 100 MB RSS. Measured prototype, copy kept at
`$TASK_STATE_DIR/build-postgres.sh.fallback-prototype`:
- Source: `https://ftp.postgresql.org/pub/source/v18.6/postgresql-18.6.tar.bz2`, sha256
`555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f` (matches the published
`.sha256`; upstream publishes no PGP signature for source tarballs).
- Since PG 17 the tarball ships no generated parser, so the build host needs bison, flex and perl.
The prototype builds pinned host tools with zig itself:
- m4-1.4.21: f25c6ab5...ab66, GPG Good, Eric Blake 71C2CC22B1C4602927D2F3AAA7A16B4A2527436A
- bison-3.8.2: 9bba0214...5bf2, GPG Good, Akim Demaille 7DF84374B1EE1F9764BBE25D0DDCAA3278D5264E
- flex-2.6.4: e87aae03...e995, GPG Good, Will Estes 56C67868E93390AA1039AD1CE4B29C8D64885307
- Toolchain: zig 0.16.0, x86_64 sha256
70e49664a74374b48b51e6f3fdfbf437f6395d42509050588bd49abe52ba3d00, minisign Good with
`RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U`; aarch64 sha256
ea4b09bfb22ec6f6c6ceac57ab63efb6b46e17ab08d21f69f3a48b38e1534f17. It targets
`<arch>-linux-gnu.2.28`, so the glibc floor is 2.28 regardless of build host, and it
cross-compiles arm64 on hat-8 with no container, no qemu and no apt.
- Configure flags: `--prefix=/opt/postgresql --without-icu --without-readline --without-zlib
--without-openssl` plus every other optional library off. It is not `--with-systemd`. The make
variable `rpathdir='$$ORIGIN/../lib'` gives RUNPATH `$ORIGIN/../lib`. The prefix contains
"postgres", so pkglibdir = lib/ and sharedir = share/.
- Result: NEEDED only libc/libm/libpthread/libdl/librt, max GLIBC_2.27, own tzdata, no env vars.
| arch | raw | zstd -19 | build time on hat-8 |
|---|---|---|---|
| amd64 | 16.2 MB | 4,853,858 bytes | 96-180 s |
| arm64 | - | 4,380,281 bytes (cross) | 206 s |
- Not yet byte-reproducible: two amd64 runs in different work directories differed in
`bin/postgres`, because pg_config's compiled-in CC, CFLAGS and configure arguments embed the
work path. The fix is a fixed build path. The dry-run test (`--dry-run` prints every pin) would
be committed with the script.
## 7. Rejected candidates (facts recorded before the user ruled repackagers out)
- **zonky** `io.zonky.test.postgres:embedded-postgres-binaries-linux-{amd64,arm64v8}:18.6.0`
(Maven Central, jar sha256 amd64
008ee189eaa7ca2b58bd01dd950291e638b998392cf982297acd4779ec898ac1, arm64
5f54e016723afa6213925a8acb939bac52944b1d348f65c7a20f0cd2928c31be). Problems:
- Only `initdb pg_ctl postgres`: no pg_dump and no psql.
- Bundles EOL OpenSSL 1.1 and ICU 60.
- The maven `.asc` is signed by key 6B02ED0ED53F9FD3, which is not retrievable from
keyserver.ubuntu.com or keys.openpgp.org, so it is unverifiable.
- **theseus-rs** `postgresql-18.6.0-{x86_64,aarch64}-unknown-linux-gnu.tar.gz` (sha256
bb3d09f8...ca61 and 5d2b5e8b...ee13). Problems:
- Only a same-channel `.sha256`. GitHub attestation API returns 404.
- Built on Debian 12 gcc 12 with `--without-icu --with-system-tzdata`.
- Links host libssl3, libxml2, krb5, lz4, zstd and readline (not bundled).
- The musl variants need a musl host.
## 8. Proposed internal/versions/versions.json entry (the lead edits the file)
```json
"postgres": {
"version": "18.6",
"how": "PGDG official debs (apt.postgresql.org jammy-pgdg 18.6-1.pgdg22.04+2: postgresql-18, postgresql-client-18, libpq5) plus their Ubuntu 22.04 shared-library closure (44 debs from jammy, jammy-updates, jammy-security), each verified by signed InRelease -> Packages.gz sha256 -> deb sha256, extracted without root (ar + tar) into <root>/usr/lib/postgresql/18/{bin,lib}, <root>/usr/share/postgresql/18, <root>/usr/lib/sys; see docs/research/2026-10-11-vm-lifecycle/pg-source.md for the 47 per-arch deb pins",
"pgdgKeyFingerprint": "B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8",
"ubuntuArchiveKeyFingerprint": "F6ECB3762474EDA9D21B7022871920D1991BC93C",
"debs": {
"linux/amd64": {"postgresql-18": "2aab00b13babd3d9abc257bc5f6b00efa1f7c27cda071413f358d2e0cdb84420", "postgresql-client-18": "ad4e491adceaf1453162c62691473ace78ed81ab20f50d11b344a793d13ed4bd", "libpq5": "8af0b458834e219f3aa7c904fa1237ea484950486e3165a3a90204878e060530"},
"linux/arm64": {"postgresql-18": "8a379f0684b480b60e79657ac8178e8b00c835e7575e85ae746c091d336563d3", "postgresql-client-18": "21f1b418621fc9c90785b18ec2d7fe4fd8ff111cc607fc3b3625464fc6e7886b", "libpq5": "fc0d787c9da82bd0146e5e365989f3c92dbb3d73c3a32c62a2e4d56ab90933a6"}
},
"glibcFloor": "2.35",
"icu": "70.1 (unicode collversion 153.112; unused: initdb --locale-provider=builtin --builtin-locale=C.UTF-8, datcollversion 1)",
"invariant": "Relocatable ONLY with the deb layout kept (<root>/usr/lib/postgresql/18/bin; PostgreSQL resolves share and pkglib relative to that tail) and LD_LIBRARY_PATH=<root>/usr/lib/sys set on the postgres/initdb/pg_dump exec alone, never on the core (it leaks jammy libs into any child: grep under it loaded jammy libpcre2). Timezone files are the HOST's /usr/share/zoneinfo (compiled-in absolute path): check-host requires /usr/share/zoneinfo/UTC. postgres REFUSES -p 0 (GUC minimum 1): the kcp store is unix-socket only (listen_addresses=''), tenant stores lease. Runs as non-root only. Any Ubuntu jammy security update changes the closure pins."
}
```
Replace the existing `embedded-postgres` entry ("the host's own binaries ... on PATH") when
M2-POSTGRES lands. Its port and root invariants still hold.