63 lines
2.5 KiB
Docker
63 lines
2.5 KiB
Docker
# hamprint -- single-container image (plan.md §10).
|
|
#
|
|
# Runs Gunicorn plus the two periodic jobs (process_submissions every 30 s,
|
|
# cleanup_stale every 5 min) in the same process group. No sidecars.
|
|
#
|
|
# Build: podman build -t hamprint:latest .
|
|
# Run: podman run --rm -p 8000:8000 --env-file .env hamprint:latest
|
|
#
|
|
# In production the host typically mounts a volume at /app/media so uploaded
|
|
# STLs survive container restarts; the database connection comes from
|
|
# DATABASE_URL (Postgres in prod, SQLite if you really want).
|
|
|
|
FROM docker.io/library/python:3.14-slim
|
|
|
|
ENV PYTHONDONTWRITEBYTECODE=1 \
|
|
PYTHONUNBUFFERED=1 \
|
|
PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
|
PIP_NO_CACHE_DIR=1 \
|
|
DJANGO_SETTINGS_MODULE=hamprint.settings.prod
|
|
|
|
WORKDIR /app
|
|
|
|
# System packages python:3.14-slim doesn't ship:
|
|
# tini -- PID 1 for clean signal forwarding to gunicorn + the loops
|
|
# libgomp1 -- numpy/numpy-stl runtime on some kernels
|
|
# curl -- handy for healthchecks if compose.yaml grows one
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends tini curl libgomp1 \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
# Python deps as their own layer so app-code edits don't invalidate the wheel
|
|
# cache. psycopg[binary] is added explicitly because requirements.txt is
|
|
# kept Postgres-driver-agnostic for the host-venv (SQLite) path.
|
|
COPY requirements.txt .
|
|
RUN pip install -r requirements.txt 'psycopg[binary]'
|
|
|
|
# Application code (.dockerignore excludes .venv, .git, db.sqlite3, demo/, etc).
|
|
COPY . .
|
|
|
|
# Build Tailwind CSS, gather everything under STATIC_ROOT for WhiteNoise, then
|
|
# drop the ~120 MB Tailwind CLI download so it doesn't ride along. The source
|
|
# .css lives at assets/tailwind.source.css (per TAILWIND_CLI_SRC_CSS in
|
|
# settings/base.py); only the CLI binary cache is purged.
|
|
RUN python manage.py tailwind build --force \
|
|
&& python manage.py collectstatic --noinput \
|
|
&& rm -rf /app/.django_tailwind_cli
|
|
|
|
# Default writable dirs. Mount a volume at /app/media in prod for persistence.
|
|
RUN mkdir -p /app/media /app/staticfiles
|
|
|
|
# Drop privileges. uid 1000 maps cleanly to the typical host user in rootless
|
|
# podman, so a bind-mounted media volume stays writable without extra fuss.
|
|
RUN useradd -m -u 1000 app \
|
|
&& chown -R app:app /app
|
|
USER app
|
|
|
|
EXPOSE 8000
|
|
|
|
# tini reaps zombies + forwards SIGTERM to all children, so when the orchestrator
|
|
# stops the container both Gunicorn AND the two background loops get the signal.
|
|
ENTRYPOINT ["/usr/bin/tini", "--"]
|
|
CMD ["bash", "entrypoint.sh"]
|