Single-binary / distroless deployments: document `load()` or ship a glibc build

Aperta
#1,071 6 commenti 0 reazioni 1 assegnatario Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

@theoephraim ci sta già lavorando.

Dal 7/9/2026.

Valutazione

Questa issue non è ancora stata valutata.

Descrizione

I'm deploying a Bun app with Elysiajs and compiled with bun build --compile on
gcr.io/distroless/base-debian13:nonroot
(glibc, no shell, no node_modules). I wanted varlock to validate the env
inside the container at boot, and ran into three things.

1. The ghcr binary is musl-only

ghcr.io/dmno-dev/varlock:1.18.0
ships a binary dynamically linked against musl and libstdc++:

$ ldd /usr/local/bin/varlock
	/lib/ld-musl-x86_64.so.1
	libstdc++.so.6 => /usr/lib/libstdc++.so.6
	libc.musl-x86_64.so.1 => /lib/ld-musl-x86_64.so.1
	libgcc_s.so.1 => /usr/lib/libgcc_s.so.1

Copied into distroless base-debian13 it fails before starting:

exec /varlock: no such file or directory

On a bare alpine:3.20 it fails on libstdc++.so.6 / libgcc_s.so.1.
So it only runs on Alpine with libstdc++ installed. The Docker integration
guide presents COPY --from=varlock /usr/local/bin/varlock as usable in
distroless images without saying that.

2. The documented paths for compiled executables need the CLI as a process

Both varlock/auto-load and varlock run -- shell out to the CLI
(auto-load walks up the tree looking for node_modules/.bin/varlock).
Since the ghcr binary can't run here, my first attempt was to compile the
CLI from node_modules for glibc in the builder stage and use it as the
entrypoint:

FROM oven/bun:1.4.2-slim AS builder
# ... bun ci, COPY src, .env.schema ...
RUN bun build --compile --minify --target bun-linux-x64 src/index.ts --outfile dist/server \
 && bun build --compile --minify --target bun-linux-x64 node_modules/varlock/bin/cli.js --outfile dist/varlock

FROM gcr.io/distroless/base-debian13:nonroot
WORKDIR /app
COPY --from=builder /app/dist/server /app/dist/varlock ./
COPY --from=builder /app/.env.schema ./.env.schema
ENTRYPOINT ["/app/varlock", "run", "--"]
CMD ["/app/server"]

This works: validation runs at boot, missing vars are reported and the
container exits 1. But every bun build --compile embeds the whole Bun
runtime, so the image goes from 158 MB to 278 MB (server 82 MB + varlock
82 MB) just to validate a .env.schema.

3. What I ended up doing

The varlock package exports load(), which is what auto-load does
minus the execSync. Using it as the entry of the compiled binary gives a
single process and a single binary:

// src/index.ts, the bun build --compile entry
import { load } from "varlock";

try {
	await load();
} catch (error) {
	const formatted =
		error instanceof Error && "getFormattedOutput" in error
			? (error as { getFormattedOutput: () => string }).getFormattedOutput()
			: String(error);
	process.stderr.write(`${formatted}\n`);
	process.exit(1);
}

await import("@/modules/app"); // the Elysia app, which imports { ENV } from "varlock/env"
FROM gcr.io/distroless/base-debian13:nonroot
WORKDIR /app
COPY --from=builder /app/dist/server ./server
COPY --from=builder /app/.env.schema ./.env.schema
ENV NODE_ENV=production
ENTRYPOINT ["/app/server"]

Image is back to 158 MB, the server is PID 1, ENV is typed and coerced
from @generateTsTypes, and a bad env prints the usual varlock report and
exits 1. For bun run dev / bun test I keep preload = ["varlock/auto-load"]
in bunfig.toml and point those scripts at the app module, not the entry.

The catch is that load() appears in index.d.mts but not in the docs,
so I don't know whether it's meant to be stable.

Would you consider documenting load() as the recommended path for
single-binary / distroless deployments, and either a glibc or static build
of the CLI, or a note in the Docker guide that the ghcr binary is
Alpine-only?

varlock 1.18.0, Bun 1.4.2, linux/amd64.

Lingua principale
TypeScript
Stelle
4.9k
Fork
140
Merge medio
2g 3h
PR unite (30g)
35

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di dmno-dev/varlock

Tutte le issue di dmno-dev/varlock

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.