# syntax=docker/dockerfile:1 # # MEGA INFRA ยท BOQ Converter # Streamlit web app. Single process, no database, no build step. FROM python:3.12-slim # libGL/libglib are what pdfplumber -> pdfminer.six -> Wand/OpenCV chain in; # without them the container starts and then dies on the first PDF. RUN apt-get update \ && apt-get install -y --no-install-recommends \ libglib2.0-0 libgl1 curl \ && rm -rf /var/lib/apt/lists/* ENV PYTHONUNBUFFERED=1 \ PYTHONDONTWRITEBYTECODE=1 \ PIP_NO_CACHE_DIR=1 \ PIP_DISABLE_PIP_VERSION_CHECK=1 WORKDIR /app # Dependencies first, so a code change does not reinstall the world. COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY app.py run.py ./ COPY boq_engine/ ./boq_engine/ COPY static/ ./static/ COPY .streamlit/ ./.streamlit/ # Run as a non-root user. UID 1000 matches the usual Coolify/Docker convention. RUN useradd --create-home --uid 1000 --shell /bin/bash megainfra \ && mkdir -p /data/profiles /app/out \ && chown -R megainfra:megainfra /app /data # A fingerprint of the actual source, computed HERE at image build time. # # A commit SHA looked better but is self-referential and can never be right: # the file naming the commit has to live in that commit, so it can only ever # name the parent. A hash of the files actually in the image has no such # problem - it is true by construction and cannot go stale. RUN find /app -name '*.py' -o -name '*.css' -o -name '*.toml' \ | sort | xargs sha256sum | sha256sum | cut -c1-12 > /app/build.txt \ && chown megainfra:megainfra /app/build.txt \ && echo "build fingerprint: $(cat /app/build.txt)" USER megainfra ENV HOME=/home/megainfra \ BOQ_PROFILES=/data/profiles \ STREAMLIT_BROWSER_GATHER_USAGE_STATS=false \ MPLCONFIGDIR=/tmp/mpl EXPOSE 8501 HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \ CMD curl -fsS http://localhost:8501/_stcore/health || exit 1 # Flags here deliberately override .streamlit/config.toml: # --server.address 0.0.0.0 the config binds 127.0.0.1 for local dev, which # is unreachable from outside the container # --server.enableXsrfProtection false # Coolify's Traefik sits in front and rewrites the # origin; Streamlit's origin check then rejects # the rewritten POST as a forged request CMD ["streamlit", "run", "app.py", \ "--server.port=8501", \ "--server.address=0.0.0.0", \ "--server.headless=true", \ "--server.enableCORS=false", \ "--server.enableXsrfProtection=false", \ "--browser.gatherUsageStats=false"]