diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1d9121b --- /dev/null +++ b/Dockerfile @@ -0,0 +1,51 @@ +# LEGO Instructions Manager +# Created by: Jess Rogerson (yelling commands at Claude.AI) + +FROM python:3.12-slim + +ENV PYTHONUNBUFFERED=1 \ + PYTHONDONTWRITEBYTECODE=1 \ + PIP_NO_CACHE_DIR=1 \ + FLASK_APP=run.py \ + FLASK_ENV=production \ + UPLOAD_FOLDER=/data/uploads \ + BACKUP_FOLDER=/data/backups + +# libpq for psycopg, and the runtime libs PyMuPDF/Pillow need for +# PDF thumbnails and image handling. +RUN apt-get update && apt-get install -y --no-install-recommends \ + libpq5 \ + libjpeg62-turbo \ + zlib1g \ + libfreetype6 \ + curl \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app + +COPY requirements-docker.txt ./ +RUN pip install -r requirements-docker.txt + +COPY . . + +# Non-root. /data is chowned here; the entrypoint fixes up volume +# ownership at runtime since a fresh volume mounts as root. +RUN useradd --create-home --uid 1000 lim \ + && mkdir -p /data/uploads /data/backups \ + && chown -R lim:lim /app /data \ + && chmod +x /app/docker/entrypoint.sh + +VOLUME ["/data"] +EXPOSE 8000 + +HEALTHCHECK --interval=30s --timeout=5s --start-period=40s --retries=3 \ + CMD curl -fsS http://localhost:8000/ >/dev/null || exit 1 + +ENTRYPOINT ["/app/docker/entrypoint.sh"] +# One worker, many threads — on purpose. The backup job registry lives in +# process memory, so with multiple workers the progress-polling request can +# land on a worker that has never heard of the job. Threads keep it coherent +# and are plenty for a household-sized instance. +CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "1", "--threads", "8", \ + "--timeout", "600", "--access-logfile", "-", "--error-logfile", "-", \ + "run:app"]