# --------------------------------------------------------------------------- # Multi-stage image with an OPTIONAL server-engine (faster-whisper) layer. # # docker build -t whisper-web . # Bun-only (default) # docker build --target with-server-engine -t whisper-web:fw . # + Python/faster-whisper # # The default image stays Bun-only (Decisions.md D-013): no Python inside, and # the server engine simply reports "unavailable" while the browser/anonymous # path works in full. `--target with-server-engine` adds python3 + the # server-py/requirements.txt packages so logged-in users can run inference on # the server. See README.md ("Deployment") for run commands and env vars. # --------------------------------------------------------------------------- # Build stage: install all deps (build needs onnxruntime-web files from # node_modules) and emit the self-contained dist/ (scripts/build.ts). FROM oven/bun:1 AS build WORKDIR /app COPY package.json bun.lock bunfig.toml ./ COPY patches ./patches RUN bun install --frozen-lockfile COPY src ./src COPY scripts ./scripts RUN bun run build # Runtime stage: production deps only. src/ and scripts/ are still needed — # src/index.ts is the server entrypoint and imports scripts/generate-icons. FROM oven/bun:1 AS runtime WORKDIR /app ENV NODE_ENV=production ENV DB_PATH=/app/data/app.db # Runtime feature flags (D-015) — override at `docker run -e` / compose time. # AUTH_ENABLED=false disables login/register (the auth API 404s and the # client hides the Sign in UI). Defaults to enabled. ENV AUTH_ENABLED=true COPY package.json bun.lock bunfig.toml ./ COPY patches ./patches RUN bun install --frozen-lockfile --production COPY src ./src COPY scripts ./scripts # The sidecar script ships in every image (it is one small .py file). Without # a Python runtime it is never executed: the supervisor in # src/server/transcribe.ts records "python3 is not available" once and # GET /api/transcribe/status reports the server engine as unavailable (D-013). COPY server-py ./server-py COPY --from=build /app/dist ./dist # SQLite lives under /app/data — mount a volume there to persist users/sessions # (and, in the with-server-engine flavor, downloaded Whisper weights). RUN mkdir -p /app/data && chown -R bun:bun /app/data USER bun ENV PORT=3000 EXPOSE $PORT HEALTHCHECK --interval=30s --timeout=5s --start-period=15s \ CMD ["bun", "-e", "const r = await fetch(`http://127.0.0.1:${process.env.PORT || 3000}/`); if (!r.ok) throw new Error(String(r.status));"] CMD ["bun", "src/index.ts"] # --------------------------------------------------------------------------- # Optional server-engine layer (build with `--target with-server-engine`). # Adds Debian's python3 plus server-py/requirements.txt in a virtualenv # (Debian pip is PEP 668 "externally managed", and a venv keeps the layer # self-contained); SIDECAR_PYTHON points the supervisor at it. CPU-only by # default — FASTER_WHISPER_DEVICE=auto resolves to CPU here, int8 compute. # GPU (FASTER_WHISPER_DEVICE=cuda) needs a CUDA/cuDNN base image and # `docker run --gpus`; that is deliberately not part of this Dockerfile. # --------------------------------------------------------------------------- FROM runtime AS with-server-engine USER root RUN apt-get update \ && apt-get install -y --no-install-recommends python3 python3-venv \ && rm -rf /var/lib/apt/lists/* RUN python3 -m venv /opt/faster-whisper \ && /opt/faster-whisper/bin/pip install --no-cache-dir -r server-py/requirements.txt ENV SIDECAR_PYTHON=/opt/faster-whisper/bin/python3 # Cache Whisper weights inside the image (outside the /app/data volume) # so they are not shadowed by bind mounts and don't need to be downloaded at runtime. ENV FASTER_WHISPER_DOWNLOAD_ROOT=/opt/faster-whisper/models RUN mkdir -p /opt/faster-whisper/models && chown -R bun:bun /opt/faster-whisper/models USER bun # Pre-download the allowed models (large-v3-turbo and medium.en) RUN $SIDECAR_PYTHON -c "import faster_whisper; faster_whisper.download_model('large-v3-turbo', cache_dir='/opt/faster-whisper/models'); faster_whisper.download_model('medium.en', cache_dir='/opt/faster-whisper/models')" # --------------------------------------------------------------------------- # Default target: a plain `docker build .` (what .gitlab-ci.yml runs) must # produce the Bun-only image, but Docker builds the *last* stage by default # and with-server-engine has to come after the stage it extends — so re-select # the runtime stage here. Adds no layers. # --------------------------------------------------------------------------- FROM runtime AS default