Skip to content

Commit e09968c

Browse files
authored
Bundle libxml2 + libicu74 to support Ubuntu 25.10 / 26.04 (#14)
* fix: bundle libxml2 + libicu74 to support Ubuntu 25.10 / 26.04 Ubuntu 25.10 (Plucky) shipped libxml2 2.14, which bumped the SONAME from .so.2 to .so.16, and renamed the apt package from libxml2 to libxml2-16. ICU also moved from 74 to 76. The theseus-rs PostgreSQL binaries that pg0 bundles are linked against libxml2.so.2 and libicu*.so.74, both of which are unavailable on 25.10 (and inherited on the upcoming 26.04 LTS), so `pg0 start` failed with "missing required system libraries". Reproduction added in docker-tests/test_ubuntu_amd64.sh: ubuntu:24.04 passes against the released v0.13.0 binary; ubuntu:25.10 fails. Fix: download libxml2 (2.9.14) and libicu74 (74.2-1ubuntu3.1) from the Ubuntu 24.04 archive at build time, embed the .so files into the pg0 binary, extract them into <installation>/<ver>/lib/ at runtime alongside the postgres binary, create the SONAME symlinks (libxml2.so.2 -> libxml2.so.2.9.14 etc.), and prepend that lib dir to LD_LIBRARY_PATH so postgres / initdb / pg_ctl / psql resolve through the bundled copies. Bundle is built only for Linux GNU targets (x86_64 + aarch64). macOS, Windows, and the musl Linux builds get an empty bundle - on those platforms the libs are either system-provided in a forward-compatible way (Alpine 3.20/3.21 with ICU 74) or shipped by other means (macOS frameworks, Windows .dlls beside postgres.exe). Upgrade safety: the runtime extraction runs even when postgres is already extracted from a previous pg0 release, so users on existing ~/.pg0/installation/ trees pick up the new libs automatically without having to wipe the directory. Other: - versions.env now pins exact .deb URLs + SHA256s for reproducibility. - ar / zstd / tar / flate2 / sha2 / hex added as build-dependencies for cracking open the .deb (ar archive of zstd-compressed tar). - Linux GNU binary grows ~10 MB (matches the gz-compressed bundle). - test_missing_libs.sh now uses libgssapi-krb5-2 as the canary missing lib, since libxml2 is no longer required from the host. - New docker-tests/test_ubuntu_amd64.sh covers ubuntu:24.04 + ubuntu:25.10 and is wired into CI (with PG0_BINARY_PATH built fresh, like the existing missing-libs job). - README updated: removes libxml2 / libicu from the apt install snippets, adds a "Tested and Supported Platforms" table that explicitly calls out which Alpine versions work (3.20 / 3.21) vs not (3.22+), and adds Ubuntu 24.04 / 25.10 / 26.04 as supported. * fix: source bundled libs from Ubuntu 22.04 to keep manylinux_2_35 baseline The previous commit pulled libxml2 and libicu74 from Ubuntu 24.04, but those .so files require GLIBC 2.38, which is newer than the manylinux_2_35 baseline (Ubuntu 22.04 / Debian 12) the wheel ships under. CI's Linux x86_64-gnu wheel test failed on the ubuntu-22.04 runner with: postgres: libm.so.6: version `GLIBC_2.38' not found (required by libxml2.so.2) Root-cause realization: the bundled postgres binary itself does NOT have libicu in DT_NEEDED. It only links against libxml2.so.2; libxml2 is what pulls libicu in transitively. So the ICU major version we ship just needs to match whatever ICU the bundled libxml2 was built against - it does not have to match the original SONAME `.so.74` from Ubuntu 24.04. Switch to Ubuntu 22.04 sources: - libxml2 2.9.13+dfsg-1ubuntu0.11 (.so.2 SONAME, transitively wants .so.70) - libicu70 70.1-2ubuntu1 (icudata + icui18n + icuuc) These libs require at most GLIBC 2.34, comfortably below our 2.35 wheel baseline, and are still ABI-compatible everywhere we care about (22.04, 24.04, 25.10, 26.04). They sit alongside whatever libicu the host has installed - on 24.04 the system libicu74 is unused; on 25.10 the system libicu76 is unused; in both cases the bundled postgres resolves through our libxml2 -> our libicu70. Verified locally: - Vanilla ubuntu:22.04 (glibc 2.35, no libxml2/libicu installed): pg0 start + psql + stop works. - ubuntu:24.04 + ubuntu:25.10: docker-tests/test_ubuntu_amd64.sh passes. - python:3.11-slim debian: docker-tests/test_debian_amd64.sh passes. - missing-libs detection still works. Comments in build.rs and main.rs updated to reflect the new sourcing rationale; README's Ubuntu 25.10 row updated from "ICU 74" to "libicu70".
1 parent d088f4d commit e09968c

11 files changed

Lines changed: 704 additions & 89 deletions

File tree

.github/workflows/ci.yml

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,22 +127,32 @@ jobs:
127127
- platform: debian-amd64
128128
cli_script: docker-tests/test_debian_amd64.sh
129129
python_script: docker-tests/python/test_debian_amd64.sh
130+
needs_local_binary: false
130131
- platform: alpine-amd64
131132
cli_script: docker-tests/test_alpine_amd64.sh
132133
python_script: docker-tests/python/test_alpine_amd64.sh
134+
needs_local_binary: false
135+
# Ubuntu 24.04 + 25.10. 25.10 only works with the libxml2/ICU
136+
# bundling added in this binary, so the test must run against a
137+
# fresh local build rather than the latest released artifact.
138+
- platform: ubuntu-amd64
139+
cli_script: docker-tests/test_ubuntu_amd64.sh
140+
python_script: ""
141+
needs_local_binary: true
133142
- platform: missing-libs-debian-amd64
134143
cli_script: docker-tests/test_missing_libs.sh
135144
python_script: ""
145+
needs_local_binary: true
136146

137147
steps:
138148
- uses: actions/checkout@v4
139149

140150
- name: Install Rust
141-
if: matrix.platform == 'missing-libs-debian-amd64'
151+
if: matrix.needs_local_binary
142152
uses: dtolnay/rust-toolchain@stable
143153

144154
- name: Build Linux binary
145-
if: matrix.platform == 'missing-libs-debian-amd64'
155+
if: matrix.needs_local_binary
146156
run: |
147157
cargo build --release
148158
echo "PG0_BINARY_PATH=$(pwd)/target/release/pg0" >> $GITHUB_ENV

Cargo.lock

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ tar = "0.4"
2929
[target.'cfg(windows)'.dependencies]
3030
zip = { version = "2", default-features = false, features = ["deflate"] }
3131

32+
[build-dependencies]
33+
# .deb files are ar archives wrapping a zstd-compressed tar; we crack them open
34+
# at build time to extract libxml2.so.2 and the ICU 74 .so files so they can
35+
# be bundled into the pg0 binary.
36+
ar = "0.9"
37+
zstd = "0.13"
38+
tar = "0.4"
39+
flate2 = "1"
40+
sha2 = "0.10"
41+
hex = "0.4"
42+
3243
[profile.release]
3344
strip = true
3445
lto = true

README.md

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,17 @@ Use pg0 for local development, testing, CI/CD pipelines, or any scenario where y
2222

2323
## Supported Platforms
2424

25+
This table describes which **binaries** we publish. Whether a binary actually runs on a given OS release depends on the libraries that distro ships - see [Tested and Supported Platforms](#tested-and-supported-platforms) for the per-distribution story (e.g. Alpine 3.20-3.21 work, Alpine 3.22+ does not).
26+
2527
| Platform | Architecture | Binary |
2628
|----------|--------------|--------|
27-
| macOS | Apple Silicon (M1/M2/M3) | `pg0-macos-arm64` |
28-
| Linux | x86_64 (glibc) | `pg0-linux-amd64-gnu` |
29-
| Linux | x86_64 (musl/Alpine) | `pg0-linux-amd64-musl` |
30-
| Linux | ARM64 (glibc) | `pg0-linux-arm64-gnu` |
31-
| Linux | ARM64 (musl/Alpine) | `pg0-linux-arm64-musl` |
32-
| Windows | x64 | `pg0-windows-amd64.exe` |
29+
| macOS | Apple Silicon (M1/M2/M3) | `pg0-darwin-aarch64` |
30+
| macOS | Intel | `pg0-darwin-x86_64` |
31+
| Linux | x86_64 (glibc, e.g. Debian/Ubuntu) | `pg0-linux-x86_64-gnu` |
32+
| Linux | x86_64 (musl, e.g. Alpine) | `pg0-linux-x86_64-musl` |
33+
| Linux | ARM64 (glibc) | `pg0-linux-aarch64-gnu` |
34+
| Linux | ARM64 (musl) | `pg0-linux-aarch64-musl` |
35+
| Windows | x64 | `pg0-windows-x86_64.exe` |
3336

3437
## Features
3538

@@ -119,15 +122,19 @@ pg0 works in Docker containers. Here are the minimal setup steps for each suppor
119122

120123
```dockerfile
121124
FROM debian:bookworm-slim
122-
# or: python:3.11-slim, ubuntu:22.04, etc.
125+
# or: python:3.11-slim, ubuntu:22.04, ubuntu:24.04, ubuntu:25.10, etc.
123126

124-
# Install required dependencies
127+
# Install required dependencies. libxml2 and ICU are bundled into the pg0
128+
# binary so they do not need to be installed - this means pg0 works on
129+
# Ubuntu 25.10+ where libxml2.so.2 has been replaced by libxml2.so.16.
130+
# tzdata is needed because PostgreSQL reads /usr/share/zoneinfo at startup,
131+
# and libreadline is needed by `pg0 psql`.
125132
RUN apt-get update && apt-get install -y \
126133
curl \
127-
libxml2 \
128134
libssl3 \
129135
libgssapi-krb5-2 \
130-
&& apt-get install -y libicu72 || apt-get install -y libicu74 || apt-get install -y libicu* \
136+
tzdata \
137+
libreadline8 \
131138
&& rm -rf /var/lib/apt/lists/*
132139

133140
# Create non-root user (PostgreSQL cannot run as root)
@@ -182,10 +189,10 @@ docker run -d myimage sh -c "pg0 start && exec your-application"
182189
Run pg0 in a Docker container with a single command:
183190

184191
```bash
185-
# Debian/Ubuntu
192+
# Debian/Ubuntu (works on 22.04, 24.04, 25.10, 26.04, ...)
186193
docker run --rm -it python:3.11-slim bash -c '
187194
apt-get update -qq &&
188-
apt-get install -y curl libxml2 libssl3 libgssapi-krb5-2 libicu72 &&
195+
apt-get install -y curl libssl3 libgssapi-krb5-2 tzdata libreadline8 &&
189196
useradd -m pguser &&
190197
su - pguser -c "curl -fsSL https://raw.githubusercontent.com/vectorize-io/pg0/main/install.sh | bash &&
191198
export PATH=\"\$HOME/.local/bin:\$PATH\" &&
@@ -448,34 +455,59 @@ Data is stored in `~/.pg0/instances/<name>/data/` (or your custom `--data-dir`)
448455

449456
## Runtime Dependencies
450457

451-
pg0 bundles PostgreSQL but requires some shared libraries at runtime. These are typically pre-installed on most systems, but may need to be added in minimal environments like Docker.
458+
pg0 bundles PostgreSQL, pgvector, libxml2 and ICU directly into the binary, so it works on minimal systems without those libraries installed (including Ubuntu 25.10+ where the libxml2 SONAME was bumped to `.so.16`). A few common shared libraries still need to be present on the host because they are reused from the OS.
452459

453460
**macOS:** No additional dependencies required.
454461

455462
**Linux (Debian/Ubuntu):**
456463
```bash
457-
apt-get install libxml2 libssl3 libgssapi-krb5-2
464+
apt-get install libssl3 libgssapi-krb5-2 tzdata libreadline8
458465
```
459466

460467
**Linux (Alpine):**
461468
```bash
462469
apk add icu-libs lz4-libs libxml2
463470
```
471+
(Alpine uses the musl pg0 binary, which dynamically links against system ICU and libxml2. See the support table below for compatible Alpine versions.)
464472

465473
### Why these dependencies?
466474

467475
The bundled PostgreSQL binaries are compiled with these features enabled:
468476

469-
| Library | Purpose | Can disable? |
470-
|---------|---------|--------------|
471-
| OpenSSL (`libssl`) | SSL/TLS connections | Not recommended |
472-
| GSSAPI (`libgssapi-krb5`) | Kerberos authentication | Rarely needed locally |
473-
| libxml2 | XML data type and functions | Rarely needed |
474-
| ICU (`icu-libs`) | Unicode collation (Alpine only) | glibc builds don't need it |
475-
| LZ4 (`lz4-libs`) | WAL/TOAST compression | Small impact |
477+
| Library | Purpose | Bundled in pg0? |
478+
|---------|---------|-----------------|
479+
| libxml2 | XML data type and functions | Yes (Linux GNU only) |
480+
| ICU (`libicu*`) | Unicode collation | Yes (Linux GNU only); Alpine uses system `icu-libs` |
481+
| OpenSSL (`libssl`) | SSL/TLS connections | No - host-provided |
482+
| GSSAPI (`libgssapi-krb5`) | Kerberos authentication | No - host-provided |
483+
| LZ4 (`lz4-libs`) | WAL/TOAST compression | No - usually pre-installed |
484+
| tzdata (`/usr/share/zoneinfo`) | Time zone data | No - host-provided |
485+
| Readline (`libreadline`) | Interactive `pg0 psql` | No - host-provided |
476486

477487
Most desktop Linux distributions and macOS have these libraries pre-installed. You only need to install them manually in minimal Docker images or bare-metal servers.
478488

489+
## Tested and Supported Platforms
490+
491+
The table below reflects what we actually exercise via the docker tests in `docker-tests/` plus the platforms targeted by the release CI. Anything not in the table is best-effort: it may work, but we do not test it.
492+
493+
| Platform / Image | Architecture | Status | Notes |
494+
|---|---|---|---|
495+
| macOS (Apple Silicon, M1/M2/M3) | aarch64 | ✅ Supported | Released binary; built in CI |
496+
| macOS (Intel) | x86_64 | ✅ Supported | Released binary; built in CI |
497+
| Debian 12 (bookworm) | x86_64, aarch64 | ✅ Tested | `docker-tests/test_debian_*.sh` (python:3.11-slim) |
498+
| Debian 13 (trixie) | x86_64, aarch64 | ✅ Expected to work | Same glibc / libxml2 ABI as bookworm |
499+
| Ubuntu 22.04 (Jammy) | x86_64, aarch64 | ✅ Expected to work | glibc 2.35 baseline; matches release CI build host |
500+
| Ubuntu 24.04 (Noble) | x86_64 | ✅ Tested | `docker-tests/test_ubuntu_amd64.sh` |
501+
| Ubuntu 25.10 (Plucky) | x86_64 | ✅ Tested | `docker-tests/test_ubuntu_amd64.sh` - works thanks to bundled libxml2.so.2 + libicu70 |
502+
| Ubuntu 26.04 (next LTS) | x86_64, aarch64 | ✅ Expected to work | Inherits libxml2 2.14 / ICU 76 from 25.10 |
503+
| Alpine 3.20 | x86_64, aarch64 | ✅ Tested | `docker-tests/test_alpine_*.sh` (python:3.12-alpine3.20). Uses musl + system ICU 74 |
504+
| Alpine 3.21 | x86_64, aarch64 | ✅ Expected to work | Same ICU 74 line as 3.20 (untested but ABI-compatible) |
505+
| Alpine 3.22, 3.23+ | x86_64, aarch64 | ❌ Not supported | Ships ICU 76; the upstream theseus-rs musl PostgreSQL binary is built against ICU 74 and there is no compat package on Alpine. Use Alpine 3.20 or 3.21 instead |
506+
| Windows 10/11 | x86_64 | ✅ Supported | Released binary; built in CI |
507+
| NixOS | x86_64, aarch64 | ✅ Supported | Timezone pinned to UTC since v0.13.0 ([#11](https://github.com/vectorize-io/pg0/issues/11)) |
508+
| Any environment that runs as root only (e.g. Google Colab, restricted containers) | any | ❌ Not supported | PostgreSQL refuses to run as root - see [Troubleshooting](#postgresql-cannot-run-as-root) |
509+
| Linux with glibc < 2.35 | any | ⚠️ Auto-fallback | The install script switches to the statically-linked musl binary; pgvector is not available in that mode |
510+
479511
## Troubleshooting
480512

481513
### PostgreSQL Cannot Run as Root

0 commit comments

Comments
 (0)