Skip to content

build: bake the DuckDB extensions into the image - #435

Merged
jrosskopf merged 1 commit into
mainfrom
feat/bake-extensions
Sep 2, 2026
Merged

build: bake the DuckDB extensions into the image#435
jrosskopf merged 1 commit into
mainfrom
feat/bake-extensions

Conversation

@jrosskopf

@jrosskopf jrosskopf commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

A cold escurel pod downloaded ~137 MB of DuckDB extensions before it could serve anything, and against a Google Drive lake it never finished inside the substrate's 8m deploy timeout.

Measured in the cluster:

duration
cold container 7m56s, still going
next start, same pod, warm cache 7 seconds to reach the catalog

Why a volume was not the fix

helm upgrade --atomic deletes the PVC when it rolls the release back, so the cache is destroyed by the very failure it exists to prevent. It could only ever help after a deploy had already succeeded — which was the thing that could not happen. That was tried; this replaces it.

What this does

ducklake, postgres, httpfs, fts, vss and gdrive are installed in a separate build stage and copied into the runtime image. 158 MB, and a pod downloads nothing at boot — which also removes a boot-time dependency on two extension repositories being reachable.

Verified against the constraints the chart imposes, not in a friendly shell

docker run --read-only --network none --user 65532:65532 …
  INSTALL ducklake; LOAD ducklake; INSTALL postgres; LOAD postgres;
  INSTALL fts; LOAD fts; INSTALL vss; LOAD vss;
  LOAD '…/gdrive.duckdb_extension';
→ no network, read-only, all loaded

--network none proves the baked cache is what was used. --read-only proves INSTALL no-ops against a pre-installed extension rather than needing to write — which was the one real risk in this design.

The build also asserts all six land under $HOME/.duckdb/extensions/<version>/<platform>. DuckDB resolves by that exact path, so a version or platform mismatch would silently download again at runtime — the failure that looks like success.

Two details that each cost a build, now encoded

  • INSTALL postgres is an alias; the artifact it lands is postgres_scanner.duckdb_extension. Asserting the alias name failed the build while all six were present.
  • HOME decides where DuckDB looks. The runtime image sets HOME=/opt/escurel to match the copy; a deployment that overrides HOME elsewhere silently reverts to downloading. (The heron-escurel values do exactly that today and will be updated alongside this.)

gdrive source

From the erpl.io mirror, not community: workload identity federation only exists in v2026.09.01, and the community repository still serves v2026.08.07, whose credential_chain refuses external_account outright. GDRIVE_REPO is a build arg — switch it once duckdb/community-extensions#2588 is merged and built.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

A cold escurel pod downloaded ~137MB of DuckDB extensions before it could
serve anything, and against a Google Drive lake it never finished inside the
substrate's 8m deploy timeout. Measured in the cluster: a cold container ran
7m56s and was still going, while the next start in the SAME pod reached the
catalog in 7 SECONDS off a warm cache.

A PersistentVolume looks like the fix and is not: `helm upgrade --atomic`
deletes the PVC when it rolls the release back, so the cache is destroyed by
the very failure it exists to prevent. It could only help AFTER a deploy had
already succeeded, which was the thing that could not happen.

So the extensions ship in the image: ducklake, postgres, httpfs, fts, vss and
gdrive, installed in a separate stage and copied into the runtime image.
158MB, and a pod now downloads nothing at boot -- which also removes a
boot-time dependency on two extension repositories being reachable.

Verified against the constraints the substrate chart actually imposes, not in
a friendly shell:

  * --read-only --network none --user 65532: INSTALL ducklake / postgres /
    fts / vss and a path LOAD of gdrive all succeed. No network means the
    baked cache is definitely what was used, and read-only means INSTALL
    no-ops against a pre-installed extension rather than needing to write.
  * The build itself asserts all six land under
    $HOME/.duckdb/extensions/<version>/<platform>. DuckDB resolves by that
    exact path, so a version or platform mismatch would silently download
    again at runtime -- the failure that looks like success.

Two details that cost a build each and are now encoded:

  * `INSTALL postgres` is an ALIAS; the artifact it lands is
    postgres_scanner.duckdb_extension. Asserting the alias name failed the
    build while all six were present.
  * HOME is what decides where DuckDB looks. The runtime image sets
    HOME=/opt/escurel to match the copy; a deployment that overrides HOME
    elsewhere silently reverts to downloading.

gdrive comes from the erpl.io mirror rather than `community`, because
workload identity federation only exists in v2026.09.01 and the community
repository still serves v2026.08.07, whose credential_chain refuses
external_account outright. GDRIVE_REPO is a build arg; switch it once
duckdb/community-extensions#2588 is merged and built.
@jrosskopf
jrosskopf merged commit 047ca0a into main Sep 2, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant