# ACCESS Core — Multi-stage Dockerfile # Build stage: install Python dependencies # Runtime stage: minimal image with gunicorn FROM python:3.13-slim AS base ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PIP_NO_CACHE_DIR=1 \ PIP_DISABLE_PIP_VERSION_CHECK=1 WORKDIR /app # System dependencies (PostgreSQL client, etc.) RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ libpq-dev \ && rm -rf /var/lib/apt/lists/* # ── Build Stage ────────────────────────────────────────────────────────────── FROM base AS builder COPY requirements.txt . RUN pip install --prefix=/install -r requirements.txt # ── Runtime Stage ──────────────────────────────────────────────────────────── FROM base AS runtime # Default matches the variable contract used by infra/ (POSTGRES_*, PHI_KMS_KEY_ID). # Compose and ECS task definitions override this per environment. ENV DJANGO_SETTINGS_MODULE=access_core.settings.base \ PORT=8000 \ GUNICORN_WORKERS=4 \ GUNICORN_THREADS=2 \ GUNICORN_TIMEOUT=120 # Copy Python packages from builder COPY --from=builder /install /usr/local # Copy application code COPY . . # Create non-root user for security RUN groupadd -r appuser && useradd -r -g appuser -d /app appuser \ && chown -R appuser:appuser /app USER appuser # Health check HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD python manage.py check --deploy || exit 1 # EXPOSE does not expand ENV vars; the runtime bind port is ${PORT} (default 8000). EXPOSE 8000 CMD gunicorn access_core.wsgi:application \ --bind 0.0.0.0:${PORT} \ --workers ${GUNICORN_WORKERS} \ --threads ${GUNICORN_THREADS} \ --timeout ${GUNICORN_TIMEOUT} \ --access-logfile - \ --error-logfile -