# Multi-stage to keep the runtime image small. # build123d pulls scipy + matplotlib + OCP wheel — a few hundred MB. FROM python:3.12-slim AS builder ENV PIP_NO_CACHE_DIR=1 \ PIP_DISABLE_PIP_VERSION_CHECK=1 RUN apt-get update && apt-get install -y --no-install-recommends \ build-essential libgl1 libglu1-mesa libxrender1 libxext6 libsm6 \ libgomp1 \ && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY requirements.txt . RUN pip install --user -r requirements.txt && \ pip install --user gunicorn # --------------------------------------------------------------------------- FROM python:3.12-slim AS runtime # OCP runtime libs + openscad (subprocess for /api/holder/render). # openscad headless renders STL from .scad with parameter overrides via -D. RUN apt-get update && apt-get install -y --no-install-recommends \ libgl1 libglu1-mesa libxrender1 libxext6 libsm6 libgomp1 \ openscad xvfb \ && rm -rf /var/lib/apt/lists/* \ && useradd --create-home --shell /usr/sbin/nologin app COPY --from=builder /root/.local /home/app/.local ENV PATH=/home/app/.local/bin:$PATH \ PYTHONUNBUFFERED=1 \ HOST=0.0.0.0 \ PORT=5000 \ FLASK_DEBUG=0 WORKDIR /app COPY --chown=app:app app.py busbar_export.py ./ COPY --chown=app:app static/ ./static/ USER app EXPOSE 5000 # SQLite DB lives here; mount as a volume to persist across container restarts. ENV BUSBAR_DB=/app/data/busbar.db VOLUME /app/data # 2 workers is plenty — each export is CPU-bound on OpenCASCADE; more workers # don't help on a single-socket VM and just balloon RAM. CMD ["gunicorn", "--bind=0.0.0.0:5000", "--workers=2", "--threads=2", \ "--timeout=120", "app:app"]