# ── Multi-stage Dockerfile for Native Bun Runtime (web-latest-bun) ─────────── FROM oven/bun:1.4.0-slim AS base WORKDIR /app RUN apt-get update \ && apt-get upgrade -y \ && apt-get install -y --no-install-recommends \ build-essential \ python3 \ python-is-python3 \ make \ g++ \ libsecret-1-0 \ ca-certificates \ curl \ && rm -rf /var/lib/apt/lists/* # ── Builder stage (100% Bun Native Install & Build) ───────────────────────── FROM base AS builder WORKDIR /app COPY . . # Fast Bun native package install RUN bun install --include=optional --quiet # Compile native better-sqlite3 Node-API addon under Bun RUN if [ -d "node_modules/better-sqlite3" ]; then \ (cd node_modules/better-sqlite3 && bunx node-gyp rebuild); \ fi # Fetch tls-client-node native binary if script exists RUN if [ -f "node_modules/tls-client-node/scripts/postinstall.js" ]; then \ bun node_modules/tls-client-node/scripts/postinstall.js || true; \ fi # Turbopack is supported on Bun 1.4+ (Next 16.3); override via # --build-arg OMNIROUTE_USE_TURBOPACK=0 to force the webpack fallback. ARG OMNIROUTE_USE_TURBOPACK=1 ENV OMNIROUTE_USE_TURBOPACK=${OMNIROUTE_USE_TURBOPACK} ARG OMNIROUTE_BASE_PATH="" ENV OMNIROUTE_BASE_PATH=$OMNIROUTE_BASE_PATH ARG DASHBOARD_ALLOW_EMBED="" ENV DASHBOARD_ALLOW_EMBED=$DASHBOARD_ALLOW_EMBED ENV NEXT_TELEMETRY_DISABLED=1 ENV NODE_ENV=production # Cap the Next.js build heap and page-data worker pool inside the Bun image the # same way the node Dockerfile does (#10060/#11419/#7518). Without these knobs # Next falls back to its defaults: worker pool = os.cpus()-1 (3 on the 4-vCPU # GitHub runner) and an 8 GB V8 heap ceiling per process. 4+ V8 processes at # multi-GB each blow past the 16 GB runner, the cgroup OOM killer SIGKILLs a # build worker mid-compile, and buildx fails the step with `ResourceExhausted: # ... cannot allocate memory` — every Bun image published on main since the -bun # targets landed (#11709, #11039). # # The per-process peak is a MEASURED ~4.5 GB RSS (dmesg OOM-killer report, # #7518), independent of NODE_OPTIONS — Turbopack is native/Rust and compiles # outside the V8 heap — and it applies to the parent process too, so 2 page-data # workers (3 processes × 4.5 GB ≈ 13.5 GB) do not fit the 12.288 GB (75%) # budget either. Both images therefore default to OMNIROUTE_BUILD_WORKERS=2 # (1 page-data worker): 2 processes × 4.5 GB ≈ 9 GB fits with headroom (#11663). # The default Turbopack path keeps the compile outside the V8 heap, but the # guards must hold for the webpack fallback (OMNIROUTE_USE_TURBOPACK=0) too, so # they are wired exactly like the node image. # # NODE_OPTIONS propagates to the spawned `next build` child and its workers # (build-next-isolated.mjs → resolveNextBuildEnv spreads process.env), so the # ceiling is per PROCESS, not per build. ARG OMNIROUTE_BUILD_MEMORY_MB=6144 ENV NODE_OPTIONS="--max-old-space-size=${OMNIROUTE_BUILD_MEMORY_MB}" ARG OMNIROUTE_BUILD_WORKERS=2 ENV CIRCLE_NODE_TOTAL=${OMNIROUTE_BUILD_WORKERS} # Bun native Next.js build execution RUN bun run --quiet build # ── Runner Base stage (100% Bun Native Production Runtime) ────────────────── FROM oven/bun:1.3.14-slim AS runner-base LABEL org.opencontainers.image.title="omniroute" \ org.opencontainers.image.description="Unified AI proxy — route any LLM through one endpoint (Bun Native)" \ org.opencontainers.image.url="https://omniroute.online" \ org.opencontainers.image.source="https://github.com/diegosouzapw/OmniRoute" \ org.opencontainers.image.licenses="MIT" WORKDIR /app RUN apt-get update \ && apt-get install -y --no-install-recommends \ libsecret-1-0 \ ca-certificates \ curl \ && rm -rf /var/lib/apt/lists/* ENV NODE_ENV=production ENV PORT=20128 ENV HOSTNAME=0.0.0.0 ENV OMNIROUTE_MEMORY_MB=1024 ENV DATA_DIR=/app/data RUN mkdir -p /app/data COPY --from=builder /app/.build/next/standalone ./ COPY --from=builder /app/node_modules/better-sqlite3 ./node_modules/better-sqlite3 ENV OMNIROUTE_MIGRATIONS_DIR=/app/migrations COPY --from=builder /app/scripts/dev/healthcheck.mjs ./healthcheck.mjs EXPOSE 20128 HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=3 \ CMD bun healthcheck.mjs || exit 1 ENTRYPOINT ["bun", "dev/run-standalone.mjs"] # ── Runner Web stage (Bun Native + Chromium/Playwright for Web providers) ─── FROM runner-base AS runner-web USER root RUN apt-get update \ && apt-get install -y --no-install-recommends \ chromium \ chromium-driver \ fonts-liberation \ libasound2t64 \ gconf-service \ libatk-bridge2.0-0 \ libatk1.0-0 \ libc6 \ libcairo2 \ libcups2 \ libdbus-1-3 \ libexpat1 \ libfontconfig1 \ libgbm1 \ libgcc-s1 \ libglib2.0-0 \ libgtk-3-0 \ libnspr4 \ libnss3 \ libpango-1.0-0 \ pangocairo-1.0-0 \ stdc++6 \ libx11-6 \ libx11-xcb1 \ libxcb1 \ libxcomposite1 \ libxcursor1 \ libxdamage1 \ libxext6 \ libxfixes3 \ libxi6 \ libxrandr2 \ libxrender1 \ libxss1 \ libxtst6 \ ca-certificates \ fonts-gargi \ fonts-ipafont-gothic \ fonts-kacst \ fonts-thai-tlwg \ fonts-wqy-zenhei \ && rm -rf /var/lib/apt/lists/* ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 ENV PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH=/usr/bin/chromium # Return to the base image non-root user after the apt install (mirrors the # Node Dockerfile runner-web stage, which re-asserts USER node). USER bun