lamhieu/docsifer
13
1# syntax=docker/dockerfile:1.72# -----------------------------------------------------------------------------3# Docsifer — multi-stage image (CPU-only, HF Spaces compatible).4#5# Stage 1 builder compiles wheels from requirements.txt into /install6# Stage 2 runtime slim image with jemalloc + healthcheck + non-root user7#8# Build:9# docker build -t docsifer .10# Run:11# docker run --rm -p 7860:7860 --env-file .env docsifer12# -----------------------------------------------------------------------------13 14ARG PYTHON_VERSION=3.1115 16# ============================================================================17# Stage 1: builder18# ============================================================================19FROM python:${PYTHON_VERSION}-slim AS builder20 21ENV PIP_DISABLE_PIP_VERSION_CHECK=1 \22 PIP_NO_CACHE_DIR=0 \23 PYTHONDONTWRITEBYTECODE=1 \24 PYTHONUNBUFFERED=125 26# Build tools required by source-only wheels (e.g. python-magic, tiktoken).27RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \28 --mount=type=cache,target=/var/lib/apt,sharing=locked \29 apt-get update && apt-get install -y --no-install-recommends \30 build-essential git ca-certificates libmagic131 32WORKDIR /build33COPY requirements.txt ./34 35RUN --mount=type=cache,target=/root/.cache/pip,sharing=locked \36 pip install --prefix=/install -r requirements.txt37 38# ============================================================================39# Stage 2: runtime40# ============================================================================41FROM python:${PYTHON_VERSION}-slim AS runtime42 43LABEL org.opencontainers.image.title="docsifer" \44 org.opencontainers.image.description="Document → Markdown service powered by MarkItDown" \45 org.opencontainers.image.licenses="MIT" \46 org.opencontainers.image.source="https://github.com/lh0x00/docsifer"47 48ENV PYTHONUNBUFFERED=1 \49 PYTHONDONTWRITEBYTECODE=1 \50 PIP_DISABLE_PIP_VERSION_CHECK=1 \51 OMP_NUM_THREADS=2 \52 MKL_NUM_THREADS=2 \53 TOKENIZERS_PARALLELISM=false \54 HF_HOME=/home/user/.cache/huggingface \55 XDG_CACHE_HOME=/home/user/.cache \56 TMPDIR=/tmp \57 DOCSIFER_TMP_DIR=/tmp \58 DOCSIFER_LOG_JSON=true \59 DOCSIFER_ENVIRONMENT=production \60 PORT=786061 62# jemalloc keeps RSS predictable for workloads with frequent (de)allocations63# (markitdown / ffmpeg / pillow chains all churn the heap).64# libmagic1 + ffmpeg are required at runtime by python-magic and audio65# transcription respectively. curl is used by HEALTHCHECK.66RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \67 --mount=type=cache,target=/var/lib/apt,sharing=locked \68 apt-get update && apt-get install -y --no-install-recommends \69 libjemalloc2 libmagic1 ffmpeg ca-certificates curl70 71ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.272 73# Non-root. Hugging Face Spaces requires uid 1000 with write access to /home/user.74RUN useradd -m -u 1000 user75USER user76WORKDIR /home/user/app77 78# Pull the prebuilt site-packages from stage 1.79COPY --from=builder /install /usr/local80 81# Application source.82COPY --chown=user . .83 84# Ensure HF cache dirs exist (Spaces / restricted FS).85RUN mkdir -p "$HF_HOME" "$XDG_CACHE_HOME"86 87EXPOSE 786088 89HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \90 CMD curl -fsS "http://127.0.0.1:${PORT}/v1/healthz" >/dev/null || exit 191 92CMD ["uvicorn", "docsifer.main:app", \93 "--host", "0.0.0.0", \94 "--port", "7860", \95 "--proxy-headers", \96 "--forwarded-allow-ips", "*"]97 